/** * Saves files from a draft file area to a real one (merging the list of files). * Can rewrite URLs in some content at the same time if desired. * * @category files * @global stdClass $USER * @param int $draftitemid the id of the draft area to use. Normally obtained * from file_get_submitted_draft_itemid('elementname') or similar. * @param int $contextid This parameter and the next two identify the file area to save to. * @param string $component * @param string $filearea indentifies the file area. * @param int $itemid helps identifies the file area. * @param array $options area options (subdirs=>false, maxfiles=-1, maxbytes=0) * @param string $text some html content that needs to have embedded links rewritten * to the @@PLUGINFILE@@ form for saving in the database. * @param bool $forcehttps force https urls. * @return string|null if $text was passed in, the rewritten $text is returned. Otherwise NULL. */ function file_save_draft_area_files($draftitemid, $contextid, $component, $filearea, $itemid, array $options = null, $text = null, $forcehttps = false) { global $USER; $usercontext = context_user::instance($USER->id); $fs = get_file_storage(); $options = (array) $options; if (!isset($options['subdirs'])) { $options['subdirs'] = false; } if (!isset($options['maxfiles'])) { $options['maxfiles'] = -1; // unlimited } if (!isset($options['maxbytes']) || $options['maxbytes'] == USER_CAN_IGNORE_FILE_SIZE_LIMITS) { $options['maxbytes'] = 0; // unlimited } if (!isset($options['areamaxbytes'])) { $options['areamaxbytes'] = FILE_AREA_MAX_BYTES_UNLIMITED; // Unlimited. } $allowreferences = true; if (isset($options['return_types']) && !($options['return_types'] & FILE_REFERENCE)) { // we assume that if $options['return_types'] is NOT specified, we DO allow references. // this is not exactly right. BUT there are many places in code where filemanager options // are not passed to file_save_draft_area_files() $allowreferences = false; } // Check if the draft area has exceeded the authorised limit. This should never happen as validation // should have taken place before, unless the user is doing something nauthly. If so, let's just not save // anything at all in the next area. if (file_is_draft_area_limit_reached($draftitemid, $options['areamaxbytes'])) { return null; } $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid, 'id'); $oldfiles = $fs->get_area_files($contextid, $component, $filearea, $itemid, 'id'); // One file in filearea means it is empty (it has only top-level directory '.'). if (count($draftfiles) > 1 || count($oldfiles) > 1) { // we have to merge old and new files - we want to keep file ids for files that were not changed // we change time modified for all new and changed files, we keep time created as is $newhashes = array(); $filecount = 0; foreach ($draftfiles as $file) { if (!$options['subdirs'] && $file->get_filepath() !== '/') { continue; } if (!$allowreferences && $file->is_external_file()) { continue; } if (!$file->is_directory()) { if ($options['maxbytes'] and $options['maxbytes'] < $file->get_filesize()) { // oversized file - should not get here at all continue; } if ($options['maxfiles'] != -1 and $options['maxfiles'] <= $filecount) { // more files - should not get here at all continue; } $filecount++; } $newhash = $fs->get_pathname_hash($contextid, $component, $filearea, $itemid, $file->get_filepath(), $file->get_filename()); $newhashes[$newhash] = $file; } // Loop through oldfiles and decide which we need to delete and which to update. // After this cycle the array $newhashes will only contain the files that need to be added. foreach ($oldfiles as $oldfile) { $oldhash = $oldfile->get_pathnamehash(); if (!isset($newhashes[$oldhash])) { // delete files not needed any more - deleted by user $oldfile->delete(); continue; } $newfile = $newhashes[$oldhash]; // Now we know that we have $oldfile and $newfile for the same path. // Let's check if we can update this file or we need to delete and create. if ($newfile->is_directory()) { // Directories are always ok to just update. } else { if (($source = @unserialize($newfile->get_source())) && isset($source->original)) { // File has the 'original' - we need to update the file (it may even have not been changed at all). $original = file_storage::unpack_reference($source->original); if ($original['filename'] !== $oldfile->get_filename() || $original['filepath'] !== $oldfile->get_filepath()) { // Very odd, original points to another file. Delete and create file. $oldfile->delete(); continue; } } else { // The same file name but absence of 'original' means that file was deteled and uploaded again. // By deleting and creating new file we properly manage all existing references. $oldfile->delete(); continue; } } // status changed, we delete old file, and create a new one if ($oldfile->get_status() != $newfile->get_status()) { // file was changed, use updated with new timemodified data $oldfile->delete(); // This file will be added later continue; } // Updated author if ($oldfile->get_author() != $newfile->get_author()) { $oldfile->set_author($newfile->get_author()); } // Updated license if ($oldfile->get_license() != $newfile->get_license()) { $oldfile->set_license($newfile->get_license()); } // Updated file source // Field files.source for draftarea files contains serialised object with source and original information. // We only store the source part of it for non-draft file area. $newsource = $newfile->get_source(); if ($source = @unserialize($newfile->get_source())) { $newsource = $source->source; } if ($oldfile->get_source() !== $newsource) { $oldfile->set_source($newsource); } // Updated sort order if ($oldfile->get_sortorder() != $newfile->get_sortorder()) { $oldfile->set_sortorder($newfile->get_sortorder()); } // Update file timemodified if ($oldfile->get_timemodified() != $newfile->get_timemodified()) { $oldfile->set_timemodified($newfile->get_timemodified()); } // Replaced file content if (!$oldfile->is_directory() && ($oldfile->get_contenthash() != $newfile->get_contenthash() || $oldfile->get_filesize() != $newfile->get_filesize() || $oldfile->get_referencefileid() != $newfile->get_referencefileid() || $oldfile->get_userid() != $newfile->get_userid())) { $oldfile->replace_file_with($newfile); } // unchanged file or directory - we keep it as is unset($newhashes[$oldhash]); } // Add fresh file or the file which has changed status // the size and subdirectory tests are extra safety only, the UI should prevent it foreach ($newhashes as $file) { $file_record = array('contextid' => $contextid, 'component' => $component, 'filearea' => $filearea, 'itemid' => $itemid, 'timemodified' => time()); if ($source = @unserialize($file->get_source())) { // Field files.source for draftarea files contains serialised object with source and original information. // We only store the source part of it for non-draft file area. $file_record['source'] = $source->source; } if ($file->is_external_file()) { $repoid = $file->get_repository_id(); if (!empty($repoid)) { $file_record['repositoryid'] = $repoid; $file_record['reference'] = $file->get_reference(); } } $fs->create_file_from_storedfile($file_record, $file); } } // note: do not purge the draft area - we clean up areas later in cron, // the reason is that user might press submit twice and they would loose the files, // also sometimes we might want to use hacks that save files into two different areas if (is_null($text)) { return null; } else { return file_rewrite_urls_to_pluginfile($text, $draftitemid, $forcehttps); } }
/** * Update the calendar entries for this assignment. * * @param int $coursemoduleid - Required to pass this in because it might * not exist in the database yet. * @return bool */ public function update_calendar($coursemoduleid) { global $DB, $CFG; require_once $CFG->dirroot . '/calendar/lib.php'; // Special case for add_instance as the coursemodule has not been set yet. $instance = $this->get_instance(); if ($instance->duedate) { $event = new stdClass(); $params = array('modulename' => 'assign', 'instance' => $instance->id); $event->id = $DB->get_field('event', 'id', $params); $event->name = $instance->name; $event->timestart = $instance->duedate; // Convert the links to pluginfile. It is a bit hacky but at this stage the files // might not have been saved in the module area yet. $intro = $instance->intro; if ($draftid = file_get_submitted_draft_itemid('introeditor')) { $intro = file_rewrite_urls_to_pluginfile($intro, $draftid); } // We need to remove the links to files as the calendar is not ready // to support module events with file areas. $intro = strip_pluginfile_content($intro); if ($this->show_intro()) { $event->description = array('text' => $intro, 'format' => $instance->introformat); } else { $event->description = array('text' => '', 'format' => $instance->introformat); } if ($event->id) { $calendarevent = calendar_event::load($event->id); $calendarevent->update($event); } else { unset($event->id); $event->courseid = $instance->course; $event->groupid = 0; $event->userid = 0; $event->modulename = 'assign'; $event->instance = $instance->id; $event->eventtype = 'due'; $event->timeduration = 0; calendar_event::create($event); } } else { $DB->delete_records('event', array('modulename' => 'assign', 'instance' => $instance->id)); } }
/** * Saves files from a draft file area to a real one (merging the list of files). * Can rewrite URLs in some content at the same time if desired. * * @global object * @global object * @param integer $draftitemid the id of the draft area to use. Normally obtained * from file_get_submitted_draft_itemid('elementname') or similar. * @param integer $contextid This parameter and the next two identify the file area to save to. * @param string $component * @param string $filearea indentifies the file area. * @param integer $itemid helps identifies the file area. * @param array $options area options (subdirs=>false, maxfiles=-1, maxbytes=0) * @param string $text some html content that needs to have embedded links rewritten * to the @@PLUGINFILE@@ form for saving in the database. * @param boolean $forcehttps force https urls. * @return string if $text was passed in, the rewritten $text is returned. Otherwise NULL. */ function file_save_draft_area_files($draftitemid, $contextid, $component, $filearea, $itemid, array $options = null, $text = null, $forcehttps = false) { global $USER; $usercontext = get_context_instance(CONTEXT_USER, $USER->id); $fs = get_file_storage(); $options = (array) $options; if (!isset($options['subdirs'])) { $options['subdirs'] = false; } if (!isset($options['maxfiles'])) { $options['maxfiles'] = -1; // unlimited } if (!isset($options['maxbytes'])) { $options['maxbytes'] = 0; // unlimited } $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid, 'id'); $oldfiles = $fs->get_area_files($contextid, $component, $filearea, $itemid, 'id'); if (count($draftfiles) < 2) { // means there are no files - one file means root dir only ;-) $fs->delete_area_files($contextid, $component, $filearea, $itemid); } else { if (count($oldfiles) < 2) { $filecount = 0; // there were no files before - one file means root dir only ;-) $file_record = array('contextid' => $contextid, 'component' => $component, 'filearea' => $filearea, 'itemid' => $itemid); foreach ($draftfiles as $file) { if (!$options['subdirs']) { if ($file->get_filepath() !== '/' or $file->is_directory()) { continue; } } if ($options['maxbytes'] and $options['maxbytes'] < $file->get_filesize()) { // oversized file - should not get here at all continue; } if ($options['maxfiles'] != -1 and $options['maxfiles'] <= $filecount) { // more files - should not get here at all break; } if (!$file->is_directory()) { $filecount++; } $fs->create_file_from_storedfile($file_record, $file); } } else { // we have to merge old and new files - we want to keep file ids for files that were not changed // we change time modified for all new and changed files, we keep time created as is $file_record = array('contextid' => $contextid, 'component' => $component, 'filearea' => $filearea, 'itemid' => $itemid, 'timemodified' => time()); $newhashes = array(); foreach ($draftfiles as $file) { $newhash = $fs->get_pathname_hash($contextid, $component, $filearea, $itemid, $file->get_filepath(), $file->get_filename()); $newhashes[$newhash] = $file; } $filecount = 0; foreach ($oldfiles as $oldfile) { $oldhash = $oldfile->get_pathnamehash(); if (!isset($newhashes[$oldhash])) { // delete files not needed any more - deleted by user $oldfile->delete(); continue; } $newfile = $newhashes[$oldhash]; if ($oldfile->get_contenthash() != $newfile->get_contenthash() or $oldfile->get_sortorder() != $newfile->get_sortorder() or $oldfile->get_status() != $newfile->get_status() or $oldfile->get_license() != $newfile->get_license() or $oldfile->get_author() != $newfile->get_author() or $oldfile->get_source() != $newfile->get_source()) { // file was changed, use updated with new timemodified data $oldfile->delete(); continue; } // unchanged file or directory - we keep it as is unset($newhashes[$oldhash]); if (!$oldfile->is_directory()) { $filecount++; } } // now add new/changed files // the size and subdirectory tests are extra safety only, the UI should prevent it foreach ($newhashes as $file) { if (!$options['subdirs']) { if ($file->get_filepath() !== '/' or $file->is_directory()) { continue; } } if ($options['maxbytes'] and $options['maxbytes'] < $file->get_filesize()) { // oversized file - should not get here at all continue; } if ($options['maxfiles'] != -1 and $options['maxfiles'] <= $filecount) { // more files - should not get here at all break; } if (!$file->is_directory()) { $filecount++; } $fs->create_file_from_storedfile($file_record, $file); } } } // note: do not purge the draft area - we clean up areas later in cron, // the reason is that user might press submit twice and they would loose the files, // also sometimes we might want to use hacks that save files into two different areas if (is_null($text)) { return null; } else { return file_rewrite_urls_to_pluginfile($text, $draftitemid, $forcehttps); } }
/** * Compute the value that should be stored in the question_attempt_step_data * table. Contains a hash that (almost) uniquely encodes all the files. * @param int $draftitemid the draft file area itemid. * @param string $text optional content containing file links. */ protected function compute_value($draftitemid, $text) { global $USER; $fs = get_file_storage(); $usercontext = context_user::instance($USER->id); $files = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid, 'sortorder, filepath, filename', false); $string = ''; foreach ($files as $file) { $string .= $file->get_filepath() . $file->get_filename() . '|' . $file->get_contenthash() . '|'; } $hash = md5($string); if (is_null($text)) { if ($string) { return $hash; } else { return ''; } } // We add the file hash so a simple string comparison will say if the // files have been changed. First strip off any existing file hash. if ($text !== '') { $text = preg_replace('/\\s*<!-- File hash: \\w+ -->\\s*$/', '', $text); $text = file_rewrite_urls_to_pluginfile($text, $draftitemid); if ($string) { $text .= '<!-- File hash: ' . $hash . ' -->'; } } return $text; }
/** * Saves files from a draft file area to a real one (merging the list of files). * Can rewrite URLs in some content at the same time if desired. * * @category files * @global stdClass $USER * @param int $draftitemid the id of the draft area to use. Normally obtained * from file_get_submitted_draft_itemid('elementname') or similar. * @param int $contextid This parameter and the next two identify the file area to save to. * @param string $component * @param string $filearea indentifies the file area. * @param int $itemid helps identifies the file area. * @param array $options area options (subdirs=>false, maxfiles=-1, maxbytes=0) * @param string $text some html content that needs to have embedded links rewritten * to the @@PLUGINFILE@@ form for saving in the database. * @param bool $forcehttps force https urls. * @return string|null if $text was passed in, the rewritten $text is returned. Otherwise NULL. */ function file_save_draft_area_files($draftitemid, $contextid, $component, $filearea, $itemid, array $options = null, $text = null, $forcehttps = false) { global $USER; $usercontext = context_user::instance($USER->id); $fs = get_file_storage(); $options = (array) $options; if (!isset($options['subdirs'])) { $options['subdirs'] = false; } if (!isset($options['maxfiles'])) { $options['maxfiles'] = -1; // unlimited } if (!isset($options['maxbytes']) || $options['maxbytes'] == USER_CAN_IGNORE_FILE_SIZE_LIMITS) { $options['maxbytes'] = 0; // unlimited } $allowreferences = true; if (isset($options['return_types']) && !($options['return_types'] & FILE_REFERENCE)) { // we assume that if $options['return_types'] is NOT specified, we DO allow references. // this is not exactly right. BUT there are many places in code where filemanager options // are not passed to file_save_draft_area_files() $allowreferences = false; } $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid, 'id'); $oldfiles = $fs->get_area_files($contextid, $component, $filearea, $itemid, 'id'); if (count($draftfiles) < 2) { // means there are no files - one file means root dir only ;-) $fs->delete_area_files($contextid, $component, $filearea, $itemid); } else { if (count($oldfiles) < 2) { $filecount = 0; // there were no files before - one file means root dir only ;-) foreach ($draftfiles as $file) { $file_record = array('contextid' => $contextid, 'component' => $component, 'filearea' => $filearea, 'itemid' => $itemid); if (!$options['subdirs']) { if ($file->get_filepath() !== '/' or $file->is_directory()) { continue; } } if ($options['maxbytes'] and $options['maxbytes'] < $file->get_filesize()) { // oversized file - should not get here at all continue; } if ($options['maxfiles'] != -1 and $options['maxfiles'] <= $filecount) { // more files - should not get here at all break; } if (!$file->is_directory()) { $filecount++; } if ($file->is_external_file()) { if (!$allowreferences) { continue; } $repoid = $file->get_repository_id(); if (!empty($repoid)) { $file_record['repositoryid'] = $repoid; $file_record['reference'] = $file->get_reference(); } } file_restore_source_field_from_draft_file($file); $fs->create_file_from_storedfile($file_record, $file); } } else { // we have to merge old and new files - we want to keep file ids for files that were not changed // we change time modified for all new and changed files, we keep time created as is $newhashes = array(); foreach ($draftfiles as $file) { $newhash = $fs->get_pathname_hash($contextid, $component, $filearea, $itemid, $file->get_filepath(), $file->get_filename()); file_restore_source_field_from_draft_file($file); $newhashes[$newhash] = $file; } $filecount = 0; foreach ($oldfiles as $oldfile) { $oldhash = $oldfile->get_pathnamehash(); if (!isset($newhashes[$oldhash])) { // delete files not needed any more - deleted by user $oldfile->delete(); continue; } $newfile = $newhashes[$oldhash]; // status changed, we delete old file, and create a new one if ($oldfile->get_status() != $newfile->get_status()) { // file was changed, use updated with new timemodified data $oldfile->delete(); // This file will be added later continue; } // Updated author if ($oldfile->get_author() != $newfile->get_author()) { $oldfile->set_author($newfile->get_author()); } // Updated license if ($oldfile->get_license() != $newfile->get_license()) { $oldfile->set_license($newfile->get_license()); } // Updated file source if ($oldfile->get_source() != $newfile->get_source()) { $oldfile->set_source($newfile->get_source()); } // Updated sort order if ($oldfile->get_sortorder() != $newfile->get_sortorder()) { $oldfile->set_sortorder($newfile->get_sortorder()); } // Update file timemodified if ($oldfile->get_timemodified() != $newfile->get_timemodified()) { $oldfile->set_timemodified($newfile->get_timemodified()); } // Replaced file content if ($oldfile->get_contenthash() != $newfile->get_contenthash() || $oldfile->get_filesize() != $newfile->get_filesize()) { $oldfile->replace_content_with($newfile); // push changes to all local files that are referencing this file $fs->update_references_to_storedfile($oldfile); } // unchanged file or directory - we keep it as is unset($newhashes[$oldhash]); if (!$oldfile->is_directory()) { $filecount++; } } // Add fresh file or the file which has changed status // the size and subdirectory tests are extra safety only, the UI should prevent it foreach ($newhashes as $file) { $file_record = array('contextid' => $contextid, 'component' => $component, 'filearea' => $filearea, 'itemid' => $itemid, 'timemodified' => time()); if (!$options['subdirs']) { if ($file->get_filepath() !== '/' or $file->is_directory()) { continue; } } if ($options['maxbytes'] and $options['maxbytes'] < $file->get_filesize()) { // oversized file - should not get here at all continue; } if ($options['maxfiles'] != -1 and $options['maxfiles'] <= $filecount) { // more files - should not get here at all break; } if (!$file->is_directory()) { $filecount++; } if ($file->is_external_file()) { if (!$allowreferences) { continue; } $repoid = $file->get_repository_id(); if (!empty($repoid)) { $file_record['repositoryid'] = $repoid; $file_record['reference'] = $file->get_reference(); } } $fs->create_file_from_storedfile($file_record, $file); } } } // note: do not purge the draft area - we clean up areas later in cron, // the reason is that user might press submit twice and they would loose the files, // also sometimes we might want to use hacks that save files into two different areas if (is_null($text)) { return null; } else { return file_rewrite_urls_to_pluginfile($text, $draftitemid, $forcehttps); } }
/** * Update the calendar entries for this assignment. * * @param \stdClass $collaborate- collaborate record * * @return bool */ public static function update_calendar($collaborate) { global $DB, $CFG; require_once $CFG->dirroot . '/calendar/lib.php'; $event = new \stdClass(); $params = array('modulename' => 'collaborate', 'instance' => $collaborate->id); $event->id = $DB->get_field('event', 'id', $params); $event->name = $collaborate->name; $event->timestart = $collaborate->timestart; // Convert the links to pluginfile. It is a bit hacky but at this stage the files // might not have been saved in the module area yet. $intro = $collaborate->intro; if ($draftid = file_get_submitted_draft_itemid('introeditor')) { $intro = file_rewrite_urls_to_pluginfile($intro, $draftid); } // We need to remove the links to files as the calendar is not ready // to support module events with file areas. $intro = strip_pluginfile_content($intro); $event->description = array('text' => $intro, 'format' => $collaborate->introformat); if ($event->id) { $calendarevent = \calendar_event::load($event->id); $calendarevent->update($event); } else { unset($event->id); $event->courseid = $collaborate->course; $event->groupid = 0; $event->userid = 0; $event->modulename = 'collaborate'; $event->instance = $collaborate->id; $event->eventtype = 'due'; $event->timeend = self::timeend_from_duration($collaborate->timestart, $collaborate->duration); if (!empty($event->timeend)) { $event->timeduration = $event->timeend - $event->timestart; } else { $event->timeduration = 0; } \calendar_event::create($event); } }
public function save() { global $DB, $USER; $admin = get_admin(); // possible cronjob if ($USER->id != $admin->id and $USER->id != $this->_authorid) { throw new \moodle_exception("This doesn't belong to you!"); } $context = $this->dialogue->context; // needed for filelib functions $dialogueid = $this->dialogue->dialogueid; $conversationid = $this->conversation->conversationid; $record = new \stdClass(); $record->id = $this->_messageid; $record->dialogueid = $dialogueid; $record->conversationid = $conversationid; $record->conversationindex = $this->_conversationindex; $record->authorid = $this->_authorid; // rewrite body now if has embedded files if (dialogue_contains_draft_files($this->_bodydraftid)) { $record->body = file_rewrite_urls_to_pluginfile($this->_body, $this->_bodydraftid); } else { $record->body = $this->_body; } $record->bodyformat = $this->_bodyformat; // mark atttachments now if has them if (dialogue_contains_draft_files($this->_attachmentsdraftid)) { $record->attachments = 1; } else { $record->attachments = 0; } $record->state = $this->_state; $record->timecreated = $this->_timecreated; $record->timemodified = $this->_timemodified; if (is_null($this->_messageid)) { // create new record $this->_messageid = $DB->insert_record('dialogue_messages', $record); } else { $record->timemodified = time(); // update existing record $DB->update_record('dialogue_messages', $record); } // deal with embedded files if ($this->_bodydraftid) { file_save_draft_area_files($this->_bodydraftid, $context->id, 'mod_dialogue', 'message', $this->_messageid); } // deal with attached files if ($this->_attachmentsdraftid) { file_save_draft_area_files($this->_attachmentsdraftid, $context->id, 'mod_dialogue', 'attachment', $this->_messageid); } return true; }