���ѧۧݧ�ӧ�� �ާ֧ߧ֧էا֧� - ���֧էѧܧ�ڧ��ӧѧ�� - /home3/cpr76684/public_html/file.tar
���ѧ٧ѧ�
uploadzipform.php 0000644 00000005515 15152217772 0010167 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * This file contains the forms to create and edit an instance of this module * * @package assignfeedback_file * @copyright 2012 NetSpot {@link http://www.netspot.com.au} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); require_once($CFG->libdir.'/formslib.php'); /** * Upload feedback zip * * @package assignfeedback_file * @copyright 2012 NetSpot {@link http://www.netspot.com.au} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class assignfeedback_file_upload_zip_form extends moodleform { /** * Define this form - called by the parent constructor */ public function definition() { global $COURSE, $USER; $mform = $this->_form; $params = $this->_customdata; $mform->addElement('header', 'uploadzip', get_string('uploadzip', 'assignfeedback_file')); $fileoptions = array('subdirs'=>0, 'maxbytes'=>$COURSE->maxbytes, 'accepted_types'=>'zip', 'maxfiles'=>1, 'return_types'=>FILE_INTERNAL); $mform->addElement('filepicker', 'feedbackzip', get_string('uploadafile'), null, $fileoptions); $mform->addRule('feedbackzip', get_string('uploadnofilefound'), 'required', null, 'client'); $mform->addHelpButton('feedbackzip', 'feedbackzip', 'assignfeedback_file'); $mform->addElement('hidden', 'id', $params['cm']); $mform->setType('id', PARAM_INT); $mform->addElement('hidden', 'action', 'viewpluginpage'); $mform->setType('action', PARAM_ALPHA); $mform->addElement('hidden', 'pluginaction', 'uploadzip'); $mform->setType('pluginaction', PARAM_ALPHA); $mform->addElement('hidden', 'plugin', 'file'); $mform->setType('plugin', PARAM_PLUGIN); $mform->addElement('hidden', 'pluginsubtype', 'assignfeedback'); $mform->setType('pluginsubtype', PARAM_PLUGIN); $this->add_action_buttons(true, get_string('importfeedbackfiles', 'assignfeedback_file')); } } db/upgrade.php 0000644 00000002662 15152217772 0007310 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Upgrade code for the feedback_file module. * * @package assignfeedback_file * @copyright 2012 NetSpot {@link http://www.netspot.com.au} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); /** * Stub for upgrade code * @param int $oldversion * @return bool */ function xmldb_assignfeedback_file_upgrade($oldversion) { global $CFG; // Automatically generated Moodle v3.9.0 release upgrade line. // Put any upgrade step following this. // Automatically generated Moodle v4.0.0 release upgrade line. // Put any upgrade step following this. // Automatically generated Moodle v4.1.0 release upgrade line. // Put any upgrade step following this. return true; } db/install.php 0000644 00000002643 15152217772 0007326 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Post-install code for the assignfeedback_file module. * * @package assignfeedback_file * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); /** * Code run after the assignfeedback_file module database tables have been created. * Moves the feedback file plugin down * * @return bool */ function xmldb_assignfeedback_file_install() { global $CFG; require_once($CFG->dirroot . '/mod/assign/adminlib.php'); // Set the correct initial order for the plugins. $pluginmanager = new assign_plugin_manager('assignfeedback'); $pluginmanager->move_plugin('file', 'down'); return true; } db/access.php 0000644 00000001653 15152217772 0007121 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Capability definitions for this module. * * @package assignfeedback_file * @copyright 2012 NetSpot {@link http://www.netspot.com.au} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ $capabilities = array(); db/install.xml 0000644 00000002507 15152217772 0007336 0 ustar 00 <?xml version="1.0" encoding="UTF-8" ?> <XMLDB PATH="mod/assign/feedback/file/db" VERSION="20120423" COMMENT="XMLDB file for Moodle mod/assign/feedback/file" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/xmldb/xmldb.xsd" > <TABLES> <TABLE NAME="assignfeedback_file" COMMENT="Stores info about the number of files submitted by a student."> <FIELDS> <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/> <FIELD NAME="assignment" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/> <FIELD NAME="grade" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/> <FIELD NAME="numfiles" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The number of files uploaded by a grader."/> </FIELDS> <KEYS> <KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="Unique id for this feedback value."/> <KEY NAME="assignment" TYPE="foreign" FIELDS="assignment" REFTABLE="assign" REFFIELDS="id" COMMENT="The assignment instance this feedback relates to."/> <KEY NAME="grade" TYPE="foreign" FIELDS="grade" REFTABLE="assign_grades" REFFIELDS="id" COMMENT="The grade instance this feedback relates to."/> </KEYS> </TABLE> </TABLES> </XMLDB> locallib.php 0000644 00000072116 15152217772 0007056 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * This file contains the definition for the library class for file feedback plugin * * * @package assignfeedback_file * @copyright 2012 NetSpot {@link http://www.netspot.com.au} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); use \mod_assign\output\assign_header; // File areas for file feedback assignment. define('ASSIGNFEEDBACK_FILE_FILEAREA', 'feedback_files'); define('ASSIGNFEEDBACK_FILE_BATCH_FILEAREA', 'feedback_files_batch'); define('ASSIGNFEEDBACK_FILE_IMPORT_FILEAREA', 'feedback_files_import'); define('ASSIGNFEEDBACK_FILE_MAXSUMMARYFILES', 5); define('ASSIGNFEEDBACK_FILE_MAXSUMMARYUSERS', 5); define('ASSIGNFEEDBACK_FILE_MAXFILEUNZIPTIME', 120); /** * Library class for file feedback plugin extending feedback plugin base class. * * @package assignfeedback_file * @copyright 2012 NetSpot {@link http://www.netspot.com.au} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class assign_feedback_file extends assign_feedback_plugin { /** * Get the name of the file feedback plugin. * * @return string */ public function get_name() { return get_string('file', 'assignfeedback_file'); } /** * Get file feedback information from the database. * * @param int $gradeid * @return mixed */ public function get_file_feedback($gradeid) { global $DB; return $DB->get_record('assignfeedback_file', array('grade'=>$gradeid)); } /** * File format options. * * @return array */ private function get_file_options() { global $COURSE; $fileoptions = array('subdirs'=>1, 'maxbytes'=>$COURSE->maxbytes, 'accepted_types'=>'*', 'return_types'=>FILE_INTERNAL); return $fileoptions; } /** * Has the feedback file been modified? * * @param stdClass $grade Grade object. * @param stdClass $data Form data. * @return boolean True if the file area has been modified, else false. */ public function is_feedback_modified(stdClass $grade, stdClass $data) { global $USER; $filekey = null; $draftareainfo = null; foreach ($data as $key => $value) { if (strpos($key, 'files_') === 0 && strpos($key, '_filemanager')) { $filekey = $key; } } if (isset($filekey)) { $draftareainfo = file_get_draft_area_info($data->$filekey); $filecount = $this->count_files($grade->id, ASSIGNFEEDBACK_FILE_FILEAREA); if ($filecount != $draftareainfo['filecount']) { return true; } else { // We need to check that the files in the draft area are the same as in the file area. $usercontext = context_user::instance($USER->id); $fs = get_file_storage(); $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $data->$filekey, 'id', true); $files = $fs->get_area_files($this->assignment->get_context()->id, 'assignfeedback_file', ASSIGNFEEDBACK_FILE_FILEAREA, $grade->id, 'id', false); foreach ($files as $key => $file) { // Flag for recording if we have a matching file. $matchflag = false; foreach ($draftfiles as $draftkey => $draftfile) { if (!$file->is_directory()) { // File name is the same, but it could be a different file with the same name. if ($draftfile->get_filename() == $file->get_filename()) { // If the file name is the same but the content hash is different, or // The file path for the file has changed, then we have a modification. if ($draftfile->get_contenthash() != $file->get_contenthash() || $draftfile->get_filepath() != $file->get_filepath()) { return true; } // These files match. Check the next file. $matchflag = true; // We have a match on the file name so we can move to the next file and not // proceed through the other draftfiles. break; } } } // If the file does not match then there has been a modification. if (!$matchflag) { return true; } } } } return false; } /** * Copy all the files from one file area to another. * * @param file_storage $fs - The source context id * @param int $fromcontextid - The source context id * @param string $fromcomponent - The source component * @param string $fromfilearea - The source filearea * @param int $fromitemid - The source item id * @param int $tocontextid - The destination context id * @param string $tocomponent - The destination component * @param string $tofilearea - The destination filearea * @param int $toitemid - The destination item id * @return boolean */ private function copy_area_files(file_storage $fs, $fromcontextid, $fromcomponent, $fromfilearea, $fromitemid, $tocontextid, $tocomponent, $tofilearea, $toitemid) { $newfilerecord = new stdClass(); $newfilerecord->contextid = $tocontextid; $newfilerecord->component = $tocomponent; $newfilerecord->filearea = $tofilearea; $newfilerecord->itemid = $toitemid; if ($files = $fs->get_area_files($fromcontextid, $fromcomponent, $fromfilearea, $fromitemid)) { foreach ($files as $file) { if ($file->is_directory() and $file->get_filepath() === '/') { // We need a way to mark the age of each draft area. // By not copying the root dir we force it to be created // automatically with current timestamp. continue; } $existingfile = $fs->get_file( $newfilerecord->contextid, $newfilerecord->component, $newfilerecord->filearea, $newfilerecord->itemid, $file->get_filepath(), $file->get_filename() ); if ($existingfile) { // If the file already exists, remove it so it can be updated. $existingfile->delete(); } $newfile = $fs->create_file_from_storedfile($newfilerecord, $file); } } return true; } /** * Get form elements for grading form. * * @param stdClass $grade * @param MoodleQuickForm $mform * @param stdClass $data * @param int $userid The userid we are currently grading * @return bool true if elements were added to the form */ public function get_form_elements_for_user($grade, MoodleQuickForm $mform, stdClass $data, $userid) { $fileoptions = $this->get_file_options(); $gradeid = $grade ? $grade->id : 0; $elementname = 'files_' . $userid; $data = file_prepare_standard_filemanager($data, $elementname, $fileoptions, $this->assignment->get_context(), 'assignfeedback_file', ASSIGNFEEDBACK_FILE_FILEAREA, $gradeid); $mform->addElement('filemanager', $elementname . '_filemanager', $this->get_name(), null, $fileoptions); return true; } /** * Count the number of files. * * @param int $gradeid * @param string $area * @return int */ private function count_files($gradeid, $area) { $fs = get_file_storage(); $files = $fs->get_area_files($this->assignment->get_context()->id, 'assignfeedback_file', $area, $gradeid, 'id', false); return count($files); } /** * Update the number of files in the file area. * * @param stdClass $grade The grade record * @return bool - true if the value was saved */ public function update_file_count($grade) { global $DB; $filefeedback = $this->get_file_feedback($grade->id); if ($filefeedback) { $filefeedback->numfiles = $this->count_files($grade->id, ASSIGNFEEDBACK_FILE_FILEAREA); return $DB->update_record('assignfeedback_file', $filefeedback); } else { $filefeedback = new stdClass(); $filefeedback->numfiles = $this->count_files($grade->id, ASSIGNFEEDBACK_FILE_FILEAREA); $filefeedback->grade = $grade->id; $filefeedback->assignment = $this->assignment->get_instance()->id; return $DB->insert_record('assignfeedback_file', $filefeedback) > 0; } } /** * Save the feedback files. * * @param stdClass $grade * @param stdClass $data * @return bool */ public function save(stdClass $grade, stdClass $data) { $fileoptions = $this->get_file_options(); // The element name may have been for a different user. foreach ($data as $key => $value) { if (strpos($key, 'files_') === 0 && strpos($key, '_filemanager')) { $elementname = substr($key, 0, strpos($key, '_filemanager')); } } $data = file_postupdate_standard_filemanager($data, $elementname, $fileoptions, $this->assignment->get_context(), 'assignfeedback_file', ASSIGNFEEDBACK_FILE_FILEAREA, $grade->id); return $this->update_file_count($grade); } /** * Display the list of files in the feedback status table. * * @param stdClass $grade * @param bool $showviewlink - Set to true to show a link to see the full list of files * @return string */ public function view_summary(stdClass $grade, & $showviewlink) { $count = $this->count_files($grade->id, ASSIGNFEEDBACK_FILE_FILEAREA); // Show a view all link if the number of files is over this limit. $showviewlink = $count > ASSIGNFEEDBACK_FILE_MAXSUMMARYFILES; if ($count <= ASSIGNFEEDBACK_FILE_MAXSUMMARYFILES) { return $this->assignment->render_area_files('assignfeedback_file', ASSIGNFEEDBACK_FILE_FILEAREA, $grade->id); } else { return get_string('countfiles', 'assignfeedback_file', $count); } } /** * Display the list of files in the feedback status table. * * @param stdClass $grade * @return string */ public function view(stdClass $grade) { return $this->assignment->render_area_files('assignfeedback_file', ASSIGNFEEDBACK_FILE_FILEAREA, $grade->id); } /** * The assignment has been deleted - cleanup. * * @return bool */ public function delete_instance() { global $DB; // Will throw exception on failure. $DB->delete_records('assignfeedback_file', array('assignment'=>$this->assignment->get_instance()->id)); return true; } /** * Return true if there are no feedback files. * * @param stdClass $grade */ public function is_empty(stdClass $grade) { return $this->count_files($grade->id, ASSIGNFEEDBACK_FILE_FILEAREA) == 0; } /** * Get file areas returns a list of areas this plugin stores files. * * @return array - An array of fileareas (keys) and descriptions (values) */ public function get_file_areas() { return array(ASSIGNFEEDBACK_FILE_FILEAREA=>$this->get_name()); } /** * Return true if this plugin can upgrade an old Moodle 2.2 assignment of this type * and version. * * @param string $type old assignment subtype * @param int $version old assignment version * @return bool True if upgrade is possible */ public function can_upgrade($type, $version) { if (($type == 'upload' || $type == 'uploadsingle') && $version >= 2011112900) { return true; } return false; } /** * Upgrade the settings from the old assignment to the new plugin based one. * * @param context $oldcontext - the context for the old assignment * @param stdClass $oldassignment - the data for the old assignment * @param string $log - can be appended to by the upgrade * @return bool was it a success? (false will trigger a rollback) */ public function upgrade_settings(context $oldcontext, stdClass $oldassignment, & $log) { // First upgrade settings (nothing to do). return true; } /** * Upgrade the feedback from the old assignment to the new one. * * @param context $oldcontext - the database for the old assignment context * @param stdClass $oldassignment The data record for the old assignment * @param stdClass $oldsubmission The data record for the old submission * @param stdClass $grade The data record for the new grade * @param string $log Record upgrade messages in the log * @return bool true or false - false will trigger a rollback */ public function upgrade(context $oldcontext, stdClass $oldassignment, stdClass $oldsubmission, stdClass $grade, & $log) { global $DB; // Now copy the area files. $this->assignment->copy_area_files_for_upgrade($oldcontext->id, 'mod_assignment', 'response', $oldsubmission->id, $this->assignment->get_context()->id, 'assignfeedback_file', ASSIGNFEEDBACK_FILE_FILEAREA, $grade->id); // Now count them! $filefeedback = new stdClass(); $filefeedback->numfiles = $this->count_files($grade->id, ASSIGNFEEDBACK_FILE_FILEAREA); $filefeedback->grade = $grade->id; $filefeedback->assignment = $this->assignment->get_instance()->id; if (!$DB->insert_record('assignfeedback_file', $filefeedback) > 0) { $log .= get_string('couldnotconvertgrade', 'mod_assign', $grade->userid); return false; } return true; } /** * Return a list of the batch grading operations performed by this plugin. * This plugin supports batch upload files and upload zip. * * @return array The list of batch grading operations */ public function get_grading_batch_operations() { return array('uploadfiles'=>get_string('uploadfiles', 'assignfeedback_file')); } /** * Upload files and send them to multiple users. * * @param array $users - An array of user ids * @return string - The response html */ public function view_batch_upload_files($users) { global $CFG, $DB, $USER; require_capability('mod/assign:grade', $this->assignment->get_context()); require_once($CFG->dirroot . '/mod/assign/feedback/file/batchuploadfilesform.php'); require_once($CFG->dirroot . '/mod/assign/renderable.php'); $formparams = array('cm'=>$this->assignment->get_course_module()->id, 'users'=>$users, 'context'=>$this->assignment->get_context()); $usershtml = ''; $usercount = 0; foreach ($users as $userid) { if ($usercount >= ASSIGNFEEDBACK_FILE_MAXSUMMARYUSERS) { $moreuserscount = count($users) - ASSIGNFEEDBACK_FILE_MAXSUMMARYUSERS; $usershtml .= get_string('moreusers', 'assignfeedback_file', $moreuserscount); break; } $user = $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST); $usersummary = new assign_user_summary($user, $this->assignment->get_course()->id, has_capability('moodle/site:viewfullnames', $this->assignment->get_course_context()), $this->assignment->is_blind_marking(), $this->assignment->get_uniqueid_for_user($user->id), // TODO Does not support custom user profile fields (MDL-70456). \core_user\fields::get_identity_fields($this->assignment->get_context(), false)); $usershtml .= $this->assignment->get_renderer()->render($usersummary); $usercount += 1; } $formparams['usershtml'] = $usershtml; $mform = new assignfeedback_file_batch_upload_files_form(null, $formparams); if ($mform->is_cancelled()) { redirect(new moodle_url('view.php', array('id'=>$this->assignment->get_course_module()->id, 'action'=>'grading'))); return; } else if ($data = $mform->get_data()) { // Copy the files from the draft area to a temporary import area. $data = file_postupdate_standard_filemanager($data, 'files', $this->get_file_options(), $this->assignment->get_context(), 'assignfeedback_file', ASSIGNFEEDBACK_FILE_BATCH_FILEAREA, $USER->id); $fs = get_file_storage(); // Now copy each of these files to the users feedback file area. foreach ($users as $userid) { $grade = $this->assignment->get_user_grade($userid, true); $this->assignment->notify_grade_modified($grade); $this->copy_area_files($fs, $this->assignment->get_context()->id, 'assignfeedback_file', ASSIGNFEEDBACK_FILE_BATCH_FILEAREA, $USER->id, $this->assignment->get_context()->id, 'assignfeedback_file', ASSIGNFEEDBACK_FILE_FILEAREA, $grade->id); $filefeedback = $this->get_file_feedback($grade->id); if ($filefeedback) { $filefeedback->numfiles = $this->count_files($grade->id, ASSIGNFEEDBACK_FILE_FILEAREA); $DB->update_record('assignfeedback_file', $filefeedback); } else { $filefeedback = new stdClass(); $filefeedback->numfiles = $this->count_files($grade->id, ASSIGNFEEDBACK_FILE_FILEAREA); $filefeedback->grade = $grade->id; $filefeedback->assignment = $this->assignment->get_instance()->id; $DB->insert_record('assignfeedback_file', $filefeedback); } } // Now delete the temporary import area. $fs->delete_area_files($this->assignment->get_context()->id, 'assignfeedback_file', ASSIGNFEEDBACK_FILE_BATCH_FILEAREA, $USER->id); redirect(new moodle_url('view.php', array('id'=>$this->assignment->get_course_module()->id, 'action'=>'grading'))); return; } else { $header = new assign_header($this->assignment->get_instance(), $this->assignment->get_context(), false, $this->assignment->get_course_module()->id, get_string('batchuploadfiles', 'assignfeedback_file')); $o = ''; $o .= $this->assignment->get_renderer()->render($header); $o .= $this->assignment->get_renderer()->render(new assign_form('batchuploadfiles', $mform)); $o .= $this->assignment->get_renderer()->render_footer(); } return $o; } /** * User has chosen a custom grading batch operation and selected some users. * * @param string $action - The chosen action * @param array $users - An array of user ids * @return string - The response html */ public function grading_batch_operation($action, $users) { if ($action == 'uploadfiles') { return $this->view_batch_upload_files($users); } return ''; } /** * View the upload zip form. * * @return string - The html response */ public function view_upload_zip() { global $CFG, $USER; require_capability('mod/assign:grade', $this->assignment->get_context()); require_once($CFG->dirroot . '/mod/assign/feedback/file/uploadzipform.php'); require_once($CFG->dirroot . '/mod/assign/feedback/file/importziplib.php'); require_once($CFG->dirroot . '/mod/assign/feedback/file/importzipform.php'); $formparams = array('context'=>$this->assignment->get_context(), 'cm'=>$this->assignment->get_course_module()->id); $mform = new assignfeedback_file_upload_zip_form(null, $formparams); $o = ''; $confirm = optional_param('confirm', 0, PARAM_BOOL); $renderer = $this->assignment->get_renderer(); // Delete any existing files. $importer = new assignfeedback_file_zip_importer(); $contextid = $this->assignment->get_context()->id; if ($mform->is_cancelled()) { $importer->delete_import_files($contextid); $urlparams = array('id'=>$this->assignment->get_course_module()->id, 'action'=>'grading'); $url = new moodle_url('view.php', $urlparams); redirect($url); return; } else if ($confirm) { $params = array('assignment'=>$this->assignment, 'importer'=>$importer); $mform = new assignfeedback_file_import_zip_form(null, $params); if ($mform->is_cancelled()) { $importer->delete_import_files($contextid); $urlparams = array('id'=>$this->assignment->get_course_module()->id, 'action'=>'grading'); $url = new moodle_url('view.php', $urlparams); redirect($url); return; } $o .= $importer->import_zip_files($this->assignment, $this); $importer->delete_import_files($contextid); } else if (($data = $mform->get_data()) && ($zipfile = $mform->save_stored_file('feedbackzip', $contextid, 'assignfeedback_file', ASSIGNFEEDBACK_FILE_IMPORT_FILEAREA, $USER->id, '/', 'import.zip', true))) { $importer->extract_files_from_zip($zipfile, $contextid); $params = array('assignment'=>$this->assignment, 'importer'=>$importer); $mform = new assignfeedback_file_import_zip_form(null, $params); $header = new assign_header($this->assignment->get_instance(), $this->assignment->get_context(), false, $this->assignment->get_course_module()->id, get_string('confirmuploadzip', 'assignfeedback_file')); $o .= $renderer->render($header); $o .= $renderer->render(new assign_form('confirmimportzip', $mform)); $o .= $renderer->render_footer(); } else { $header = new assign_header($this->assignment->get_instance(), $this->assignment->get_context(), false, $this->assignment->get_course_module()->id, get_string('uploadzip', 'assignfeedback_file')); $o .= $renderer->render($header); $o .= $renderer->render(new assign_form('uploadfeedbackzip', $mform)); $o .= $renderer->render_footer(); } return $o; } /** * Called by the assignment module when someone chooses something from the * grading navigation or batch operations list. * * @param string $action - The page to view * @return string - The html response */ public function view_page($action) { if ($action == 'uploadfiles') { $users = required_param('selectedusers', PARAM_SEQUENCE); return $this->view_batch_upload_files(explode(',', $users)); } if ($action == 'uploadzip') { return $this->view_upload_zip(); } return ''; } /** * Return a list of the grading actions performed by this plugin. * This plugin supports upload zip. * * @return array The list of grading actions */ public function get_grading_actions() { return array('uploadzip'=>get_string('uploadzip', 'assignfeedback_file')); } /** * Return a description of external params suitable for uploading a feedback file from a webservice. * * @return external_description|null */ public function get_external_parameters() { return array( 'files_filemanager' => new external_value( PARAM_INT, 'The id of a draft area containing files for this feedback.', VALUE_OPTIONAL ) ); } /** * Return the plugin configs for external functions. * * @return array the list of settings * @since Moodle 3.2 */ public function get_config_for_external() { return (array) $this->get_config(); } } renderer.php 0000644 00000004176 15152217772 0007104 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * This file contains a renderer for the assignment class * * @package assignfeedback_file * @copyright 2012 NetSpot {@link http://www.netspot.com.au} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); /** * A custom renderer class that extends the plugin_renderer_base and is used by the assign module. * * @package assignfeedback_file * @copyright 2012 NetSpot {@link http://www.netspot.com.au} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class assignfeedback_file_renderer extends plugin_renderer_base { /** * Render a summary of the zip file import * * @param assignfeedback_file_import_summary $summary - Stats about the zip import * @return string The html response */ public function render_assignfeedback_file_import_summary($summary) { $o = ''; $o .= $this->container(get_string('userswithnewfeedback', 'assignfeedback_file', $summary->userswithnewfeedback)); $o .= $this->container(get_string('filesupdated', 'assignfeedback_file', $summary->feedbackfilesupdated)); $o .= $this->container(get_string('filesadded', 'assignfeedback_file', $summary->feedbackfilesadded)); $url = new moodle_url('view.php', array('id'=>$summary->cmid, 'action'=>'grading')); $o .= $this->continue_button($url); return $o; } } renderable.php 0000644 00000004527 15152217772 0007401 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * This file contains a renderer for the assignment class * * @package assignfeedback_file * @copyright 2012 NetSpot {@link http://www.netspot.com.au} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); /** * A renderable summary of the zip import * * @package assignfeedback_file * @copyright 2012 NetSpot {@link http://www.netspot.com.au} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class assignfeedback_file_import_summary implements renderable { /** @var int $cmid Course module id for constructing navigation links */ public $cmid = 0; /** @var int $userswithnewfeedback The number of users who have received new feedback */ public $userswithnewfeedback = 0; /** @var int $feedbackfilesadded The number of new feedback files */ public $feedbackfilesadded = 0; /** @var int $feedbackfilesupdated The number of updated feedback files */ public $feedbackfilesupdated = 0; /** * Constructor for this renderable class * * @param int $cmid - The course module id for navigation * @param int $userswithnewfeedback - The number of users with new feedback * @param int $feedbackfilesadded - The number of feedback files added * @param int $feedbackfilesupdated - The number of feedback files updated */ public function __construct($cmid, $userswithnewfeedback, $feedbackfilesadded, $feedbackfilesupdated) { $this->cmid = $cmid; $this->userswithnewfeedback = $userswithnewfeedback; $this->feedbackfilesadded = $feedbackfilesadded; $this->feedbackfilesupdated = $feedbackfilesupdated; } } importzipform.php 0000644 00000013027 15152217772 0010212 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * This file contains the forms to create and edit an instance of this module * * @package assignfeedback_file * @copyright 2012 NetSpot {@link http://www.netspot.com.au} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); require_once($CFG->libdir.'/formslib.php'); require_once($CFG->dirroot.'/mod/assign/feedback/file/importziplib.php'); /** * Import zip form * * @package assignfeedback_file * @copyright 2012 NetSpot {@link http://www.netspot.com.au} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class assignfeedback_file_import_zip_form extends moodleform implements renderable { /** * Create this grade import form */ public function definition() { global $CFG, $PAGE; $mform = $this->_form; $params = $this->_customdata; $renderer = $PAGE->get_renderer('assign'); // Visible elements. $assignment = $params['assignment']; $contextid = $assignment->get_context()->id; $importer = $params['importer']; $update = false; if (!$importer) { throw new \moodle_exception('invalidarguments'); return; } $files = $importer->get_import_files($contextid); $mform->addElement('header', 'uploadzip', get_string('confirmuploadzip', 'assignfeedback_file')); $currentgroup = groups_get_activity_group($assignment->get_course_module(), true); $allusers = $assignment->list_participants($currentgroup, false); $participants = array(); foreach ($allusers as $user) { $participants[$assignment->get_uniqueid_for_user($user->id)] = $user; } $fs = get_file_storage(); $updates = array(); foreach ($files as $unzippedfile) { $user = null; $plugin = null; $filename = ''; if ($importer->is_valid_filename_for_import($assignment, $unzippedfile, $participants, $user, $plugin, $filename)) { if ($importer->is_file_modified($assignment, $user, $plugin, $filename, $unzippedfile)) { // Get a string we can show to identify this user. $userdesc = fullname($user, has_capability('moodle/site:viewfullnames', $assignment->get_context())); $path = pathinfo($filename); if ($assignment->is_blind_marking()) { $userdesc = get_string('hiddenuser', 'assign') . $assignment->get_uniqueid_for_user($user->id); } $grade = $assignment->get_user_grade($user->id, false); $exists = false; if ($grade) { $exists = $fs->file_exists($contextid, 'assignfeedback_file', ASSIGNFEEDBACK_FILE_FILEAREA, $grade->id, $path['dirname'], $path['basename']); } if (!$grade || !$exists) { $updates[] = get_string('feedbackfileadded', 'assignfeedback_file', array('filename'=>$filename, 'student'=>$userdesc)); } else { $updates[] = get_string('feedbackfileupdated', 'assignfeedback_file', array('filename'=>$filename, 'student'=>$userdesc)); } } } } if (count($updates)) { $mform->addElement('html', $renderer->list_block_contents(array(), $updates)); } else { $mform->addElement('html', get_string('nochanges', 'assignfeedback_file')); } $mform->addElement('hidden', 'id', $assignment->get_course_module()->id); $mform->setType('id', PARAM_INT); $mform->addElement('hidden', 'action', 'viewpluginpage'); $mform->setType('action', PARAM_ALPHA); $mform->addElement('hidden', 'confirm', 'true'); $mform->setType('confirm', PARAM_BOOL); $mform->addElement('hidden', 'plugin', 'file'); $mform->setTYpe('plugin', PARAM_PLUGIN); $mform->addElement('hidden', 'pluginsubtype', 'assignfeedback'); $mform->setTYpe('pluginsubtype', PARAM_PLUGIN); $mform->addElement('hidden', 'pluginaction', 'uploadzip'); $mform->setType('pluginaction', PARAM_ALPHA); if (count($updates)) { $this->add_action_buttons(true, get_string('confirm')); } else { $mform->addElement('cancel'); $mform->closeHeaderBefore('cancel'); } } } tests/importziplib_test.php 0000644 00000014545 15152217772 0012224 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Unit tests for importziplib. * * @package assignfeedback_file * @copyright 2020 Eric Merrill <merrill@oakland.edu> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace assignfeedback_file; use mod_assign_test_generator; defined('MOODLE_INTERNAL') || die(); global $CFG; require_once($CFG->dirroot . '/mod/assign/tests/generator.php'); require_once($CFG->dirroot . '/mod/assign/feedback/file/importziplib.php'); /** * Unit tests for importziplib. * * @package assignfeedback_file * @copyright 2020 Eric Merrill <merrill@oakland.edu> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class importziplib_test extends \advanced_testcase { // Use the generator helper. use mod_assign_test_generator; /** * Test the assignfeedback_file_zip_importer->is_valid_filename_for_import() method. */ public function test_is_valid_filename_for_import() { // Do the initial assign setup. $this->resetAfterTest(); $course = $this->getDataGenerator()->create_course(); $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); $assign = $this->create_instance($course, [ 'assignsubmission_onlinetext_enabled' => 1, 'assignfeedback_file_enabled' => 1, ]); // Create an online text submission. $this->add_submission($student, $assign); // Now onto the file work. $fs = get_file_storage(); // Setup a basic file we will work with. We will keep renaming and repathing it. $record = new \stdClass; $record->contextid = $assign->get_context()->id; $record->component = 'assignfeedback_file'; $record->filearea = ASSIGNFEEDBACK_FILE_FILEAREA; $record->itemid = $assign->get_user_grade($student->id, true)->id; $record->filepath = '/'; $record->filename = '1.txt'; $record->source = 'test'; $file = $fs->create_file_from_string($record, 'file content'); // The importer we will use. $importer = new \assignfeedback_file_zip_importer(); // Setup some variable we use. $user = null; $plugin = null; $filename = ''; $allusers = $assign->list_participants(0, false); $participants = array(); foreach ($allusers as $user) { $participants[$assign->get_uniqueid_for_user($user->id)] = $user; } $file->rename('/import/', '.hiddenfile'); $result = $importer->is_valid_filename_for_import($assign, $file, $participants, $user, $plugin, $filename); $this->assertFalse($result); $file->rename('/import/', '~hiddenfile'); $result = $importer->is_valid_filename_for_import($assign, $file, $participants, $user, $plugin, $filename); $this->assertFalse($result); $file->rename('/import/some_path_here/', 'RandomFile.txt'); $result = $importer->is_valid_filename_for_import($assign, $file, $participants, $user, $plugin, $filename); $this->assertFalse($result); $file->rename('/import/', '~hiddenfile'); $result = $importer->is_valid_filename_for_import($assign, $file, $participants, $user, $plugin, $filename); $this->assertFalse($result); // Get the students assign id. $studentid = $assign->get_uniqueid_for_user($student->id); // Submissions are identified with the format: // StudentName_StudentID_PluginType_Plugin_FilePathAndName. // Test a string student id. $badname = 'Student Name_StringID_assignsubmission_file_My_cool_filename.txt'; $file->rename('/import/', $badname); $result = $importer->is_valid_filename_for_import($assign, $file, $participants, $user, $plugin, $filename); $this->assertFalse($result); // Test an invalid student id. $badname = 'Student Name_' . ($studentid + 100) . '_assignsubmission_file_My_cool_filename.txt'; $file->rename('/import/', $badname); $result = $importer->is_valid_filename_for_import($assign, $file, $participants, $user, $plugin, $filename); $this->assertFalse($result); // Test an invalid submission plugin. $badname = 'Student Name_' . $studentid . '_assignsubmission_noplugin_My_cool_filename.txt'; $file->rename('/import/', $badname); $result = $importer->is_valid_filename_for_import($assign, $file, $participants, $user, $plugin, $filename); $this->assertFalse($result); // Test a basic, good file. $goodbase = 'Student Name_' . $studentid . '_assignsubmission_file_'; $file->rename('/import/', $goodbase . "My_cool_filename.txt"); $result = $importer->is_valid_filename_for_import($assign, $file, $participants, $user, $plugin, $filename); $this->assertTrue($result); $this->assertEquals($participants[$studentid], $user); $this->assertEquals('My_cool_filename.txt', $filename); $this->assertInstanceOf(\assign_submission_file::class, $plugin); // Test another good file, with some additional path and underscores. $user = null; $plugin = null; $filename = ''; $file->rename('/import/some_path_here/' . $goodbase . '/some_path/', 'My File.txt'); $result = $importer->is_valid_filename_for_import($assign, $file, $participants, $user, $plugin, $filename); $this->assertTrue($result); $this->assertEquals($participants[$studentid], $user); $this->assertEquals('/some_path/My File.txt', $filename); $this->assertInstanceOf(\assign_submission_file::class, $plugin); } } tests/fixtures/feedback.txt 0000644 00000000014 15152217772 0012050 0 ustar 00 feedback.txt tests/fixtures/submission.txt 0000644 00000000016 15152217772 0012521 0 ustar 00 submission.txt tests/privacy/provider_test.php 0000644 00000027666 15152217772 0013017 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Unit tests for assignfeedback_file. * * @package assignfeedback_file * @copyright 2018 Adrian Greeve <adrian@moodle.com> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace assignfeedback_file\privacy; defined('MOODLE_INTERNAL') || die(); global $CFG; require_once($CFG->dirroot . '/mod/assign/locallib.php'); require_once($CFG->dirroot . '/mod/assign/tests/privacy/provider_test.php'); use mod_assign\privacy\assign_plugin_request_data; /** * Unit tests for mod/assign/feedback/file/classes/privacy/ * * @copyright 2018 Adrian Greeve <adrian@moodle.com> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class provider_test extends \mod_assign\privacy\provider_test { /** * Convenience function for creating feedback data. * * @param object $assign assign object * @param stdClass $student user object * @param stdClass $teacher user object * @param string $submissiontext Submission text * @param string $feedbacktext Feedback text * @return array Feedback plugin object and the grade object. */ protected function create_feedback($assign, $student, $teacher, $submissiontext, $feedbacktext) { $submission = new \stdClass(); $submission->assignment = $assign->get_instance()->id; $submission->userid = $student->id; $submission->timecreated = time(); $submission->onlinetext_editor = ['text' => $submissiontext, 'format' => FORMAT_MOODLE]; $this->setUser($student); $notices = []; $assign->save_submission($submission, $notices); $grade = $assign->get_user_grade($student->id, true); $this->setUser($teacher); $context = \context_user::instance($teacher->id); $draftitemid = file_get_unused_draft_itemid(); file_prepare_draft_area($draftitemid, $context->id, 'assignfeedback_file', 'feedback_files', 1); $dummy = array( 'contextid' => $context->id, 'component' => 'user', 'filearea' => 'draft', 'itemid' => $draftitemid, 'filepath' => '/', 'filename' => 'feedback1.txt' ); $fs = get_file_storage(); $file = $fs->create_file_from_string($dummy, $feedbacktext); // Create formdata. $data = new \stdClass(); $data->{'files_' . $teacher->id . '_filemanager'} = $draftitemid; $plugin = $assign->get_feedback_plugin_by_type('file'); // Save the feedback. $plugin->save($grade, $data); return [$plugin, $grade]; } /** * Quick test to make sure that get_metadata returns something. */ public function test_get_metadata() { $collection = new \core_privacy\local\metadata\collection('assignfeedback_file'); $collection = \assignfeedback_file\privacy\provider::get_metadata($collection); $this->assertNotEmpty($collection); } /** * Test that feedback comments are exported for a user. */ public function test_export_feedback_user_data() { $this->resetAfterTest(); // Create course, assignment, submission, and then a feedback comment. $course = $this->getDataGenerator()->create_course(); // Student. $user1 = $this->getDataGenerator()->create_user(); // Teacher. $user2 = $this->getDataGenerator()->create_user(); $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student'); $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'editingteacher'); $assign = $this->create_instance(['course' => $course]); $context = $assign->get_context(); $feedbacktext = '<p>first comment for this test</p>'; list($plugin, $grade) = $this->create_feedback($assign, $user1, $user2, 'Submission text', $feedbacktext); $writer = \core_privacy\local\request\writer::with_context($context); $this->assertFalse($writer->has_any_data()); // The student should be able to see the teachers feedback. $exportdata = new \mod_assign\privacy\assign_plugin_request_data($context, $assign, $grade, [], $user1); \assignfeedback_file\privacy\provider::export_feedback_user_data($exportdata); $feedbackfile = $writer->get_files([get_string('privacy:path', 'assignfeedback_file')])['feedback1.txt']; // Check that we got a stored file. $this->assertInstanceOf('stored_file', $feedbackfile); $this->assertEquals('feedback1.txt', $feedbackfile->get_filename()); } /** * Test that all feedback is deleted for a context. */ public function test_delete_feedback_for_context() { $this->resetAfterTest(); // Create course, assignment, submission, and then a feedback comment. $course = $this->getDataGenerator()->create_course(); // Students. $user1 = $this->getDataGenerator()->create_user(); $user2 = $this->getDataGenerator()->create_user(); // Teacher. $user3 = $this->getDataGenerator()->create_user(); $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student'); $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student'); $this->getDataGenerator()->enrol_user($user3->id, $course->id, 'editingteacher'); $assign = $this->create_instance(['course' => $course]); $context = $assign->get_context(); $feedbacktext = '<p>first comment for this test</p>'; list($plugin1, $grade1) = $this->create_feedback($assign, $user1, $user3, 'Submission text', $feedbacktext); $feedbacktext = '<p>Comment for second submission.</p>'; list($plugin2, $grade2) = $this->create_feedback($assign, $user2, $user3, 'Submission text', $feedbacktext); // Check that we have data. $this->assertFalse($plugin1->is_empty($grade1)); $this->assertFalse($plugin2->is_empty($grade2)); $requestdata = new assign_plugin_request_data($context, $assign); \assignfeedback_file\privacy\provider::delete_feedback_for_context($requestdata); // Check that we now have no data. $this->assertTrue($plugin1->is_empty($grade1)); $this->assertTrue($plugin2->is_empty($grade2)); } /** * Test that a grade item is deleted for a user. */ public function test_delete_feedback_for_grade() { $this->resetAfterTest(); // Create course, assignment, submission, and then a feedback comment. $course = $this->getDataGenerator()->create_course(); // Students. $user1 = $this->getDataGenerator()->create_user(); $user2 = $this->getDataGenerator()->create_user(); // Teacher. $user3 = $this->getDataGenerator()->create_user(); $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student'); $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student'); $this->getDataGenerator()->enrol_user($user3->id, $course->id, 'editingteacher'); $assign = $this->create_instance(['course' => $course]); $context = $assign->get_context(); $feedbacktext = '<p>first comment for this test</p>'; list($plugin1, $grade1) = $this->create_feedback($assign, $user1, $user3, 'Submission text', $feedbacktext); $feedbacktext = '<p>Comment for second submission.</p>'; list($plugin2, $grade2) = $this->create_feedback($assign, $user2, $user3, 'Submission text', $feedbacktext); // Check that we have data. $this->assertFalse($plugin1->is_empty($grade1)); $this->assertFalse($plugin2->is_empty($grade2)); $requestdata = new assign_plugin_request_data($context, $assign, $grade1, [], $user1); \assignfeedback_file\privacy\provider::delete_feedback_for_grade($requestdata); // Check that we now have no data. $this->assertTrue($plugin1->is_empty($grade1)); // User 2's data should still be intact. $this->assertFalse($plugin2->is_empty($grade2)); } /** * Test that a grade item is deleted for a user. */ public function test_delete_feedback_for_grades() { $this->resetAfterTest(); // Create course, assignment, submission, and then a feedback comment. $course = $this->getDataGenerator()->create_course(); // Students. $user1 = $this->getDataGenerator()->create_user(); $user2 = $this->getDataGenerator()->create_user(); $user3 = $this->getDataGenerator()->create_user(); $user4 = $this->getDataGenerator()->create_user(); // Teacher. $user5 = $this->getDataGenerator()->create_user(); $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student'); $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student'); $this->getDataGenerator()->enrol_user($user3->id, $course->id, 'student'); $this->getDataGenerator()->enrol_user($user4->id, $course->id, 'student'); $this->getDataGenerator()->enrol_user($user5->id, $course->id, 'editingteacher'); $assign1 = $this->create_instance(['course' => $course]); $assign2 = $this->create_instance(['course' => $course]); $context = $assign1->get_context(); $feedbacktext = '<p>first comment for this test</p>'; list($plugin1, $grade1) = $this->create_feedback($assign1, $user1, $user5, 'Submission text', $feedbacktext); $feedbacktext = '<p>Comment for second submission.</p>'; list($plugin2, $grade2) = $this->create_feedback($assign1, $user2, $user5, 'Submission text', $feedbacktext); $feedbacktext = '<p>Comment for second submission.</p>'; list($plugin3, $grade3) = $this->create_feedback($assign1, $user3, $user5, 'Submission text', $feedbacktext); $feedbacktext = '<p>Comment for second submission.</p>'; list($plugin4, $grade4) = $this->create_feedback($assign2, $user3, $user5, 'Submission text', $feedbacktext); $feedbacktext = '<p>Comment for second submission.</p>'; list($plugin5, $grade5) = $this->create_feedback($assign2, $user4, $user5, 'Submission text', $feedbacktext); // Check that we have data. $this->assertFalse($plugin1->is_empty($grade1)); $this->assertFalse($plugin2->is_empty($grade2)); $this->assertFalse($plugin3->is_empty($grade3)); $this->assertFalse($plugin4->is_empty($grade4)); $this->assertFalse($plugin5->is_empty($grade5)); $deletedata = new assign_plugin_request_data($context, $assign1); $deletedata->set_userids([$user1->id, $user3->id]); $deletedata->populate_submissions_and_grades(); \assignfeedback_file\privacy\provider::delete_feedback_for_grades($deletedata); // Check that we now have no data. $this->assertTrue($plugin1->is_empty($grade1)); // User 2's data should still be intact. $this->assertFalse($plugin2->is_empty($grade2)); // User 3's data in assignment 1 should be gone. $this->assertTrue($plugin3->is_empty($grade3)); // User 3's data in assignment 2 should still be intact. $this->assertFalse($plugin4->is_empty($grade4)); // User 4's data in assignment 2 should still be intact. $this->assertFalse($plugin5->is_empty($grade5)); } } tests/feedback_test.php 0000644 00000014736 15152217772 0011226 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. namespace assignfeedback_file; use mod_assign_test_generator; defined('MOODLE_INTERNAL') || die(); global $CFG; require_once($CFG->dirroot . '/mod/assign/tests/generator.php'); /** * Unit tests for assignfeedback_file * * @package assignfeedback_file * @copyright 2016 Adrian Greeve <adrian@moodle.com> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class feedback_test extends \advanced_testcase { // Use the generator helper. use mod_assign_test_generator; /** * Test the is_feedback_modified() method for the file feedback. */ public function test_is_feedback_modified() { $this->resetAfterTest(); $course = $this->getDataGenerator()->create_course(); $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); $assign = $this->create_instance($course, [ 'assignsubmission_onlinetext_enabled' => 1, 'assignfeedback_comments_enabled' => 1, ]); // Create an online text submission. $this->add_submission($student, $assign); $this->setUser($teacher); $fs = get_file_storage(); $context = \context_user::instance($teacher->id); $draftitemid = file_get_unused_draft_itemid(); file_prepare_draft_area($draftitemid, $context->id, 'assignfeedback_file', 'feedback_files', 1); $dummy = array( 'contextid' => $context->id, 'component' => 'user', 'filearea' => 'draft', 'itemid' => $draftitemid, 'filepath' => '/', 'filename' => 'feedback1.txt' ); $file = $fs->create_file_from_string($dummy, 'This is the first feedback file'); // Create formdata. $data = new \stdClass(); $data->{'files_' . $student->id . '_filemanager'} = $draftitemid; $grade = $assign->get_user_grade($student->id, true); // This is the first time that we are submitting feedback, so it is modified. $plugin = $assign->get_feedback_plugin_by_type('file'); $this->assertTrue($plugin->is_feedback_modified($grade, $data)); // Save the feedback. $plugin->save($grade, $data); // Try again with the same data. $draftitemid = file_get_unused_draft_itemid(); file_prepare_draft_area($draftitemid, $context->id, 'assignfeedback_file', 'feedback_files', 1); $dummy['itemid'] = $draftitemid; $file = $fs->create_file_from_string($dummy, 'This is the first feedback file'); // Create formdata. $data = new \stdClass(); $data->{'files_' . $student->id . '_filemanager'} = $draftitemid; $this->assertFalse($plugin->is_feedback_modified($grade, $data)); // Same name for the file but different content. $draftitemid = file_get_unused_draft_itemid(); file_prepare_draft_area($draftitemid, $context->id, 'assignfeedback_file', 'feedback_files', 1); $dummy['itemid'] = $draftitemid; $file = $fs->create_file_from_string($dummy, 'This is different feedback'); // Create formdata. $data = new \stdClass(); $data->{'files_' . $student->id . '_filemanager'} = $draftitemid; $this->assertTrue($plugin->is_feedback_modified($grade, $data)); $plugin->save($grade, $data); // Add another file. $draftitemid = file_get_unused_draft_itemid(); file_prepare_draft_area($draftitemid, $context->id, 'assignfeedback_file', 'feedback_files', 1); $dummy['itemid'] = $draftitemid; $file = $fs->create_file_from_string($dummy, 'This is different feedback'); $dummy['filename'] = 'feedback2.txt'; $file = $fs->create_file_from_string($dummy, 'A second feedback file'); // Create formdata. $data = new \stdClass(); $data->{'files_' . $student->id . '_filemanager'} = $draftitemid; $this->assertTrue($plugin->is_feedback_modified($grade, $data)); $plugin->save($grade, $data); // Deleting a file. $draftitemid = file_get_unused_draft_itemid(); file_prepare_draft_area($draftitemid, $context->id, 'assignfeedback_file', 'feedback_files', 1); $dummy['itemid'] = $draftitemid; $file = $fs->create_file_from_string($dummy, 'This is different feedback'); // Create formdata. $data = new \stdClass(); $data->{'files_' . $student->id . '_filemanager'} = $draftitemid; $this->assertTrue($plugin->is_feedback_modified($grade, $data)); $plugin->save($grade, $data); // The file was moved to a folder. $draftitemid = file_get_unused_draft_itemid(); file_prepare_draft_area($draftitemid, $context->id, 'assignfeedback_file', 'feedback_files', 1); $dummy['itemid'] = $draftitemid; $dummy['filepath'] = '/testdir/'; $file = $fs->create_file_from_string($dummy, 'This is different feedback'); // Create formdata. $data = new \stdClass(); $data->{'files_' . $student->id . '_filemanager'} = $draftitemid; $this->assertTrue($plugin->is_feedback_modified($grade, $data)); $plugin->save($grade, $data); // No modification to the file in the folder. $draftitemid = file_get_unused_draft_itemid(); file_prepare_draft_area($draftitemid, $context->id, 'assignfeedback_file', 'feedback_files', 1); $dummy['itemid'] = $draftitemid; $dummy['filepath'] = '/testdir/'; $file = $fs->create_file_from_string($dummy, 'This is different feedback'); // Create formdata. $data = new \stdClass(); $data->{'files_' . $student->id . '_filemanager'} = $draftitemid; $this->assertFalse($plugin->is_feedback_modified($grade, $data)); } } tests/behat/feedback_file.feature 0000644 00000006642 15152217772 0013112 0 ustar 00 @mod @mod_assign @assignfeedback @assignfeedback_file @_file_upload Feature: In an assignment, teacher can submit feedback files during grading In order to provide a feedback file As a teacher I need to submit a feedback file. Background: Given the following "courses" exist: | fullname | shortname | category | groupmode | | Course 1 | C1 | 0 | 1 | And the following "users" exist: | username | firstname | lastname | email | | teacher1 | Teacher | 1 | teacher1@example.com | | student1 | Student | 1 | student1@example.com | | student2 | Student | 2 | student2@example.com | And the following "course enrolments" exist: | user | course | role | | teacher1 | C1 | editingteacher | | student1 | C1 | student | | student2 | C1 | student | And the following "groups" exist: | name | course | idnumber | | G1 | C1 | G1 | And the following "group members" exist: | user | group | | student1 | G1 | | student2 | G1 | And the following "activity" exists: | activity | assign | | course | C1 | | name | Test assignment name | | assignsubmission_file_enabled | 1 | | assignsubmission_file_maxfiles | 1 | | assignsubmission_file_maxsizebytes | 1024 | | assignfeedback_comments_enabled | 1 | | assignfeedback_file_enabled | 1 | | maxfilessubmission | 2 | | teamsubmission | 1 | | submissiondrafts | 0 | And the following "mod_assign > submission" exists: | assign | Test assignment name | | user | student1 | | file | mod/assign/feedback/file/tests/fixtures/submission.txt | And I am on the "Test assignment name" Activity page logged in as teacher1 And I click on "Grade" "link" in the ".tertiary-navigation" "css_element" And I upload "mod/assign/feedback/file/tests/fixtures/feedback.txt" file to "Feedback files" filemanager @javascript Scenario: A teacher can provide a feedback file when grading an assignment. Given I set the field "applytoall" to "0" And I press "Save changes" And I click on "Course 1" "link" in the "[data-region=assignment-info]" "css_element" And I log out And I am on the "Test assignment name" Activity page logged in as student1 And I should see "feedback.txt" And I log out And I am on the "Test assignment name" Activity page logged in as student2 Then I should not see "feedback.txt" @javascript Scenario: A teacher can provide a feedback file when grading an assignment and all students in the group will receive the file. Given I press "Save changes" And I click on "Course 1" "link" in the "[data-region=assignment-info]" "css_element" And I log out And I am on the "Test assignment name" Activity page logged in as student1 And I should see "feedback.txt" And I log out When I am on the "Test assignment name" Activity page logged in as student2 Then I should see "feedback.txt" backup/moodle2/backup_assignfeedback_file_subplugin.class.php 0000644 00000004375 15152217772 0020556 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * This file contains the backup code for the feedback_file plugin. * * @package assignfeedback_file * @copyright 2012 NetSpot {@link http://www.netspot.com.au} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); /** * Provides the information to backup feedback files. * * This just adds its filearea to the annotations and records the number of files. * * @package assignfeedback_file * @copyright 2012 NetSpot {@link http://www.netspot.com.au} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class backup_assignfeedback_file_subplugin extends backup_subplugin { /** * Returns the subplugin information to attach to feedback element * @return backup_subplugin_element */ protected function define_grade_subplugin_structure() { // Create XML elements. $subplugin = $this->get_subplugin_element(); $subpluginwrapper = new backup_nested_element($this->get_recommended_name()); $subpluginelement = new backup_nested_element('feedback_file', null, array('numfiles', 'grade')); // Connect XML elements into the tree. $subplugin->add_child($subpluginwrapper); $subpluginwrapper->add_child($subpluginelement); // Set source to populate the data. $subpluginelement->set_source_table('assignfeedback_file', array('grade' => backup::VAR_PARENTID)); // The parent is the grade. $subpluginelement->annotate_files('assignfeedback_file', 'feedback_files', 'grade'); return $subplugin; } } backup/moodle2/restore_assignfeedback_file_subplugin.class.php 0000644 00000004636 15152217772 0020774 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * This file contains the restore code for the feedback_file plugin. * * @package assignfeedback_file * @copyright 2012 NetSpot {@link http://www.netspot.com.au} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); /** * Restore subplugin class. * * Provides the necessary information needed * to restore one assign_feedback subplugin. * * @package assignfeedback_file * @copyright 2012 NetSpot {@link http://www.netspot.com.au} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class restore_assignfeedback_file_subplugin extends restore_subplugin { /** * Returns the paths to be handled by the subplugin at assignment level * @return array */ protected function define_grade_subplugin_structure() { $paths = array(); $elename = $this->get_namefor('grade'); // We used get_recommended_name() so this works. $elepath = $this->get_pathfor('/feedback_file'); $paths[] = new restore_path_element($elename, $elepath); return $paths; } /** * Processes one feedback_file element * @param mixed $data */ public function process_assignfeedback_file_grade($data) { global $DB; $data = (object)$data; $data->assignment = $this->get_new_parentid('assign'); $oldgradeid = $data->grade; // The mapping is set in the restore for the core assign activity // when a grade node is processed. $data->grade = $this->get_mappingid('grade', $data->grade); $DB->insert_record('assignfeedback_file', $data); $this->add_related_files('assignfeedback_file', 'feedback_files', 'grade', null, $oldgradeid); } } settings.php 0000644 00000002202 15152217772 0007122 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * This file defines the admin settings for this plugin * * @package assignfeedback_file * @copyright 2012 NetSpot {@link http://www.netspot.com.au} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ $settings->add(new admin_setting_configcheckbox('assignfeedback_file/default', new lang_string('default', 'assignfeedback_file'), new lang_string('default_help', 'assignfeedback_file'), 0)); batchuploadfilesform.php 0000644 00000007172 15152217772 0011472 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * This file contains the forms to create and edit an instance of this module * * @package assignfeedback_file * @copyright 2012 NetSpot {@link http://www.netspot.com.au} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); require_once($CFG->libdir.'/formslib.php'); require_once($CFG->dirroot . '/mod/assign/feedback/file/locallib.php'); /** * Assignment grading options form * * @package assignfeedback_file * @copyright 2012 NetSpot {@link http://www.netspot.com.au} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class assignfeedback_file_batch_upload_files_form extends moodleform { /** * Define this form - called by the parent constructor */ public function definition() { global $COURSE, $USER; $mform = $this->_form; $params = $this->_customdata; $mform->addElement('header', 'batchuploadfilesforusers', get_string('batchuploadfilesforusers', 'assignfeedback_file', count($params['users']))); $mform->addElement('static', 'userslist', get_string('selectedusers', 'assignfeedback_file'), $params['usershtml']); $data = new stdClass(); $fileoptions = array('subdirs'=>1, 'maxbytes'=>$COURSE->maxbytes, 'accepted_types'=>'*', 'return_types'=>FILE_INTERNAL); $data = file_prepare_standard_filemanager($data, 'files', $fileoptions, $params['context'], 'assignfeedback_file', ASSIGNFEEDBACK_FILE_BATCH_FILEAREA, $USER->id); $mform->addElement('filemanager', 'files_filemanager', '', null, $fileoptions); $this->set_data($data); $mform->addElement('hidden', 'id', $params['cm']); $mform->setType('id', PARAM_INT); $mform->addElement('hidden', 'operation', 'plugingradingbatchoperation_file_uploadfiles'); $mform->setType('operation', PARAM_ALPHAEXT); $mform->addElement('hidden', 'action', 'viewpluginpage'); $mform->setType('action', PARAM_ALPHA); $mform->addElement('hidden', 'pluginaction', 'uploadfiles'); $mform->setType('pluginaction', PARAM_ALPHA); $mform->addElement('hidden', 'plugin', 'file'); $mform->setType('plugin', PARAM_PLUGIN); $mform->addElement('hidden', 'pluginsubtype', 'assignfeedback'); $mform->setType('pluginsubtype', PARAM_PLUGIN); $mform->addElement('hidden', 'selectedusers', implode(',', $params['users'])); $mform->setType('selectedusers', PARAM_SEQUENCE); $this->add_action_buttons(true, get_string('uploadfiles', 'assignfeedback_file')); } } importziplib.php 0000644 00000033073 15152217772 0010020 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * This file contains the definition for the library class for file feedback plugin * * * @package assignfeedback_file * @copyright 2012 NetSpot {@link http://www.netspot.com.au} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); use mod_assign\output\assign_header; /** * library class for importing feedback files from a zip * * @package assignfeedback_file * @copyright 2012 NetSpot {@link http://www.netspot.com.au} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class assignfeedback_file_zip_importer { /** * Is this filename valid (contains a unique participant ID) for import? * * @param assign $assignment - The assignment instance * @param stored_file $fileinfo - The fileinfo * @param array $participants - A list of valid participants for this module indexed by unique_id * @param stdClass $user - Set to the user that matches by participant id * @param assign_plugin $plugin - Set to the plugin that exported the file * @param string $filename - Set to truncated filename (prefix stripped) * @return true If the participant Id can be extracted and this is a valid user */ public function is_valid_filename_for_import($assignment, $fileinfo, $participants, & $user, & $plugin, & $filename) { if ($fileinfo->is_directory()) { return false; } // Ignore hidden files. if (strpos($fileinfo->get_filename(), '.') === 0) { return false; } // Ignore hidden files. if (strpos($fileinfo->get_filename(), '~') === 0) { return false; } // Break the full path-name into path parts. $pathparts = explode('/', $fileinfo->get_filepath() . $fileinfo->get_filename()); while (!empty($pathparts)) { // Get the next path part and break it up by underscores. $pathpart = array_shift($pathparts); $info = explode('_', $pathpart, 5); // Expected format for the directory names in $pathpart is fullname_userid_plugintype_pluginname (as created by zip // export in Moodle >= 4.1) resp. fullname_userid_plugintype_pluginname_ (as created by earlier versions). We ensure // compatibility with both ways here. if (count($info) < 4) { continue; } // Check the participant id. $participantid = $info[1]; if (!is_numeric($participantid)) { continue; } // Convert to int. $participantid += 0; if (empty($participants[$participantid])) { continue; } // Set user, which is by reference, so is used by the calling script. $user = $participants[$participantid]; // Set the plugin. This by reference, and is used by the calling script. $plugin = $assignment->get_plugin_by_type($info[2], $info[3]); if (!$plugin) { continue; } // To get clean path names, we need to have at least an empty entry for $info[4]. if (count($info) == 4) { $info[4] = ''; } // Take any remaining text in this part and put it back in the path parts array. array_unshift($pathparts, $info[4]); // Combine the remaining parts and set it as the filename. // Note that filename is a 'by reference' variable, so we need to set it before returning. $filename = implode('/', $pathparts); return true; } return false; } /** * Does this file exist in any of the current files supported by this plugin for this user? * * @param assign $assignment - The assignment instance * @param stdClass $user The user matching this uploaded file * @param assign_plugin $plugin The matching plugin from the filename * @param string $filename The parsed filename from the zip * @param stored_file $fileinfo The info about the extracted file from the zip * @return bool - True if the file has been modified or is new */ public function is_file_modified($assignment, $user, $plugin, $filename, $fileinfo) { $sg = null; if ($plugin->get_subtype() == 'assignsubmission') { $sg = $assignment->get_user_submission($user->id, false); } else if ($plugin->get_subtype() == 'assignfeedback') { $sg = $assignment->get_user_grade($user->id, false); } else { return false; } if (!$sg) { return true; } foreach ($plugin->get_files($sg, $user) as $pluginfilename => $file) { if ($pluginfilename == $filename) { // Extract the file and compare hashes. $contenthash = ''; if (is_array($file)) { $content = reset($file); $contenthash = file_storage::hash_from_string($content); } else { $contenthash = $file->get_contenthash(); } if ($contenthash != $fileinfo->get_contenthash()) { return true; } else { return false; } } } return true; } /** * Delete all temp files used when importing a zip * * @param int $contextid - The context id of this assignment instance * @return bool true if all files were deleted */ public function delete_import_files($contextid) { global $USER; $fs = get_file_storage(); return $fs->delete_area_files($contextid, 'assignfeedback_file', ASSIGNFEEDBACK_FILE_IMPORT_FILEAREA, $USER->id); } /** * Extract the uploaded zip to a temporary import area for this user * * @param stored_file $zipfile The uploaded file * @param int $contextid The context for this assignment * @return bool - True if the files were unpacked */ public function extract_files_from_zip($zipfile, $contextid) { global $USER; $feedbackfilesupdated = 0; $feedbackfilesadded = 0; $userswithnewfeedback = array(); // Unzipping a large zip file is memory intensive. raise_memory_limit(MEMORY_EXTRA); $packer = get_file_packer('application/zip'); core_php_time_limit::raise(ASSIGNFEEDBACK_FILE_MAXFILEUNZIPTIME); return $packer->extract_to_storage($zipfile, $contextid, 'assignfeedback_file', ASSIGNFEEDBACK_FILE_IMPORT_FILEAREA, $USER->id, 'import'); } /** * Get the list of files extracted from the uploaded zip * * @param int $contextid * @return array of stored_files */ public function get_import_files($contextid) { global $USER; $fs = get_file_storage(); $files = $fs->get_directory_files($contextid, 'assignfeedback_file', ASSIGNFEEDBACK_FILE_IMPORT_FILEAREA, $USER->id, '/import/', true); // Get files recursive (all levels). $keys = array_keys($files); return $files; } /** * Process an uploaded zip file * * @param assign $assignment - The assignment instance * @param assign_feedback_file $fileplugin - The file feedback plugin * @return string - The html response */ public function import_zip_files($assignment, $fileplugin) { global $CFG, $PAGE, $DB; core_php_time_limit::raise(ASSIGNFEEDBACK_FILE_MAXFILEUNZIPTIME); $packer = get_file_packer('application/zip'); $feedbackfilesupdated = 0; $feedbackfilesadded = 0; $userswithnewfeedback = array(); $contextid = $assignment->get_context()->id; $fs = get_file_storage(); $files = $this->get_import_files($contextid); $currentgroup = groups_get_activity_group($assignment->get_course_module(), true); $allusers = $assignment->list_participants($currentgroup, false); $participants = array(); foreach ($allusers as $user) { $participants[$assignment->get_uniqueid_for_user($user->id)] = $user; } foreach ($files as $unzippedfile) { // Set the timeout for unzipping each file. $user = null; $plugin = null; $filename = ''; if ($this->is_valid_filename_for_import($assignment, $unzippedfile, $participants, $user, $plugin, $filename)) { if ($this->is_file_modified($assignment, $user, $plugin, $filename, $unzippedfile)) { $grade = $assignment->get_user_grade($user->id, true); // In 3.1 the default download structure of the submission files changed so that each student had their own // separate folder, the files were not renamed and the folder structure was kept. It is possible that // a user downloaded the submission files in 3.0 (or earlier) and edited the zip to add feedback or // changed the behavior back to the previous format, the following code means that we will still support the // old file structure. For more information please see - MDL-52489 / MDL-56022. $path = pathinfo($filename); if ($path['dirname'] == '.') { // Student submissions are not in separate folders. $basename = $filename; $dirname = "/"; $dirnamewslash = "/"; } else { $basename = $path['basename']; $dirname = $path['dirname']; $dirnamewslash = $dirname . "/"; } if ($oldfile = $fs->get_file($contextid, 'assignfeedback_file', ASSIGNFEEDBACK_FILE_FILEAREA, $grade->id, $dirname, $basename)) { // Update existing feedback file. $oldfile->replace_file_with($unzippedfile); $feedbackfilesupdated++; } else { // Create a new feedback file. $newfilerecord = new stdClass(); $newfilerecord->contextid = $contextid; $newfilerecord->component = 'assignfeedback_file'; $newfilerecord->filearea = ASSIGNFEEDBACK_FILE_FILEAREA; $newfilerecord->filename = $basename; $newfilerecord->filepath = $dirnamewslash; $newfilerecord->itemid = $grade->id; $fs->create_file_from_storedfile($newfilerecord, $unzippedfile); $feedbackfilesadded++; } $userswithnewfeedback[$user->id] = 1; // Update the number of feedback files for this user. $fileplugin->update_file_count($grade); // Update the last modified time on the grade which will trigger student notifications. $assignment->notify_grade_modified($grade); } } } require_once($CFG->dirroot . '/mod/assign/feedback/file/renderable.php'); $importsummary = new assignfeedback_file_import_summary($assignment->get_course_module()->id, count($userswithnewfeedback), $feedbackfilesadded, $feedbackfilesupdated); $assignrenderer = $assignment->get_renderer(); $renderer = $PAGE->get_renderer('assignfeedback_file'); $o = ''; $o .= $assignrenderer->render(new assign_header($assignment->get_instance(), $assignment->get_context(), false, $assignment->get_course_module()->id, get_string('uploadzipsummary', 'assignfeedback_file'))); $o .= $renderer->render($importsummary); $o .= $assignrenderer->render_footer(); return $o; } } lib.php 0000644 00000005371 15152217772 0006042 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * This file contains the moodle hooks for the feedback file plugin * * @package assignfeedback_file * @copyright 2012 NetSpot {@link http://www.netspot.com.au} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); /** * Serves assignment feedback and other files. * * @param mixed $course course or id of the course * @param mixed $cm course module or id of the course module * @param context $context * @param string $filearea * @param array $args * @param bool $forcedownload * @param array $options - List of options affecting file serving. * @return bool false if file not found, does not return if found - just send the file */ function assignfeedback_file_pluginfile($course, $cm, context $context, $filearea, $args, $forcedownload, array $options=array()) { global $USER, $DB, $CFG, $PAGE; if ($context->contextlevel != CONTEXT_MODULE) { return false; } require_once($CFG->dirroot . '/mod/assign/locallib.php'); require_login($course, false, $cm); $itemid = (int)array_shift($args); $record = $DB->get_record('assign_grades', array('id' => $itemid), 'userid,assignment', MUST_EXIST); $userid = $record->userid; $assign = new assign($context, $cm, $course); if ($assign->get_instance()->id != $record->assignment) { return false; } // Rely on mod_assign checking permissions. if (!$assign->can_view_submission($userid)) { return false; } $relativepath = implode('/', $args); $fullpath = "/{$context->id}/assignfeedback_file/$filearea/$itemid/$relativepath"; $fs = get_file_storage(); if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) { return false; } // Download MUST be forced - security! send_stored_file($file, 0, 0, true, $options); } version.php 0000644 00000002106 15152217772 0006752 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * This file contains the version information for the comments feedback plugin * * @package assignfeedback_file * @copyright 2012 NetSpot {@link http://www.netspot.com.au} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); $plugin->version = 2022112800; $plugin->requires = 2022111800; $plugin->component = 'assignfeedback_file'; classes/privacy/provider.php 0000644 00000015205 15152217772 0012235 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Privacy class for requesting user data. * * @package assignfeedback_file * @copyright 2018 Adrian Greeve <adrian@moodle.com> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace assignfeedback_file\privacy; defined('MOODLE_INTERNAL') || die(); require_once($CFG->dirroot . '/mod/assign/locallib.php'); use \core_privacy\local\metadata\collection; use \core_privacy\local\request\contextlist; use \mod_assign\privacy\assign_plugin_request_data; use mod_assign\privacy\useridlist; /** * Privacy class for requesting user data. * * @package assignfeedback_file * @copyright 2018 Adrian Greeve <adrian@moodle.com> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class provider implements \core_privacy\local\metadata\provider, \mod_assign\privacy\assignfeedback_provider, \mod_assign\privacy\assignfeedback_user_provider { /** * Return meta data about this plugin. * * @param collection $collection A list of information to add to. * @return collection Return the collection after adding to it. */ public static function get_metadata(collection $collection) : collection { $collection->link_subsystem('core_files', 'privacy:metadata:filepurpose'); return $collection; } /** * No need to fill in this method as all information can be acquired from the assign_grades table in the mod assign * provider. * * @param int $userid The user ID. * @param contextlist $contextlist The context list. */ public static function get_context_for_userid_within_feedback(int $userid, contextlist $contextlist) { // This uses the assign_grade table. } /** * This also does not need to be filled in as this is already collected in the mod assign provider. * * @param useridlist $useridlist A list of user IDs */ public static function get_student_user_ids(useridlist $useridlist) { // Not required. } /** * If you have tables that contain userids and you can generate entries in your tables without creating an * entry in the assign_grades table then please fill in this method. * * @param \core_privacy\local\request\userlist $userlist The userlist object */ public static function get_userids_from_context(\core_privacy\local\request\userlist $userlist) { // Not required. } /** * Export all user data for this plugin. * * @param assign_plugin_request_data $exportdata Data used to determine which context and user to export and other useful * information to help with exporting. */ public static function export_feedback_user_data(assign_plugin_request_data $exportdata) { $currentpath = $exportdata->get_subcontext(); $currentpath[] = get_string('privacy:path', 'assignfeedback_file'); $assign = $exportdata->get_assign(); $plugin = $assign->get_plugin_by_type('assignfeedback', 'file'); $gradeid = $exportdata->get_pluginobject()->id; $filefeedback = $plugin->get_file_feedback($gradeid); if ($filefeedback) { $fileareas = $plugin->get_file_areas(); foreach ($fileareas as $filearea => $notused) { \core_privacy\local\request\writer::with_context($exportdata->get_context()) ->export_area_files($currentpath, 'assignfeedback_file', $filearea, $gradeid); } } } /** * Any call to this method should delete all user data for the context defined in the deletion_criteria. * * @param assign_plugin_request_data $requestdata Data useful for deleting user data from this sub-plugin. */ public static function delete_feedback_for_context(assign_plugin_request_data $requestdata) { $assign = $requestdata->get_assign(); $plugin = $assign->get_plugin_by_type('assignfeedback', 'file'); $fileareas = $plugin->get_file_areas(); $fs = get_file_storage(); foreach ($fileareas as $filearea => $notused) { // Delete feedback files. $fs->delete_area_files($requestdata->get_context()->id, 'assignfeedback_file', $filearea); } $plugin->delete_instance(); } /** * Calling this function should delete all user data associated with this grade. * * @param assign_plugin_request_data $requestdata Data useful for deleting user data. */ public static function delete_feedback_for_grade(assign_plugin_request_data $requestdata) { $requestdata->set_userids([$requestdata->get_user()->id]); $requestdata->populate_submissions_and_grades(); self::delete_feedback_for_grades($requestdata); } /** * Deletes all feedback for the grade ids / userids provided in a context. * assign_plugin_request_data contains: * - context * - assign object * - grade ids (pluginids) * - user ids * @param assign_plugin_request_data $deletedata A class that contains the relevant information required for deletion. */ public static function delete_feedback_for_grades(assign_plugin_request_data $deletedata) { global $DB; if (empty($deletedata->get_gradeids())) { return; } $assign = $deletedata->get_assign(); $plugin = $assign->get_plugin_by_type('assignfeedback', 'file'); $fileareas = $plugin->get_file_areas(); $fs = get_file_storage(); list($sql, $params) = $DB->get_in_or_equal($deletedata->get_gradeids(), SQL_PARAMS_NAMED); $params['assignment'] = $deletedata->get_assignid(); foreach ($fileareas as $filearea => $notused) { // Delete feedback files. $fs->delete_area_files_select($deletedata->get_context()->id, 'assignfeedback_file', $filearea, $sql, $params); } // Delete table entries. $DB->delete_records_select('assignfeedback_file', "assignment = :assignment AND grade $sql", $params); } } lang/en/assignfeedback_file.php 0000644 00000006526 15152217772 0012552 0 ustar 00 <?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Strings for component 'feedback_file', language 'en' * * @package assignfeedback_file * @copyright 2012 NetSpot {@link http://www.netspot.com.au} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ $string['batchoperationconfirmuploadfiles'] = 'Upload one or more feedback files for all selected users?'; $string['batchuploadfiles'] = 'Upload feedback files for multiple users'; $string['batchuploadfilesforusers'] = 'Send feedback files to {$a} selected user(s).'; $string['configmaxbytes'] = 'Maximum file size'; $string['confirmuploadzip'] = 'Confirm zip upload'; $string['countfiles'] = '{$a} files'; $string['default'] = 'Enabled by default'; $string['default_help'] = 'If set, this feedback method will be enabled by default for all new assignments.'; $string['enabled'] = 'File feedback'; $string['enabled_help'] = 'If enabled, the teacher will be able to upload files with feedback when marking assignment submissions. These files may be, but are not limited to, marked-up student submissions, documents with comments or spoken audio feedback.'; $string['feedbackzip'] = 'Zip file with feedback files'; $string['feedbackfileadded'] = 'New feedback file "{$a->filename}" for student "{$a->student}"'; $string['feedbackfileupdated'] = 'Modified feedback file "{$a->filename}" for student "{$a->student}"'; $string['feedbackzip_help'] = 'A zip file containing a list of feedback files for one or more students. Feedback files will be assigned to students based on the user ID which should be the second part of each file name immediately after the user\'s full name. This naming convention is used when downloading submissions so you can download all submissions, add comments to a few files, then re-zip and upload all of the files. Files with no changes will be ignored.'; $string['file'] = 'Feedback files'; $string['privacy:metadata:filepurpose'] = 'Feedback files from the teacher for the student.'; $string['privacy:path'] = 'Feedback files'; $string['filesupdated'] = 'Feedback files updated: {$a}'; $string['filesadded'] = 'Feedback files added: {$a}'; $string['importfeedbackfiles'] = 'Import feedback file(s)'; $string['maxbytes'] = 'Maximum file size'; $string['maxfiles'] = 'Maximum number of uploaded files'; $string['maximumsize'] = 'Maximum file size'; $string['moreusers'] = '{$a} more...'; $string['nochanges'] = 'No changes'; $string['pluginname'] = 'File feedback'; $string['uploadfiles'] = 'Send feedback files'; $string['uploadzip'] = 'Upload multiple feedback files in a zip'; $string['uploadzipsummary'] = 'Feedback files imported from a zip'; $string['userswithnewfeedback'] = 'Users with updated feedback: {$a}'; $string['selectedusers'] = 'Selected users';
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | ���֧ߧ֧�ѧ�ڧ� ����ѧߧڧ��: 0 |
proxy
|
phpinfo
|
���ѧ����ۧܧ�