first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
@@ -0,0 +1,55 @@
<?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/>.
/**
* The assignsubmission_comments comment created event.
*
* @package assignsubmission_comments
* @copyright 2013 Rajesh Taneja <rajesh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace assignsubmission_comments\event;
defined('MOODLE_INTERNAL') || die();
/**
* The assignsubmission_comments comment created event class.
*
* @package assignsubmission_comments
* @since Moodle 2.7
* @copyright 2013 Rajesh Taneja <rajesh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class comment_created extends \core\event\comment_created {
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/assign/view.php', array('id' => $this->contextinstanceid));
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' added the comment with id '$this->objectid' to the submission " .
"with id '{$this->other['itemid']}' for the assignment with course module id '$this->contextinstanceid'.";
}
}
@@ -0,0 +1,55 @@
<?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/>.
/**
* The assignsubmission_comments comment deleted event.
*
* @package assignsubmission_comments
* @copyright 2013 Rajesh Taneja <rajesh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace assignsubmission_comments\event;
defined('MOODLE_INTERNAL') || die();
/**
* The assignsubmission_comments comment deleted event.
*
* @package assignsubmission_comments
* @since Moodle 2.7
* @copyright 2013 Rajesh Taneja <rajesh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class comment_deleted extends \core\event\comment_deleted {
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/assign/view.php', array('id' => $this->contextinstanceid));
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' deleted the comment with id '$this->objectid' from the submission " .
"with id '{$this->other['itemid']}' for the assignment with course module id '$this->contextinstanceid'.";
}
}
@@ -0,0 +1,164 @@
<?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 assignsubmission_comments
* @copyright 2018 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace assignsubmission_comments\privacy;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/mod/assign/locallib.php');
use \core_privacy\local\metadata\collection;
use \core_privacy\local\metadata\provider as metadataprovider;
use \core_comment\privacy\provider as comments_provider;
use \core_privacy\local\request\contextlist;
use \mod_assign\privacy\assign_plugin_request_data;
/**
* Privacy class for requesting user data.
*
* @package assignsubmission_comments
* @copyright 2018 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements metadataprovider,
\mod_assign\privacy\assignsubmission_provider,
\mod_assign\privacy\assignsubmission_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_comment', 'privacy:metadata:commentpurpose');
return $collection;
}
/**
* It is possible to make a comment as a teacher without creating an entry in the submission table, so this is required
* to find those entries.
*
* @param int $userid The user ID that we are finding contexts for.
* @param contextlist $contextlist A context list to add sql and params to for contexts.
*/
public static function get_context_for_userid_within_submission(int $userid, contextlist $contextlist) {
$sql = "SELECT contextid
FROM {comments}
WHERE component = :component
AND commentarea = :commentarea
AND userid = :userid";
$params = ['userid' => $userid, 'component' => 'assignsubmission_comments', 'commentarea' => 'submission_comments'];
$contextlist->add_from_sql($sql, $params);
}
/**
* Due to the fact that we can't rely on the queries in the mod_assign provider we have to add some additional sql.
*
* @param \mod_assign\privacy\useridlist $useridlist An object for obtaining user IDs of students.
*/
public static function get_student_user_ids(\mod_assign\privacy\useridlist $useridlist) {
$params = ['assignid' => $useridlist->get_assignid(), 'commentuserid' => $useridlist->get_teacherid(),
'commentuserid2' => $useridlist->get_teacherid()];
$sql = "SELECT DISTINCT c.userid AS id
FROM {comments} c
JOIN (SELECT c.itemid
FROM {comments} c
JOIN {assign_submission} s ON s.id = c.itemid AND s.assignment = :assignid
WHERE c.userid = :commentuserid) aa ON aa.itemid = c.itemid
WHERE c.userid NOT IN (:commentuserid2)";
$useridlist->add_from_sql($sql, $params);
}
/**
* If you have tables that contain userids and you can generate entries in your tables without creating an
* entry in the assign_submission 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) {
$context = $userlist->get_context();
if ($context->contextlevel != CONTEXT_MODULE) {
return;
}
comments_provider::get_users_in_context_from_sql($userlist, 'c', 'assignsubmission_comments', 'submission_comments',
$context->id);
}
/**
* 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_submission_user_data(assign_plugin_request_data $exportdata) {
$component = 'assignsubmission_comments';
$commentarea = 'submission_comments';
$userid = ($exportdata->get_user() != null);
$submission = $exportdata->get_pluginobject();
// For the moment we are only showing the comments made by this user.
comments_provider::export_comments($exportdata->get_context(), $component, $commentarea, $submission->id,
$exportdata->get_subcontext(), $userid);
}
/**
* Delete all the comments made for this context.
*
* @param assign_plugin_request_data $requestdata Data to fulfill the deletion request.
*/
public static function delete_submission_for_context(assign_plugin_request_data $requestdata) {
comments_provider::delete_comments_for_all_users($requestdata->get_context(), 'assignsubmission_comments',
'submission_comments');
}
/**
* A call to this method should delete user data (where practical) using the userid and submission.
*
* @param assign_plugin_request_data $exportdata Details about the user and context to focus the deletion.
*/
public static function delete_submission_for_userid(assign_plugin_request_data $exportdata) {
// Create an approved context list to delete the comments.
$contextlist = new \core_privacy\local\request\approved_contextlist($exportdata->get_user(), 'assignsubmission_comments',
[$exportdata->get_context()->id]);
comments_provider::delete_comments_for_user($contextlist, 'assignsubmission_comments', 'submission_comments');
}
/**
* Deletes all submissions for the submission ids / userids provided in a context.
* assign_plugin_request_data contains:
* - context
* - assign object
* - submission ids (pluginids)
* - user ids
* @param assign_plugin_request_data $deletedata A class that contains the relevant information required for deletion.
*/
public static function delete_submissions(assign_plugin_request_data $deletedata) {
$userlist = new \core_privacy\local\request\approved_userlist($deletedata->get_context(), 'assignsubmission_comments',
$deletedata->get_userids());
comments_provider::delete_comments_for_users($userlist, 'assignsubmission_comments', 'submission_comments');
}
}
@@ -0,0 +1,30 @@
<?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 assignsubmission_comments
* @copyright 2012 NetSpot {@link http://www.netspot.com.au}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$capabilities = array(
);
@@ -0,0 +1,45 @@
<?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 submission_comments module.
*
* @package assignsubmission_comments
* @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();
/**
* Code run after the module database tables have been created.
* Moves the comments plugin to the bottom
* @return bool
*/
function xmldb_assignsubmission_comments_install() {
global $CFG;
require_once($CFG->dirroot . '/mod/assign/adminlib.php');
// Set the correct initial order for the plugins.
$pluginmanager = new assign_plugin_manager('assignsubmission');
$pluginmanager->move_plugin('comments', 'down');
$pluginmanager->move_plugin('comments', 'down');
return true;
}
@@ -0,0 +1,44 @@
<?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 install
*
* @package assignsubmission_comments
* @copyright 2012 NetSpot {@link http://www.netspot.com.au}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Stub for upgrade code
* @param int $oldversion
* @return bool
*/
function xmldb_assignsubmission_comments_upgrade($oldversion) {
// Automatically generated Moodle v4.1.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.2.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.3.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.4.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
@@ -0,0 +1,32 @@
<?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 'submission_comments', language 'en'
*
* @package assignsubmission_comments
* @copyright 2012 NetSpot {@link http://www.netspot.com.au}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['blindmarkingname'] = 'Participant {$a}';
$string['blindmarkingviewfullname'] = 'Participant {$a->participantnumber} ({$a->participantfullname})';
$string['privacy:metadata:commentpurpose'] = 'Comments between the student and teacher about a submission.';
$string['default'] = 'Enabled by default';
$string['default_help'] = 'If set, this submission method will be enabled by default for all new assignments.';
$string['enabled'] = 'Submission comments';
$string['enabled_help'] = 'If enabled, students can leave comments on their own submission. For example, this can be used for students to specify which is the master file when submitting inter-linked files.';
$string['pluginname'] = 'Submission comments';
+204
View File
@@ -0,0 +1,204 @@
<?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 submission comments plugin
*
* @package assignsubmission_comments
* @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();
/**
*
* Callback method for data validation---- required method for AJAXmoodle based comment API
*
* @param stdClass $options
* @return bool
*/
function assignsubmission_comments_comment_validate(stdClass $options) {
global $USER, $CFG, $DB;
if ($options->commentarea != 'submission_comments' &&
$options->commentarea != 'submission_comments_upgrade') {
throw new comment_exception('invalidcommentarea');
}
if (!$submission = $DB->get_record('assign_submission', array('id'=>$options->itemid))) {
throw new comment_exception('invalidcommentitemid');
}
$context = $options->context;
require_once($CFG->dirroot . '/mod/assign/locallib.php');
static $assignment = null;
if (is_null($assignment) || $assignment->get_context() != $context) {
$assignment = new assign($context, null, null);
}
if ($assignment->get_instance()->id != $submission->assignment) {
throw new comment_exception('invalidcontext');
}
return true;
}
/**
* Permission control method for submission plugin ---- required method for AJAXmoodle based comment API
*
* @param stdClass $options
* @return array
*/
function assignsubmission_comments_comment_permissions(stdClass $options) {
global $USER, $CFG, $DB;
if ($options->commentarea != 'submission_comments' &&
$options->commentarea != 'submission_comments_upgrade') {
throw new comment_exception('invalidcommentarea');
}
if (!$submission = $DB->get_record('assign_submission', array('id'=>$options->itemid))) {
throw new comment_exception('invalidcommentitemid');
}
$context = $options->context;
require_once($CFG->dirroot . '/mod/assign/locallib.php');
static $assignment = null;
if (is_null($assignment) || $assignment->get_context() != $context) {
$assignment = new assign($context, null, null);
}
if ($assignment->get_instance()->id != $submission->assignment) {
throw new comment_exception('invalidcontext');
}
if ($assignment->get_instance()->teamsubmission &&
!$assignment->can_view_group_submission($submission->groupid)) {
return array('post' => false, 'view' => false);
}
if (!$assignment->get_instance()->teamsubmission &&
!$assignment->can_view_submission($submission->userid)) {
return array('post' => false, 'view' => false);
}
return array('post' => true, 'view' => true);
}
/**
* Callback called by comment::get_comments() and comment::add(). Gives an opportunity to enforce blind-marking.
*
* @param array $comments
* @param stdClass $options
* @return array
* @throws comment_exception
*/
function assignsubmission_comments_comment_display($comments, $options) {
global $CFG, $DB, $USER;
if ($options->commentarea != 'submission_comments' &&
$options->commentarea != 'submission_comments_upgrade') {
throw new comment_exception('invalidcommentarea');
}
if (!$submission = $DB->get_record('assign_submission', array('id'=>$options->itemid))) {
throw new comment_exception('invalidcommentitemid');
}
$context = $options->context;
$cm = $options->cm;
$course = $options->courseid;
require_once($CFG->dirroot . '/mod/assign/locallib.php');
$assignment = new assign($context, $cm, $course);
if ($assignment->get_instance()->id != $submission->assignment) {
throw new comment_exception('invalidcontext');
}
if ($assignment->is_blind_marking() && !empty($comments)) {
// Blind marking is being used, may need to map unique anonymous ids to the comments.
$usermappings = array();
$guestuser = guest_user();
// Check group users first.
$userinteam = false;
if ($assignment->get_instance()->teamsubmission && has_capability('mod/assign:submit', $context)) {
$assignment->set_course(get_course($course));
$userinteam = $assignment->can_edit_group_submission($submission->groupid);
}
foreach ($comments as $comment) {
if (has_capability('mod/assign:viewblinddetails', $context) && $USER->id != $comment->userid) {
$anonid = $assignment->get_uniqueid_for_user($comment->userid);
// Show participant information and the user's full name to users with the view blind details capability.
$a = new stdClass();
$a->participantnumber = $anonid;
$a->participantfullname = $comment->fullname;
$comment->fullname = get_string('blindmarkingviewfullname', 'assignsubmission_comments', $a);
} else if ($USER->id == $comment->userid || $submission->userid == $USER->id || $userinteam) { // phpcs:ignore
// Do not anonymize the user details for this comment.
} else {
// Anonymize the comments.
if (empty($usermappings[$comment->userid])) {
$anonid = $assignment->get_uniqueid_for_user($comment->userid);
// The blind-marking information for this commenter has not been generated; do so now.
$commenter = new stdClass();
$commenter->firstname = get_string('blindmarkingname', 'assignsubmission_comments', $anonid);
$commenter->lastname = '';
$commenter->firstnamephonetic = '';
$commenter->lastnamephonetic = '';
$commenter->middlename = '';
$commenter->alternatename = '';
$commenter->picture = 0;
$commenter->id = $guestuser->id;
$commenter->email = $guestuser->email;
$commenter->imagealt = $guestuser->imagealt;
// Temporarily store blind-marking information for use in later comments if necessary.
$usermappings[$comment->userid] = new stdClass();
$usermappings[$comment->userid]->fullname = fullname($commenter);
$usermappings[$comment->userid]->avatar = $assignment->get_renderer()->user_picture($commenter,
array('size' => 18, 'link' => false));
}
// Set blind-marking information for this comment.
$comment->fullname = $usermappings[$comment->userid]->fullname;
$comment->avatar = $usermappings[$comment->userid]->avatar;
$comment->profileurl = null;
}
}
}
return $comments;
}
/**
* Callback to force the userid for all comments to be the userid of the submission and NOT the global $USER->id. This
* is required by the upgrade code. Note the comment area is used to identify upgrades.
*
* @param stdClass $comment
* @param stdClass $param
*/
function assignsubmission_comments_comment_add(stdClass $comment, stdClass $param) {
global $DB;
if ($comment->commentarea == 'submission_comments_upgrade') {
$submissionid = $comment->itemid;
$submission = $DB->get_record('assign_submission', array('id' => $submissionid));
$comment->userid = $submission->userid;
$comment->commentarea = 'submission_comments';
}
}
+200
View File
@@ -0,0 +1,200 @@
<?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 online comment submission plugin
*
* @package assignsubmission_comments
* @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();
require_once($CFG->dirroot . '/comment/lib.php');
require_once($CFG->dirroot . '/mod/assign/submissionplugin.php');
/**
* Library class for comment submission plugin extending submission plugin base class
*
* @package assignsubmission_comments
* @copyright 2012 NetSpot {@link http://www.netspot.com.au}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class assign_submission_comments extends assign_submission_plugin {
/**
* Get the name of the online comment submission plugin
* @return string
*/
public function get_name() {
return get_string('pluginname', 'assignsubmission_comments');
}
/**
* Display AJAX based comment in the submission status table
*
* @param stdClass $submission
* @param bool $showviewlink - If the comments are long this is
* set to true so they can be shown in a separate page
* @return string
*/
public function view_summary(stdClass $submission, & $showviewlink) {
// Never show a link to view full submission.
$showviewlink = false;
// Need to used this init() otherwise it does not have the javascript includes.
comment::init();
$options = new stdClass();
$options->area = 'submission_comments';
$options->course = $this->assignment->get_course();
$options->context = $this->assignment->get_context();
$options->itemid = $submission->id;
$options->component = 'assignsubmission_comments';
$options->showcount = true;
$options->displaycancel = true;
$comment = new comment($options);
$o = $this->assignment->get_renderer()->container($comment->output(true), 'commentscontainer');
return $o;
}
/**
* Always return true because the submission comments are not part of the submission form.
*
* @param stdClass $submission
* @return bool
*/
public function is_empty(stdClass $submission) {
return true;
}
/**
* 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' && $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) {
if ($oldassignment->assignmenttype == 'upload') {
// Disable if allow notes was not enabled.
if (!$oldassignment->var2) {
$this->disable();
}
}
return true;
}
/**
* Upgrade the submission from the old assignment to the new one
*
* @param context $oldcontext The context for the old assignment
* @param stdClass $oldassignment The data record for the old assignment
* @param stdClass $oldsubmission The data record for the old submission
* @param stdClass $submission The new submission record
* @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 $submission,
& $log) {
if ($oldsubmission->data1 != '') {
// Need to used this init() otherwise it does not have the javascript includes.
comment::init();
$options = new stdClass();
$options->area = 'submission_comments_upgrade';
$options->course = $this->assignment->get_course();
$options->context = $this->assignment->get_context();
$options->itemid = $submission->id;
$options->component = 'assignsubmission_comments';
$options->showcount = true;
$options->displaycancel = true;
$comment = new comment($options);
$comment->add($oldsubmission->data1);
$comment->set_view_permission(true);
return $comment->output(true);
}
return true;
}
/**
* The submission comments plugin has no submission component so should not be counted
* when determining whether to show the edit submission link.
* @return boolean
*/
public function allow_submissions() {
return false;
}
/**
* Automatically enable or disable this plugin based on "$CFG->commentsenabled"
*
* @return bool
*/
public function is_enabled() {
global $CFG;
return (!empty($CFG->usecomments));
}
/**
* Automatically hide the setting for the submission plugin.
*
* @return bool
*/
public function is_configurable() {
return false;
}
/**
* 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();
}
}
@@ -0,0 +1,132 @@
<?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/>.
/**
* Events tests.
*
* @package assignsubmission_comments
* @category test
* @copyright 2013 Rajesh Taneja <rajesh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace assignsubmission_comments\event;
use mod_assign_test_generator;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/mod/assign/lib.php');
require_once($CFG->dirroot . '/mod/assign/locallib.php');
require_once($CFG->dirroot . '/mod/assign/tests/generator.php');
require_once($CFG->dirroot . '/comment/lib.php');
/**
* Events tests class.
*
* @package assignsubmission_comments
* @category test
* @copyright 2013 Rajesh Taneja <rajesh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class events_test extends \advanced_testcase {
// Use the generator helper.
use mod_assign_test_generator;
/**
* Test comment_created event.
*/
public function test_comment_created(): void {
$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);
$this->setUser($teacher);
$submission = $assign->get_user_submission($student->id, true);
$context = $assign->get_context();
$options = new \stdClass();
$options->area = 'submission_comments';
$options->course = $assign->get_course();
$options->context = $context;
$options->itemid = $submission->id;
$options->component = 'assignsubmission_comments';
$options->showcount = true;
$options->displaycancel = true;
$comment = new \comment($options);
// Triggering and capturing the event.
$sink = $this->redirectEvents();
$comment->add('New comment');
$events = $sink->get_events();
$this->assertCount(1, $events);
$event = reset($events);
$sink->close();
// Checking that the event contains the expected values.
$this->assertInstanceOf('\assignsubmission_comments\event\comment_created', $event);
$this->assertEquals($context, $event->get_context());
$url = new \moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id));
$this->assertEquals($url, $event->get_url());
$this->assertEventContextNotUsed($event);
}
/**
* Test comment_deleted event.
*/
public function test_comment_deleted(): void {
$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);
$this->setUser($teacher);
$submission = $assign->get_user_submission($student->id, true);
$context = $assign->get_context();
$options = new \stdClass();
$options->area = 'submission_comments';
$options->course = $assign->get_course();
$options->context = $context;
$options->itemid = $submission->id;
$options->component = 'assignsubmission_comments';
$options->showcount = true;
$options->displaycancel = true;
$comment = new \comment($options);
$newcomment = $comment->add('New comment 1');
// Triggering and capturing the event.
$sink = $this->redirectEvents();
$comment->delete($newcomment->id);
$events = $sink->get_events();
$this->assertCount(1, $events);
$event = reset($events);
// Checking that the event contains the expected values.
$this->assertInstanceOf('\assignsubmission_comments\event\comment_deleted', $event);
$this->assertEquals($context, $event->get_context());
$url = new \moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id));
$this->assertEquals($url, $event->get_url());
$this->assertEventContextNotUsed($event);
}
}
@@ -0,0 +1,363 @@
<?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 assignsubmission_comments.
*
* @package assignsubmission_comments
* @copyright 2018 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace assignsubmission_comments\privacy;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/mod/assign/tests/privacy/provider_test.php');
use mod_assign\privacy\useridlist;
/**
* Unit tests for mod/assign/submission/comments/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 string $submissiontext Submission text
* @return array Submission plugin object and the submission object and the comment object.
*/
protected function create_comment_submission($assign, $student, $submissiontext) {
$submission = $assign->get_user_submission($student->id, true);
$plugin = $assign->get_submission_plugin_by_type('comments');
$context = $assign->get_context();
$options = new \stdClass();
$options->area = 'submission_comments';
$options->course = $assign->get_course();
$options->context = $context;
$options->itemid = $submission->id;
$options->component = 'assignsubmission_comments';
$options->showcount = true;
$options->displaycancel = true;
$comment = new \comment($options);
$comment->set_post_permission(true);
$this->setUser($student);
$comment->add($submissiontext);
return [$plugin, $submission, $comment];
}
/**
* Quick test to make sure that get_metadata returns something.
*/
public function test_get_metadata(): void {
$collection = new \core_privacy\local\metadata\collection('assignsubmission_comments');
$collection = \assignsubmission_comments\privacy\provider::get_metadata($collection);
$this->assertNotEmpty($collection);
}
/**
* Test returning the context for a user who has made a comment in an assignment.
*/
public function test_get_context_for_userid_within_submission(): void {
$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();
$studentcomment = 'Comment from user 1';
list($plugin, $submission, $comment) = $this->create_comment_submission($assign, $user1, $studentcomment);
$teachercomment = 'From the teacher';
$this->setUser($user2);
$comment->add($teachercomment);
$contextlist = new \core_privacy\local\request\contextlist();
\assignsubmission_comments\privacy\provider::get_context_for_userid_within_submission($user2->id, $contextlist);
$this->assertEquals($context->id, $contextlist->get_contextids()[0]);
}
/**
* Test returning student ids given a user ID.
*/
public function test_get_student_user_ids(): void {
$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();
$studentcomment = 'Comment from user 1';
list($plugin, $submission, $comment) = $this->create_comment_submission($assign, $user1, $studentcomment);
$teachercomment = 'From the teacher';
$this->setUser($user2);
$comment->add($teachercomment);
$useridlist = new useridlist($user2->id, $assign->get_instance()->id);
\assignsubmission_comments\privacy\provider::get_student_user_ids($useridlist);
$this->assertEquals($user1->id, $useridlist->get_userids()[0]->id);
}
/**
* Test returning users related to a given context.
*/
public function test_get_userids_from_context(): void {
// Get a bunch of users making comments.
// Some in one context some in another.
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
// Only in first context.
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
// First and second context.
$user3 = $this->getDataGenerator()->create_user();
// Second context only.
$user4 = $this->getDataGenerator()->create_user();
$this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student');
$assign1 = $this->create_instance(['course' => $course]);
$assign2 = $this->create_instance(['course' => $course]);
$assigncontext1 = $assign1->get_context();
$assigncontext2 = $assign2->get_context();
$user1comment = 'Comment from user 1';
list($plugin, $submission, $comment) = $this->create_comment_submission($assign1, $user1, $user1comment);
$user2comment = 'From user 2';
$this->setUser($user2);
$comment->add($user2comment);
$user3comment = 'User 3 comment';
$this->setUser($user3);
$comment->add($user3comment);
$user4comment = 'Comment from user 4';
list($plugin, $submission, $comment) = $this->create_comment_submission($assign2, $user4, $user4comment);
$user3secondcomment = 'Comment on user 4 post.';
$this->setUser($user3);
$comment->add($user3comment);
$userlist = new \core_privacy\local\request\userlist($assigncontext1, 'assignsubmission_comments');
\assignsubmission_comments\privacy\provider::get_userids_from_context($userlist);
$userids = $userlist->get_userids();
$this->assertCount(3, $userids);
// User 1,2 and 3 are the expected ones in the array. User 4 isn't.
$this->assertContainsEquals($user1->id, $userids);
$this->assertContainsEquals($user2->id, $userids);
$this->assertContainsEquals($user3->id, $userids);
$this->assertNotContainsEquals($user4->id, $userids);
}
/**
* Test that comments are exported for a user.
*/
public function test_export_submission_user_data(): void {
$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();
$studentcomment = 'Comment from user 1';
list($plugin, $submission, $comment) = $this->create_comment_submission($assign, $user1, $studentcomment);
$teachercomment = 'From the teacher';
$this->setUser($user2);
$comment->add($teachercomment);
$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, $submission);
\assignsubmission_comments\privacy\provider::export_submission_user_data($exportdata);
$exportedcomments = $writer->get_data(['Comments']);
// Can't rely on these comments coming out in order.
if ($exportedcomments->comments[0]->userid == $user1->id) {
$exportedstudentcomment = $exportedcomments->comments[0]->content;
$exportedteachercomment = $exportedcomments->comments[1]->content;
} else {
$exportedstudentcomment = $exportedcomments->comments[1]->content;
$exportedteachercomment = $exportedcomments->comments[0]->content;
}
$this->assertCount(2, $exportedcomments->comments);
$this->assertStringContainsString($studentcomment, $exportedstudentcomment);
$this->assertStringContainsString($teachercomment, $exportedteachercomment);
}
/**
* Test that all comments are deleted for this context.
*/
public function test_delete_submission_for_context(): void {
global $DB;
$this->resetAfterTest();
// Create course, assignment, submission, and then a feedback comment.
$course = $this->getDataGenerator()->create_course();
// Student.
$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();
$studentcomment = 'Comment from user 1';
list($plugin, $submission, $comment) = $this->create_comment_submission($assign, $user1, $studentcomment);
$studentcomment = 'Comment from user 2';
list($plugin2, $submission2, $comment2) = $this->create_comment_submission($assign, $user2, $studentcomment);
$teachercomment1 = 'From the teacher';
$teachercomment2 = 'From the teacher for second student.';
$this->setUser($user3);
$comment->add($teachercomment1);
$comment2->add($teachercomment2);
// Only need the context in this plugin for this operation.
$requestdata = new \mod_assign\privacy\assign_plugin_request_data($context, $assign);
\assignsubmission_comments\privacy\provider::delete_submission_for_context($requestdata);
$results = $DB->get_records('comments', ['contextid' => $context->id]);
$this->assertEmpty($results);
}
/**
* Test that the comments for a user are deleted.
*/
public function test_delete_submission_for_userid(): void {
global $DB;
$this->resetAfterTest();
// Create course, assignment, submission, and then a feedback comment.
$course = $this->getDataGenerator()->create_course();
// Student.
$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();
$studentcomment = 'Comment from user 1';
list($plugin, $submission, $comment) = $this->create_comment_submission($assign, $user1, $studentcomment);
$studentcomment = 'Comment from user 2';
list($plugin2, $submission2, $comment2) = $this->create_comment_submission($assign, $user2, $studentcomment);
$teachercomment1 = 'From the teacher';
$teachercomment2 = 'From the teacher for second student.';
$this->setUser($user3);
$comment->add($teachercomment1);
$comment2->add($teachercomment2);
// Provide full details to delete the comments.
$requestdata = new \mod_assign\privacy\assign_plugin_request_data($context, $assign, null, [], $user1);
\assignsubmission_comments\privacy\provider::delete_submission_for_userid($requestdata);
$results = $DB->get_records('comments', ['contextid' => $context->id]);
// We are only deleting the comments for user1 (one comment) so we should have three left.
$this->assertCount(3, $results);
foreach ($results as $result) {
// Check that none of the comments are from user1.
$this->assertNotEquals($user1->id, $result->userid);
}
}
/**
* Test deletion of all submissions for a context works.
*/
public function test_delete_submissions(): void {
global $DB;
// Get a bunch of users making comments.
// Some in one context some in another.
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
// Only in first context.
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
// First and second context.
$user3 = $this->getDataGenerator()->create_user();
// Second context only.
$user4 = $this->getDataGenerator()->create_user();
$this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student');
$assign1 = $this->create_instance(['course' => $course]);
$assign2 = $this->create_instance(['course' => $course]);
$assigncontext1 = $assign1->get_context();
$assigncontext2 = $assign2->get_context();
$user1comment = 'Comment from user 1';
list($plugin, $submission, $comment) = $this->create_comment_submission($assign1, $user1, $user1comment);
$user2comment = 'From user 2';
$this->setUser($user2);
$comment->add($user2comment);
$user3comment = 'User 3 comment';
$this->setUser($user3);
$comment->add($user3comment);
$user4comment = 'Comment from user 4';
list($plugin, $submission, $comment) = $this->create_comment_submission($assign2, $user4, $user4comment);
$user3secondcomment = 'Comment on user 4 post.';
$this->setUser($user3);
$comment->add($user3comment);
// There should be three entries. One for the first three users.
$results = $DB->get_records('comments', ['contextid' => $assigncontext1->id]);
$this->assertCount(3, $results);
$deletedata = new \mod_assign\privacy\assign_plugin_request_data($assigncontext1, $assign1);
$deletedata->set_userids([$user1->id, $user3->id]);
\assignsubmission_comments\privacy\provider::delete_submissions($deletedata);
// We should be left with just a comment from user 2.
$results = $DB->get_records('comments', ['contextid' => $assigncontext1->id]);
$this->assertCount(1, $results);
$this->assertEquals($user2comment, current($results)->content);
}
}
@@ -0,0 +1,29 @@
<?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 submission plugin
*
* @package assignsubmission_comments
* @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 = 2024042200;
$plugin->requires = 2024041600;
$plugin->component = 'assignsubmission_comments';
@@ -0,0 +1,66 @@
<?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 class for backup of this submission plugin
*
* @package assignsubmission_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 submission files
*
* This just adds its filearea to the annotations and records the number of files
*
* @package assignsubmission_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_assignsubmission_file_subplugin extends backup_subplugin {
/**
* Returns the subplugin information to attach to submission element
* @return backup_subplugin_element
*/
protected function define_submission_subplugin_structure() {
// Create XML elements.
$subplugin = $this->get_subplugin_element();
$subpluginwrapper = new backup_nested_element($this->get_recommended_name());
$subpluginelement = new backup_nested_element('submission_file',
null,
array('numfiles', 'submission'));
// Connect XML elements into the tree.
$subplugin->add_child($subpluginwrapper);
$subpluginwrapper->add_child($subpluginelement);
// Set source to populate the data.
$subpluginelement->set_source_table('assignsubmission_file',
array('submission' => backup::VAR_PARENTID));
// The parent is the submission.
$subpluginelement->annotate_files('assignsubmission_file',
'submission_files',
'submission');
return $subplugin;
}
}
@@ -0,0 +1,77 @@
<?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 class for restore of this submission plugin
*
* @package assignsubmission_file
* @copyright 2012 NetSpot {@link http://www.netspot.com.au}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Restore subplugin class.
*
* Provides the necessary information
* needed to restore one assign_submission subplugin.
*
* @package assignsubmission_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_assignsubmission_file_subplugin extends restore_subplugin {
/**
* Returns the paths to be handled by the subplugin at workshop level
* @return array
*/
protected function define_submission_subplugin_structure() {
$paths = array();
$elename = $this->get_namefor('submission');
$elepath = $this->get_pathfor('/submission_file');
// We used get_recommended_name() so this works.
$paths[] = new restore_path_element($elename, $elepath);
return $paths;
}
/**
* Processes one submission_file element
* @param mixed $data
* @return void
*/
public function process_assignsubmission_file_submission($data) {
global $DB;
$data = (object)$data;
$data->assignment = $this->get_new_parentid('assign');
$oldsubmissionid = $data->submission;
// The mapping is set in the restore for the core assign activity
// when a submission node is processed.
$data->submission = $this->get_mappingid('submission', $data->submission);
$DB->insert_record('assignsubmission_file', $data);
$this->add_related_files('assignsubmission_file',
'submission_files',
'submission',
null,
$oldsubmissionid);
}
}
@@ -0,0 +1,97 @@
<?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/>.
/**
* The assignsubmission_file assessable uploaded event.
*
* @package assignsubmission_file
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace assignsubmission_file\event;
defined('MOODLE_INTERNAL') || die();
/**
* The assignsubmission_file assessable uploaded event class.
*
* @package assignsubmission_file
* @since Moodle 2.6
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class assessable_uploaded extends \core\event\assessable_uploaded {
/**
* Legacy event files.
*
* @var array
*/
protected $legacyfiles = array();
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' has uploaded a file to the submission with id '$this->objectid' " .
"in the assignment activity with course module id '$this->contextinstanceid'.";
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventassessableuploaded', 'assignsubmission_file');
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/assign/view.php', array('id' => $this->contextinstanceid));
}
/**
* Sets the legacy event data.
*
* @param \stdClass $legacyfiles legacy event data.
* @return void
*/
public function set_legacy_files($legacyfiles) {
$this->legacyfiles = $legacyfiles;
}
/**
* Init method.
*
* @return void
*/
protected function init() {
parent::init();
$this->data['objecttable'] = 'assign_submission';
}
public static function get_objectid_mapping() {
return array('db' => 'assign_submission', 'restore' => 'submission');
}
}
@@ -0,0 +1,88 @@
<?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/>.
/**
* The assignsubmission_file submission_created event.
*
* @package assignsubmission_file
* @copyright 2014 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace assignsubmission_file\event;
defined('MOODLE_INTERNAL') || die();
/**
* The assignsubmission_file submission_created event class.
*
* @property-read array $other {
* Extra information about the event.
*
* - int filesubmissioncount: The number of files uploaded.
* }
*
* @package assignsubmission_file
* @since Moodle 2.7
* @copyright 2014 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class submission_created extends \mod_assign\event\submission_created {
/**
* Init method.
*/
protected function init() {
parent::init();
$this->data['objecttable'] = 'assignsubmission_file';
}
/**
* Returns non-localised description of what happened.
*
* @return string
*/
public function get_description() {
$descriptionstring = "The user with id '$this->userid' created a file submission and uploaded " .
"'{$this->other['filesubmissioncount']}' file/s in the assignment with course module id " .
"'$this->contextinstanceid'";
if (!empty($this->other['groupid'])) {
$descriptionstring .= " for the group with id '{$this->other['groupid']}'.";
} else {
$descriptionstring .= ".";
}
return $descriptionstring;
}
/**
* Custom validation.
*
* @throws \coding_exception
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->other['filesubmissioncount'])) {
throw new \coding_exception('The \'filesubmissioncount\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
// No mapping available for 'assignsubmission_file'.
return array('db' => 'assignsubmission_file', 'restore' => \core\event\base::NOT_MAPPED);
}
}
@@ -0,0 +1,88 @@
<?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/>.
/**
* The assignsubmission_file submission_updated event.
*
* @package assignsubmission_file
* @copyright 2014 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace assignsubmission_file\event;
defined('MOODLE_INTERNAL') || die();
/**
* The assignsubmission_file submission_updated event class.
*
* @property-read array $other {
* Extra information about the event.
*
* - int filesubmissioncount: The number of files uploaded.
* }
*
* @package assignsubmission_file
* @since Moodle 2.7
* @copyright 2014 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class submission_updated extends \mod_assign\event\submission_updated {
/**
* Init method.
*/
protected function init() {
parent::init();
$this->data['objecttable'] = 'assignsubmission_file';
}
/**
* Returns non-localised description of what happened.
*
* @return string
*/
public function get_description() {
$descriptionstring = "The user with id '$this->userid' updated a file submission and uploaded " .
"'{$this->other['filesubmissioncount']}' file/s in the assignment with course module id " .
"'$this->contextinstanceid'";
if (!empty($this->other['groupid'])) {
$descriptionstring .= " for the group with id '{$this->other['groupid']}'.";
} else {
$descriptionstring .= ".";
}
return $descriptionstring;
}
/**
* Custom validation.
*
* @throws \coding_exception
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->other['filesubmissioncount'])) {
throw new \coding_exception('The \'filesubmissioncount\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
// No mapping available for 'assignsubmission_file'.
return array('db' => 'assignsubmission_file', 'restore' => \core\event\base::NOT_MAPPED);
}
}
@@ -0,0 +1,180 @@
<?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 assignsubmission_file
* @copyright 2018 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace assignsubmission_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\writer;
use \core_privacy\local\request\contextlist;
use \mod_assign\privacy\assign_plugin_request_data;
/**
* Privacy class for requesting user data.
*
* @package assignsubmission_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\assignsubmission_provider,
\mod_assign\privacy\assignsubmission_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;
}
/**
* This is covered by mod_assign provider and the query on assign_submissions.
*
* @param int $userid The user ID that we are finding contexts for.
* @param contextlist $contextlist A context list to add sql and params to for contexts.
*/
public static function get_context_for_userid_within_submission(int $userid, contextlist $contextlist) {
// This is already fetched from mod_assign.
}
/**
* This is also covered by the mod_assign provider and it's queries.
*
* @param \mod_assign\privacy\useridlist $useridlist An object for obtaining user IDs of students.
*/
public static function get_student_user_ids(\mod_assign\privacy\useridlist $useridlist) {
// No need.
}
/**
* If you have tables that contain userids and you can generate entries in your tables without creating an
* entry in the assign_submission table then please fill in this method.
*
* @param 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_submission_user_data(assign_plugin_request_data $exportdata) {
// We currently don't show submissions to teachers when exporting their data.
$context = $exportdata->get_context();
if ($exportdata->get_user() != null) {
return null;
}
$user = new \stdClass();
$assign = $exportdata->get_assign();
$plugin = $assign->get_plugin_by_type('assignsubmission', 'file');
$files = $plugin->get_files($exportdata->get_pluginobject(), $user);
foreach ($files as $file) {
$userid = $exportdata->get_pluginobject()->userid;
writer::with_context($exportdata->get_context())->export_file($exportdata->get_subcontext(), $file);
// Plagiarism data.
$coursecontext = $context->get_course_context();
\core_plagiarism\privacy\provider::export_plagiarism_user_data($userid, $context, $exportdata->get_subcontext(), [
'cmid' => $context->instanceid,
'course' => $coursecontext->instanceid,
'userid' => $userid,
'file' => $file
]);
}
}
/**
* Any call to this method should delete all user data for the context defined in the deletion_criteria.
*
* @param assign_plugin_request_data $requestdata Information useful for deleting user data.
*/
public static function delete_submission_for_context(assign_plugin_request_data $requestdata) {
global $DB;
\core_plagiarism\privacy\provider::delete_plagiarism_for_context($requestdata->get_context());
$fs = get_file_storage();
$fs->delete_area_files($requestdata->get_context()->id, 'assignsubmission_file', ASSIGNSUBMISSION_FILE_FILEAREA);
// Delete records from assignsubmission_file table.
$DB->delete_records('assignsubmission_file', ['assignment' => $requestdata->get_assign()->get_instance()->id]);
}
/**
* A call to this method should delete user data (where practical) using the userid and submission.
*
* @param assign_plugin_request_data $deletedata Details about the user and context to focus the deletion.
*/
public static function delete_submission_for_userid(assign_plugin_request_data $deletedata) {
global $DB;
\core_plagiarism\privacy\provider::delete_plagiarism_for_user($deletedata->get_user()->id, $deletedata->get_context());
$submissionid = $deletedata->get_pluginobject()->id;
$fs = get_file_storage();
$fs->delete_area_files($deletedata->get_context()->id, 'assignsubmission_file', ASSIGNSUBMISSION_FILE_FILEAREA,
$submissionid);
$DB->delete_records('assignsubmission_file', ['assignment' => $deletedata->get_assignid(), 'submission' => $submissionid]);
}
/**
* Deletes all submissions for the submission ids / userids provided in a context.
* assign_plugin_request_data contains:
* - context
* - assign object
* - submission ids (pluginids)
* - user ids
* @param assign_plugin_request_data $deletedata A class that contains the relevant information required for deletion.
*/
public static function delete_submissions(assign_plugin_request_data $deletedata) {
global $DB;
\core_plagiarism\privacy\provider::delete_plagiarism_for_users($deletedata->get_userids(), $deletedata->get_context());
if (empty($deletedata->get_submissionids())) {
return;
}
$fs = get_file_storage();
list($sql, $params) = $DB->get_in_or_equal($deletedata->get_submissionids(), SQL_PARAMS_NAMED);
$fs->delete_area_files_select($deletedata->get_context()->id, 'assignsubmission_file', ASSIGNSUBMISSION_FILE_FILEAREA,
$sql, $params);
$params['assignid'] = $deletedata->get_assignid();
$DB->delete_records_select('assignsubmission_file', "assignment = :assignid AND submission $sql", $params);
}
}
+29
View File
@@ -0,0 +1,29 @@
<?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 assignsubmission_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(
);
+21
View File
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="mod/assign/submission/file/db" VERSION="20120423" COMMENT="XMLDB file for Moodle mod/assign/submission/file"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../../lib/xmldb/xmldb.xsd"
>
<TABLES>
<TABLE NAME="assignsubmission_file" COMMENT="Info about file submissions for assignments">
<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="submission" 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 the student submitted."/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="The unique id for this submission info."/>
<KEY NAME="assignment" TYPE="foreign" FIELDS="assignment" REFTABLE="assign" REFFIELDS="id" COMMENT="The assignment instance this submission relates to"/>
<KEY NAME="submission" TYPE="foreign" FIELDS="submission" REFTABLE="assign_submission" REFFIELDS="id" COMMENT="The submission this file submission relates to."/>
</KEYS>
</TABLE>
</TABLES>
</XMLDB>
+44
View File
@@ -0,0 +1,44 @@
<?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 install
*
* @package assignsubmission_file
* @copyright 2012 NetSpot {@link http://www.netspot.com.au}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Stub for upgrade code
* @param int $oldversion
* @return bool
*/
function xmldb_assignsubmission_file_upgrade($oldversion) {
// Automatically generated Moodle v4.1.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.2.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.3.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.4.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
@@ -0,0 +1,50 @@
<?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 'assignsubmission_file', language 'en'
*
* @package assignsubmission_file
* @copyright 2012 NetSpot {@link http://www.netspot.com.au}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['acceptedfiletypes'] = 'Accepted file types';
$string['acceptedfiletypes_help'] = 'Accepted file types can be restricted by entering a list of file extensions. If the field is left empty, then all file types are allowed.';
$string['configmaxbytes'] = 'Maximum file size';
$string['countfiles'] = '{$a} files';
$string['default'] = 'Enabled by default';
$string['default_help'] = 'If set, this submission method will be enabled by default for all new assignments.';
$string['defaultacceptedfiletypes'] = 'Default accepted file types';
$string['enabled'] = 'File submissions';
$string['enabled_help'] = 'If enabled, students are able to upload one or more files as their submission.';
$string['eventassessableuploaded'] = 'A file has been uploaded.';
$string['file'] = 'File submissions';
$string['maxbytes'] = 'Maximum file size';
$string['maxfiles'] = 'Maximum files per submission';
$string['maxfiles_help'] = 'If file submissions are enabled, each assignment can be set to accept up to this number of files for their submission.';
$string['maxfilessubmission'] = 'Maximum number of uploaded files';
$string['maxfilessubmission_help'] = 'If file submissions are enabled, each student will be able to upload up to this number of files for their submission.';
$string['maximumsubmissionsize'] = 'Maximum submission size';
$string['maximumsubmissionsize_help'] = 'Files uploaded by students may be up to this size.';
$string['pluginname'] = 'File submissions';
$string['privacy:metadata:filepurpose'] = 'The files loaded for this assignment submission';
$string['siteuploadlimit'] = 'Site upload limit';
$string['submissionfilearea'] = 'Uploaded submission files';
// Deprecated since Moodle 4.3.
$string['numfilesforlog'] = 'The number of file(s) : {$a} file(s).';
@@ -0,0 +1 @@
numfilesforlog,assignsubmission_file
+89
View File
@@ -0,0 +1,89 @@
<?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 submission file plugin
*
* @package assignsubmission_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 submissions 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 assignsubmission_file_pluginfile($course,
$cm,
context $context,
$filearea,
$args,
$forcedownload,
array $options=array()) {
global $DB, $CFG;
if ($context->contextlevel != CONTEXT_MODULE) {
return false;
}
require_login($course, false, $cm);
$itemid = (int)array_shift($args);
$record = $DB->get_record('assign_submission',
array('id'=>$itemid),
'userid, assignment, groupid',
MUST_EXIST);
$userid = $record->userid;
$groupid = $record->groupid;
require_once($CFG->dirroot . '/mod/assign/locallib.php');
$assign = new assign($context, $cm, $course);
if ($assign->get_instance()->id != $record->assignment) {
return false;
}
if ($assign->get_instance()->teamsubmission &&
!$assign->can_view_group_submission($groupid)) {
return false;
}
if (!$assign->get_instance()->teamsubmission &&
!$assign->can_view_submission($userid)) {
return false;
}
$relativepath = implode('/', $args);
$fullpath = "/{$context->id}/assignsubmission_file/$filearea/$itemid/$relativepath";
$fs = get_file_storage();
if (!($file = $fs->get_file_by_hash(sha1($fullpath))) || $file->is_directory()) {
return false;
}
// Download MUST be forced - security!
send_stored_file($file, 0, 0, true, $options);
}
+637
View File
@@ -0,0 +1,637 @@
<?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 submission plugin
*
* This class provides all the functionality for the new assign module.
*
* @package assignsubmission_file
* @copyright 2012 NetSpot {@link http://www.netspot.com.au}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use core_external\external_value;
// File areas for file submission assignment.
define('ASSIGNSUBMISSION_FILE_MAXSUMMARYFILES', 5);
define('ASSIGNSUBMISSION_FILE_FILEAREA', 'submission_files');
/**
* Library class for file submission plugin extending submission plugin base class
*
* @package assignsubmission_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_submission_file extends assign_submission_plugin {
/**
* Get the name of the file submission plugin
* @return string
*/
public function get_name() {
return get_string('file', 'assignsubmission_file');
}
/**
* Get file submission information from the database
*
* @param int $submissionid
* @return mixed
*/
private function get_file_submission($submissionid) {
global $DB;
return $DB->get_record('assignsubmission_file', array('submission'=>$submissionid));
}
/**
* Get the default setting for file submission plugin
*
* @param MoodleQuickForm $mform The form to add elements to
* @return void
*/
public function get_settings(MoodleQuickForm $mform) {
global $CFG, $COURSE;
if ($this->assignment->has_instance()) {
$defaultmaxfilesubmissions = $this->get_config('maxfilesubmissions');
$defaultmaxsubmissionsizebytes = $this->get_config('maxsubmissionsizebytes');
$defaultfiletypes = $this->get_config('filetypeslist');
} else {
$defaultmaxfilesubmissions = get_config('assignsubmission_file', 'maxfiles');
$defaultmaxsubmissionsizebytes = get_config('assignsubmission_file', 'maxbytes');
$defaultfiletypes = get_config('assignsubmission_file', 'filetypes');
}
$defaultfiletypes = (string)$defaultfiletypes;
$settings = array();
$options = array();
for ($i = 1; $i <= get_config('assignsubmission_file', 'maxfiles'); $i++) {
$options[$i] = $i;
}
$name = get_string('maxfilessubmission', 'assignsubmission_file');
$mform->addElement('select', 'assignsubmission_file_maxfiles', $name, $options);
$mform->addHelpButton('assignsubmission_file_maxfiles',
'maxfilessubmission',
'assignsubmission_file');
$mform->setDefault('assignsubmission_file_maxfiles', $defaultmaxfilesubmissions);
$mform->hideIf('assignsubmission_file_maxfiles', 'assignsubmission_file_enabled', 'notchecked');
$choices = get_max_upload_sizes($CFG->maxbytes,
$COURSE->maxbytes,
get_config('assignsubmission_file', 'maxbytes'));
$settings[] = array('type' => 'select',
'name' => 'maxsubmissionsizebytes',
'description' => get_string('maximumsubmissionsize', 'assignsubmission_file'),
'options'=> $choices,
'default'=> $defaultmaxsubmissionsizebytes);
$name = get_string('maximumsubmissionsize', 'assignsubmission_file');
$mform->addElement('select', 'assignsubmission_file_maxsizebytes', $name, $choices);
$mform->addHelpButton('assignsubmission_file_maxsizebytes',
'maximumsubmissionsize',
'assignsubmission_file');
$mform->setDefault('assignsubmission_file_maxsizebytes', $defaultmaxsubmissionsizebytes);
$mform->hideIf('assignsubmission_file_maxsizebytes',
'assignsubmission_file_enabled',
'notchecked');
$name = get_string('acceptedfiletypes', 'assignsubmission_file');
$mform->addElement('filetypes', 'assignsubmission_file_filetypes', $name);
$mform->addHelpButton('assignsubmission_file_filetypes', 'acceptedfiletypes', 'assignsubmission_file');
$mform->setDefault('assignsubmission_file_filetypes', $defaultfiletypes);
$mform->hideIf('assignsubmission_file_filetypes', 'assignsubmission_file_enabled', 'notchecked');
}
/**
* Save the settings for file submission plugin
*
* @param stdClass $data
* @return bool
*/
public function save_settings(stdClass $data) {
$this->set_config('maxfilesubmissions', $data->assignsubmission_file_maxfiles);
$this->set_config('maxsubmissionsizebytes', $data->assignsubmission_file_maxsizebytes);
if (!empty($data->assignsubmission_file_filetypes)) {
$this->set_config('filetypeslist', $data->assignsubmission_file_filetypes);
} else {
$this->set_config('filetypeslist', '');
}
return true;
}
/**
* File format options
*
* @return array
*/
private function get_file_options() {
$fileoptions = array('subdirs' => 1,
'maxbytes' => $this->get_config('maxsubmissionsizebytes'),
'maxfiles' => $this->get_config('maxfilesubmissions'),
'accepted_types' => $this->get_configured_typesets(),
'return_types' => (FILE_INTERNAL | FILE_CONTROLLED_LINK));
if ($fileoptions['maxbytes'] == 0) {
// Use module default.
$fileoptions['maxbytes'] = get_config('assignsubmission_file', 'maxbytes');
}
return $fileoptions;
}
/**
* Add elements to submission form
*
* @param mixed $submission stdClass|null
* @param MoodleQuickForm $mform
* @param stdClass $data
* @return bool
*/
public function get_form_elements($submission, MoodleQuickForm $mform, stdClass $data) {
global $OUTPUT;
if ($this->get_config('maxfilesubmissions') <= 0) {
return false;
}
$fileoptions = $this->get_file_options();
$submissionid = $submission ? $submission->id : 0;
$data = file_prepare_standard_filemanager($data,
'files',
$fileoptions,
$this->assignment->get_context(),
'assignsubmission_file',
ASSIGNSUBMISSION_FILE_FILEAREA,
$submissionid);
$mform->addElement('filemanager', 'files_filemanager', $this->get_name(), null, $fileoptions);
return true;
}
/**
* Count the number of files
*
* @param int $submissionid
* @param string $area
* @return int
*/
private function count_files($submissionid, $area) {
$fs = get_file_storage();
$files = $fs->get_area_files($this->assignment->get_context()->id,
'assignsubmission_file',
$area,
$submissionid,
'id',
false);
return count($files);
}
/**
* Save the files and trigger plagiarism plugin, if enabled,
* to scan the uploaded files via events trigger
*
* @param stdClass $submission
* @param stdClass $data
* @return bool
*/
public function save(stdClass $submission, stdClass $data) {
global $USER, $DB;
$fileoptions = $this->get_file_options();
$data = file_postupdate_standard_filemanager($data,
'files',
$fileoptions,
$this->assignment->get_context(),
'assignsubmission_file',
ASSIGNSUBMISSION_FILE_FILEAREA,
$submission->id);
$filesubmission = $this->get_file_submission($submission->id);
// Plagiarism code event trigger when files are uploaded.
$fs = get_file_storage();
$files = $fs->get_area_files($this->assignment->get_context()->id,
'assignsubmission_file',
ASSIGNSUBMISSION_FILE_FILEAREA,
$submission->id,
'id',
false);
$count = $this->count_files($submission->id, ASSIGNSUBMISSION_FILE_FILEAREA);
$params = array(
'context' => context_module::instance($this->assignment->get_course_module()->id),
'courseid' => $this->assignment->get_course()->id,
'objectid' => $submission->id,
'other' => array(
'content' => '',
'pathnamehashes' => array_keys($files)
)
);
if (!empty($submission->userid) && ($submission->userid != $USER->id)) {
$params['relateduserid'] = $submission->userid;
}
if ($this->assignment->is_blind_marking()) {
$params['anonymous'] = 1;
}
$event = \assignsubmission_file\event\assessable_uploaded::create($params);
$event->set_legacy_files($files);
$event->trigger();
$groupname = null;
$groupid = 0;
// Get the group name as other fields are not transcribed in the logs and this information is important.
if (empty($submission->userid) && !empty($submission->groupid)) {
$groupname = $DB->get_field('groups', 'name', array('id' => $submission->groupid), MUST_EXIST);
$groupid = $submission->groupid;
} else {
$params['relateduserid'] = $submission->userid;
}
// Unset the objectid and other field from params for use in submission events.
unset($params['objectid']);
unset($params['other']);
$params['other'] = array(
'submissionid' => $submission->id,
'submissionattempt' => $submission->attemptnumber,
'submissionstatus' => $submission->status,
'filesubmissioncount' => $count,
'groupid' => $groupid,
'groupname' => $groupname
);
if ($filesubmission) {
$filesubmission->numfiles = $this->count_files($submission->id,
ASSIGNSUBMISSION_FILE_FILEAREA);
$updatestatus = $DB->update_record('assignsubmission_file', $filesubmission);
$params['objectid'] = $filesubmission->id;
$event = \assignsubmission_file\event\submission_updated::create($params);
$event->set_assign($this->assignment);
$event->trigger();
return $updatestatus;
} else {
$filesubmission = new stdClass();
$filesubmission->numfiles = $this->count_files($submission->id,
ASSIGNSUBMISSION_FILE_FILEAREA);
$filesubmission->submission = $submission->id;
$filesubmission->assignment = $this->assignment->get_instance()->id;
$filesubmission->id = $DB->insert_record('assignsubmission_file', $filesubmission);
$params['objectid'] = $filesubmission->id;
$event = \assignsubmission_file\event\submission_created::create($params);
$event->set_assign($this->assignment);
$event->trigger();
return $filesubmission->id > 0;
}
}
/**
* Remove files from this submission.
*
* @param stdClass $submission The submission
* @return boolean
*/
public function remove(stdClass $submission) {
global $DB;
$fs = get_file_storage();
$fs->delete_area_files($this->assignment->get_context()->id,
'assignsubmission_file',
ASSIGNSUBMISSION_FILE_FILEAREA,
$submission->id);
$currentsubmission = $this->get_file_submission($submission->id);
if ($currentsubmission) {
$currentsubmission->numfiles = 0;
$DB->update_record('assignsubmission_file', $currentsubmission);
}
return true;
}
/**
* Produce a list of files suitable for export that represent this feedback or submission
*
* @param stdClass $submission The submission
* @param stdClass $user The user record - unused
* @return array - return an array of files indexed by filename
*/
public function get_files(stdClass $submission, stdClass $user) {
$result = array();
$fs = get_file_storage();
$files = $fs->get_area_files($this->assignment->get_context()->id,
'assignsubmission_file',
ASSIGNSUBMISSION_FILE_FILEAREA,
$submission->id,
'timemodified, id',
false);
foreach ($files as $file) {
// Do we return the full folder path or just the file name?
if (isset($submission->exportfullpath) && $submission->exportfullpath == false) {
$result[$file->get_filename()] = $file;
} else {
$result[$file->get_filepath().$file->get_filename()] = $file;
}
}
return $result;
}
/**
* Display the list of files in the submission status table
*
* @param stdClass $submission
* @param bool $showviewlink Set this to true if the list of files is long
* @return string
*/
public function view_summary(stdClass $submission, & $showviewlink) {
$count = $this->count_files($submission->id, ASSIGNSUBMISSION_FILE_FILEAREA);
// Show we show a link to view all files for this plugin?
$showviewlink = $count > ASSIGNSUBMISSION_FILE_MAXSUMMARYFILES;
if ($count <= ASSIGNSUBMISSION_FILE_MAXSUMMARYFILES) {
return $this->assignment->render_area_files('assignsubmission_file',
ASSIGNSUBMISSION_FILE_FILEAREA,
$submission->id);
} else {
return get_string('countfiles', 'assignsubmission_file', $count);
}
}
/**
* No full submission view - the summary contains the list of files and that is the whole submission
*
* @param stdClass $submission
* @return string
*/
public function view(stdClass $submission) {
return $this->assignment->render_area_files('assignsubmission_file',
ASSIGNSUBMISSION_FILE_FILEAREA,
$submission->id);
}
/**
* Return true if this plugin can upgrade an old Moodle 2.2 assignment of this type
* and version.
*
* @param string $type
* @param int $version
* @return bool True if upgrade is possible
*/
public function can_upgrade($type, $version) {
$uploadsingletype ='uploadsingle';
$uploadtype ='upload';
if (($type == $uploadsingletype || $type == $uploadtype) && $version >= 2011112900) {
return true;
}
return false;
}
/**
* Upgrade the settings from the old assignment
* to the new plugin based one
*
* @param context $oldcontext - the old assignment context
* @param stdClass $oldassignment - the old assignment data record
* @param string $log record log events here
* @return bool Was it a success? (false will trigger rollback)
*/
public function upgrade_settings(context $oldcontext, stdClass $oldassignment, & $log) {
global $DB;
if ($oldassignment->assignmenttype == 'uploadsingle') {
$this->set_config('maxfilesubmissions', 1);
$this->set_config('maxsubmissionsizebytes', $oldassignment->maxbytes);
return true;
} else if ($oldassignment->assignmenttype == 'upload') {
$this->set_config('maxfilesubmissions', $oldassignment->var1);
$this->set_config('maxsubmissionsizebytes', $oldassignment->maxbytes);
// Advanced file upload uses a different setting to do the same thing.
$DB->set_field('assign',
'submissiondrafts',
$oldassignment->var4,
array('id'=>$this->assignment->get_instance()->id));
// Convert advanced file upload "hide description before due date" setting.
$alwaysshow = 0;
if (!$oldassignment->var3) {
$alwaysshow = 1;
}
$DB->set_field('assign',
'alwaysshowdescription',
$alwaysshow,
array('id'=>$this->assignment->get_instance()->id));
return true;
}
}
/**
* Upgrade the submission from the old assignment to the new one
*
* @param context $oldcontext The context of the old assignment
* @param stdClass $oldassignment The data record for the old oldassignment
* @param stdClass $oldsubmission The data record for the old submission
* @param stdClass $submission The data record for the new submission
* @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 $submission,
& $log) {
global $DB;
$filesubmission = new stdClass();
$filesubmission->numfiles = $oldsubmission->numfiles;
$filesubmission->submission = $submission->id;
$filesubmission->assignment = $this->assignment->get_instance()->id;
if (!$DB->insert_record('assignsubmission_file', $filesubmission) > 0) {
$log .= get_string('couldnotconvertsubmission', 'mod_assign', $submission->userid);
return false;
}
// Now copy the area files.
$this->assignment->copy_area_files_for_upgrade($oldcontext->id,
'mod_assignment',
'submission',
$oldsubmission->id,
$this->assignment->get_context()->id,
'assignsubmission_file',
ASSIGNSUBMISSION_FILE_FILEAREA,
$submission->id);
return true;
}
/**
* The assignment has been deleted - cleanup
*
* @return bool
*/
public function delete_instance() {
global $DB;
// Will throw exception on failure.
$DB->delete_records('assignsubmission_file',
array('assignment'=>$this->assignment->get_instance()->id));
return true;
}
/**
* Return true if there are no submission files
* @param stdClass $submission
*/
public function is_empty(stdClass $submission) {
return $this->count_files($submission->id, ASSIGNSUBMISSION_FILE_FILEAREA) == 0;
}
/**
* Determine if a submission is empty
*
* This is distinct from is_empty in that it is intended to be used to
* determine if a submission made before saving is empty.
*
* @param stdClass $data The submission data
* @return bool
*/
public function submission_is_empty(stdClass $data) {
global $USER;
$fs = get_file_storage();
// Get a count of all the draft files, excluding any directories.
$files = $fs->get_area_files(context_user::instance($USER->id)->id,
'user',
'draft',
$data->files_filemanager,
'id',
false);
return count($files) == 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(ASSIGNSUBMISSION_FILE_FILEAREA=>$this->get_name());
}
/**
* Copy the student's submission from a previous submission. Used when a student opts to base their resubmission
* on the last submission.
* @param stdClass $sourcesubmission
* @param stdClass $destsubmission
*/
public function copy_submission(stdClass $sourcesubmission, stdClass $destsubmission) {
global $DB;
// Copy the files across.
$contextid = $this->assignment->get_context()->id;
$fs = get_file_storage();
$files = $fs->get_area_files($contextid,
'assignsubmission_file',
ASSIGNSUBMISSION_FILE_FILEAREA,
$sourcesubmission->id,
'id',
false);
foreach ($files as $file) {
$fieldupdates = array('itemid' => $destsubmission->id);
$fs->create_file_from_storedfile($fieldupdates, $file);
}
// Copy the assignsubmission_file record.
if ($filesubmission = $this->get_file_submission($sourcesubmission->id)) {
unset($filesubmission->id);
$filesubmission->submission = $destsubmission->id;
$DB->insert_record('assignsubmission_file', $filesubmission);
}
return true;
}
/**
* Return a description of external params suitable for uploading a file submission from a webservice.
*
* @return \core_external\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 submission.',
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() {
global $CFG;
$configs = $this->get_config();
// Get a size in bytes.
if ($configs->maxsubmissionsizebytes == 0) {
$configs->maxsubmissionsizebytes = get_max_upload_file_size($CFG->maxbytes, $this->assignment->get_course()->maxbytes,
get_config('assignsubmission_file', 'maxbytes'));
}
return (array) $configs;
}
/**
* Get the type sets configured for this assignment.
*
* @return array('groupname', 'mime/type', ...)
*/
private function get_configured_typesets() {
$typeslist = (string)$this->get_config('filetypeslist');
$util = new \core_form\filetypes_util();
$sets = $util->normalize_file_types($typeslist);
return $sets;
}
/**
* Determine if the plugin allows image file conversion
* @return bool
*/
public function allow_image_conversion() {
return true;
}
}
+51
View File
@@ -0,0 +1,51 @@
<?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 assignsubmission_file
* @copyright 2012 NetSpot {@link http://www.netspot.com.au}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// Note: This is on by default.
$settings->add(new admin_setting_configcheckbox('assignsubmission_file/default',
new lang_string('default', 'assignsubmission_file'),
new lang_string('default_help', 'assignsubmission_file'), 1));
$settings->add(new admin_setting_configtext('assignsubmission_file/maxfiles',
new lang_string('maxfiles', 'assignsubmission_file'),
new lang_string('maxfiles_help', 'assignsubmission_file'), 20, PARAM_INT));
$settings->add(new admin_setting_filetypes('assignsubmission_file/filetypes',
new lang_string('defaultacceptedfiletypes', 'assignsubmission_file'),
new lang_string('acceptedfiletypes_help', 'assignsubmission_file'), ''));
if (isset($CFG->maxbytes)) {
$name = new lang_string('maximumsubmissionsize', 'assignsubmission_file');
$description = new lang_string('configmaxbytes', 'assignsubmission_file');
$maxbytes = get_config('assignsubmission_file', 'maxbytes');
$element = new admin_setting_configselect('assignsubmission_file/maxbytes',
$name,
$description,
$CFG->maxbytes,
get_max_upload_sizes($CFG->maxbytes, 0, 0, $maxbytes));
$settings->add($element);
}
@@ -0,0 +1,75 @@
@mod @mod_assign @assignsubmission_file
Feature: In an assignment, limit submittable file types
In order to constrain student submissions for marking
As a teacher
I need to limit the submittable file types
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 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following config values are set as admin:
| filetypes | image/png;spreadsheet | assignsubmission_file |
@javascript
Scenario: File types validation for an assignment
Given the following "activities" exist:
| activity | course | name | duedate | assignsubmission_onlinetext_enabled | assignsubmission_file_enabled | assignsubmission_file_maxfiles | assignsubmission_file_maxsizebytes |
| assign | C1 | Test assignment name | 1388534400 | 0 | 1 | 1 | 0 |
And I am on the "Test assignment name" Activity page logged in as teacher1
And I navigate to "Settings" in current page administration
When I set the field "Accepted file types" to "image/png;doesntexist;.anything;unreal/mimetype;nodot"
And I press "Save and display"
And I should see "Unknown file types: .doesntexist, .anything, unreal/mimetype, .nodot"
And I set the field "Accepted file types" to "image/png;spreadsheet"
And I press "Save and display"
And I navigate to "Settings" in current page administration
And the field "Accepted file types" matches value "image/png,spreadsheet"
And I set the field "Accepted file types" to ""
And I press "Choose"
And I set the field "Image files" to "1"
And I press "Save changes"
And I press "Save and display"
And I navigate to "Settings" in current page administration
Then the field "Accepted file types" matches value "image"
@javascript @_file_upload
Scenario: Uploading permitted file types for an assignment
Given the following "activities" exist:
| activity | course | name | duedate | assignsubmission_onlinetext_enabled | assignsubmission_file_enabled | assignsubmission_file_maxfiles | assignsubmission_file_maxsizebytes | assignsubmission_file_filetypes |
| assign | C1 | Test assignment name | 1388534400 | 0 | 1 | 3 | 0 | image/png,spreadsheet,.xml,.txt |
And I am on the "Test assignment name" Activity page logged in as student1
When I press "Add submission"
And I should see "Accepted file types"
And I should see "Image (PNG)"
And I should see "Spreadsheet files"
And I should see "Text file"
And I upload "lib/tests/fixtures/gd-logo.png" file to "File submissions" filemanager
And I upload "lib/tests/fixtures/tabfile.csv" file to "File submissions" filemanager
And I upload "lib/tests/fixtures/empty.txt" file to "File submissions" filemanager
And I press "Save changes"
Then "gd-logo.png" "link" should exist
And "tabfile.csv" "link" should exist
And "empty.txt" "link" should exist
@javascript @_file_upload
Scenario: No filetypes allows all
Given the following "activities" exist:
| activity | course | name | duedate | assignsubmission_onlinetext_enabled | assignsubmission_file_enabled | assignsubmission_file_maxfiles | assignsubmission_file_maxsizebytes | assignsubmission_file_filetypes |
| assign | C1 | Test assignment name | 1388534400 | 0 | 1 | 2 | 0 | |
And I am on the "Test assignment name" Activity page logged in as student1
When I press "Add submission"
And I should not see "Accepted file types"
And I upload "lib/tests/fixtures/gd-logo.png" file to "File submissions" filemanager
And I upload "lib/tests/fixtures/tabfile.csv" file to "File submissions" filemanager
And I press "Save changes"
Then "gd-logo.png" "link" should exist
And "tabfile.csv" "link" should exist
@@ -0,0 +1,213 @@
<?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/>.
/**
* Contains the event tests for the plugin.
*
* @package assignsubmission_file
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace assignsubmission_file\event;
use mod_assign_test_generator;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/mod/assign/tests/generator.php');
class events_test extends \advanced_testcase {
// Use the generator helper.
use mod_assign_test_generator;
/**
* Test that the assessable_uploaded event is fired when a file submission has been made.
*/
public function test_assessable_uploaded(): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
$assign = $this->create_instance($course);
$context = $assign->get_context();
$cm = $assign->get_course_module();
$this->setUser($student->id);
$submission = $assign->get_user_submission($student->id, true);
$fs = get_file_storage();
$dummy = (object) array(
'contextid' => $context->id,
'component' => 'assignsubmission_file',
'filearea' => ASSIGNSUBMISSION_FILE_FILEAREA,
'itemid' => $submission->id,
'filepath' => '/',
'filename' => 'myassignmnent.pdf'
);
$fi = $fs->create_file_from_string($dummy, 'Content of ' . $dummy->filename);
$dummy = (object) array(
'contextid' => $context->id,
'component' => 'assignsubmission_file',
'filearea' => ASSIGNSUBMISSION_FILE_FILEAREA,
'itemid' => $submission->id,
'filepath' => '/',
'filename' => 'myassignmnent.png'
);
$fi2 = $fs->create_file_from_string($dummy, 'Content of ' . $dummy->filename);
$files = $fs->get_area_files($context->id, 'assignsubmission_file', ASSIGNSUBMISSION_FILE_FILEAREA,
$submission->id, 'id', false);
$data = new \stdClass();
$plugin = $assign->get_submission_plugin_by_type('file');
$sink = $this->redirectEvents();
$plugin->save($submission, $data);
$events = $sink->get_events();
$this->assertCount(2, $events);
$event = reset($events);
$this->assertInstanceOf('\assignsubmission_file\event\assessable_uploaded', $event);
$this->assertEquals($context->id, $event->contextid);
$this->assertEquals($submission->id, $event->objectid);
$this->assertCount(2, $event->other['pathnamehashes']);
$this->assertEquals($fi->get_pathnamehash(), $event->other['pathnamehashes'][0]);
$this->assertEquals($fi2->get_pathnamehash(), $event->other['pathnamehashes'][1]);
$expected = new \stdClass();
$expected->modulename = 'assign';
$expected->cmid = $cm->id;
$expected->itemid = $submission->id;
$expected->courseid = $course->id;
$expected->userid = $student->id;
$expected->file = $files;
$expected->files = $files;
$expected->pathnamehashes = array($fi->get_pathnamehash(), $fi2->get_pathnamehash());
$this->assertEventContextNotUsed($event);
}
/**
* Test that the submission_created event is fired when a file submission is saved.
*/
public function test_submission_created(): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
$assign = $this->create_instance($course);
$context = $assign->get_context();
$this->setUser($student->id);
$submission = $assign->get_user_submission($student->id, true);
$fs = get_file_storage();
$dummy = (object) array(
'contextid' => $context->id,
'component' => 'assignsubmission_file',
'filearea' => ASSIGNSUBMISSION_FILE_FILEAREA,
'itemid' => $submission->id,
'filepath' => '/',
'filename' => 'myassignmnent.pdf'
);
$fi = $fs->create_file_from_string($dummy, 'Content of ' . $dummy->filename);
$dummy = (object) array(
'contextid' => $context->id,
'component' => 'assignsubmission_file',
'filearea' => ASSIGNSUBMISSION_FILE_FILEAREA,
'itemid' => $submission->id,
'filepath' => '/',
'filename' => 'myassignmnent.png'
);
$fi2 = $fs->create_file_from_string($dummy, 'Content of ' . $dummy->filename);
$files = $fs->get_area_files($context->id, 'assignsubmission_file', ASSIGNSUBMISSION_FILE_FILEAREA,
$submission->id, 'id', false);
$data = new \stdClass();
$plugin = $assign->get_submission_plugin_by_type('file');
$sink = $this->redirectEvents();
$plugin->save($submission, $data);
$events = $sink->get_events();
$this->assertCount(2, $events);
// We want to test the last event fired.
$event = $events[1];
$this->assertInstanceOf('\assignsubmission_file\event\submission_created', $event);
$this->assertEquals($context->id, $event->contextid);
$this->assertEquals($course->id, $event->courseid);
$this->assertEquals($submission->id, $event->other['submissionid']);
$this->assertEquals($submission->attemptnumber, $event->other['submissionattempt']);
$this->assertEquals($submission->status, $event->other['submissionstatus']);
$this->assertEquals($submission->userid, $event->relateduserid);
}
/**
* Test that the submission_updated event is fired when a file submission is saved when an existing submission already exists.
*/
public function test_submission_updated(): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
$assign = $this->create_instance($course);
$context = $assign->get_context();
$this->setUser($student->id);
$submission = $assign->get_user_submission($student->id, true);
$fs = get_file_storage();
$dummy = (object) array(
'contextid' => $context->id,
'component' => 'assignsubmission_file',
'filearea' => ASSIGNSUBMISSION_FILE_FILEAREA,
'itemid' => $submission->id,
'filepath' => '/',
'filename' => 'myassignmnent.pdf'
);
$fi = $fs->create_file_from_string($dummy, 'Content of ' . $dummy->filename);
$dummy = (object) array(
'contextid' => $context->id,
'component' => 'assignsubmission_file',
'filearea' => ASSIGNSUBMISSION_FILE_FILEAREA,
'itemid' => $submission->id,
'filepath' => '/',
'filename' => 'myassignmnent.png'
);
$fi2 = $fs->create_file_from_string($dummy, 'Content of ' . $dummy->filename);
$files = $fs->get_area_files($context->id, 'assignsubmission_file', ASSIGNSUBMISSION_FILE_FILEAREA,
$submission->id, 'id', false);
$data = new \stdClass();
$plugin = $assign->get_submission_plugin_by_type('file');
$sink = $this->redirectEvents();
// Create a submission.
$plugin->save($submission, $data);
// Update a submission.
$plugin->save($submission, $data);
$events = $sink->get_events();
$this->assertCount(4, $events);
// We want to test the last event fired.
$event = $events[3];
$this->assertInstanceOf('\assignsubmission_file\event\submission_updated', $event);
$this->assertEquals($context->id, $event->contextid);
$this->assertEquals($course->id, $event->courseid);
$this->assertEquals($submission->id, $event->other['submissionid']);
$this->assertEquals($submission->attemptnumber, $event->other['submissionattempt']);
$this->assertEquals($submission->status, $event->other['submissionstatus']);
$this->assertEquals($submission->userid, $event->relateduserid);
}
}
@@ -0,0 +1,69 @@
<?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/>.
require_once("{$CFG->dirroot}/mod/assign/tests/generator/assignsubmission_subplugin_generator.php");
/**
* Online Text assignment submission subplugin data generator.
*
* @package assignsubmission_file
* @category test
* @copyright 2021 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class assignsubmission_file_generator extends assignsubmission_subplugin_generator {
/**
* Add submission data in the correct format for a call to `assign::save_submission()` from a table containing
* submission data for a single activity.
*
* Data should be added to the $submission object passed into the function.
*
* @param stdClass $submission The submission record to be modified
* @param assign $assign The assignment being submitted to
* @param array $data The data received
*/
public function add_submission_data(stdClass $submission, assign $assign, array $data): void {
global $CFG;
if (array_key_exists('file', $data)) {
$files = explode(',', $data['file']);
$itemid = file_get_unused_draft_itemid();
$fs = get_file_storage();
foreach ($files as $filepath) {
// All paths are relative to $CFG->dirroot.
$filepath = trim($filepath);
$filepath = "{$CFG->dirroot}/{$filepath}";
$filename = basename($filepath);
$fs->create_file_from_pathname((object) [
'itemid' => $itemid,
'contextid' => context_user::instance($submission->userid)->id,
'component' => 'user',
'filearea' => 'draft',
'filepath' => '/',
'filename' => $filename,
], $filepath);
}
$submission->files_filemanager = $itemid;
$submission->file_editor = [
'itemid' => $itemid,
];
}
}
}
@@ -0,0 +1,230 @@
<?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 assignsubmission_file;
use mod_assign_test_generator;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/mod/assign/tests/generator.php');
/**
* Unit tests for mod/assign/submission/file/locallib.php
*
* @package assignsubmission_file
* @copyright 2016 Cameron Ball
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
final class locallib_test extends \advanced_testcase {
// Use the generator helper.
use mod_assign_test_generator;
/**
* Test submission_is_empty
*
* @dataProvider submission_is_empty_testcases
* @param string $data The file submission data
* @param bool $expected The expected return value
*/
public function test_submission_is_empty($data, $expected): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
$assign = $this->create_instance($course, [
'assignsubmission_file_enabled' => 1,
'assignsubmission_file_maxfiles' => 12,
'assignsubmission_file_maxsizebytes' => 10,
]);
$this->setUser($student->id);
$itemid = file_get_unused_draft_itemid();
$submission = (object)['files_filemanager' => $itemid];
$plugin = $assign->get_submission_plugin_by_type('file');
if ($data) {
$data += ['contextid' => \context_user::instance($student->id)->id, 'itemid' => $itemid];
$fs = get_file_storage();
$fs->create_file_from_string((object)$data, 'Content of ' . $data['filename']);
}
$result = $plugin->submission_is_empty($submission);
$this->assertTrue($result === $expected);
}
/**
* Test that an empty directory is is not detected as a valid submission by submission_is_empty.
*/
public function test_submission_is_empty_directory_only(): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
$assign = $this->create_instance($course, [
'assignsubmission_file_enabled' => 1,
'assignsubmission_file_maxfiles' => 12,
'assignsubmission_file_maxsizebytes' => 10,
]);
$this->setUser($student->id);
$itemid = file_get_unused_draft_itemid();
$submission = (object)['files_filemanager' => $itemid];
$plugin = $assign->get_submission_plugin_by_type('file');
$fs = get_file_storage();
$fs->create_directory(
\context_user::instance($student->id)->id,
'user',
'draft',
$itemid,
'/subdirectory/'
);
$this->assertTrue($plugin->submission_is_empty($submission));
}
/**
* Test new_submission_empty
*
* @dataProvider submission_is_empty_testcases
* @param string $data The file submission data
* @param bool $expected The expected return value
*/
public function test_new_submission_empty($data, $expected): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
$assign = $this->create_instance($course, [
'assignsubmission_file_enabled' => 1,
'assignsubmission_file_maxfiles' => 12,
'assignsubmission_file_maxsizebytes' => 10,
]);
$this->setUser($student);
$itemid = file_get_unused_draft_itemid();
$submission = (object) ['files_filemanager' => $itemid];
if ($data) {
$data += ['contextid' => \context_user::instance($student->id)->id, 'itemid' => $itemid];
$fs = get_file_storage();
$fs->create_file_from_string((object)$data, 'Content of ' . $data['filename']);
}
$result = $assign->new_submission_empty($submission);
$this->assertTrue($result === $expected);
}
/**
* Test that an empty directory is is not detected as a valid submission by new_submission_is_empty.
*/
public function test_new_submission_empty_directory_only(): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
$assign = $this->create_instance($course, [
'assignsubmission_file_enabled' => 1,
'assignsubmission_file_maxfiles' => 12,
'assignsubmission_file_maxsizebytes' => 10,
]);
$this->setUser($student->id);
$itemid = file_get_unused_draft_itemid();
$submission = (object)['files_filemanager' => $itemid];
$plugin = $assign->get_submission_plugin_by_type('file');
$fs = get_file_storage();
$fs->create_directory(
\context_user::instance($student->id)->id,
'user',
'draft',
$itemid,
'/subdirectory/'
);
$this->assertTrue($assign->new_submission_empty($submission));
}
/**
* Dataprovider for the test_submission_is_empty testcase
*
* @return array of testcases
*/
public static function submission_is_empty_testcases(): array {
return [
'With file' => [
[
'component' => 'user',
'filearea' => 'draft',
'filepath' => '/',
'filename' => 'not_a_virus.exe'
],
false
],
'With file in directory' => [
[
'component' => 'user',
'filearea' => 'draft',
'filepath' => '/subdir/',
'filename' => 'not_a_virus.exe'
],
false
],
'Without file' => [null, true]
];
}
/**
* Test getting files from plugin submission
*/
public function test_get_files(): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
$assign = $this->create_instance($course, [
'assignsubmission_file_enabled' => 1,
'assignsubmission_file_maxfiles' => 2,
'assignsubmission_file_maxsizebytes' => 512,
]);
// Switch to student, create some dummy files, and submit data to plugin.
$this->setUser($student);
$submission = $assign->get_user_submission($student->id, true);
$filerecord = [
'contextid' => $assign->get_context()->id,
'component' => 'assignsubmission_file',
'filearea' => ASSIGNSUBMISSION_FILE_FILEAREA,
'itemid' => $submission->id,
'filepath' => '/',
];
get_file_storage()->create_file_from_string($filerecord + ['filename' => 'File 1.txt'], 'File One');
get_file_storage()->create_file_from_string($filerecord + ['filename' => 'File 2.txt'], 'File Two');
/** @var \assign_submission_file $plugin */
$plugin = $assign->get_submission_plugin_by_type('file');
$plugin->save($submission, (object) []);
// Ensure we retrieve back list of file submissions, deterministically ordered.
$files = $plugin->get_files($submission, $student);
$this->assertSame([
'/File 1.txt' => 'File 1.txt',
'/File 2.txt' => 'File 2.txt',
], array_map(fn(\stored_file $f) => $f->get_filename(), $files));
}
}
@@ -0,0 +1,249 @@
<?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 assignsubmission_file.
*
* @package assignsubmission_file
* @copyright 2018 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace assignsubmission_file\privacy;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/mod/assign/tests/privacy/provider_test.php');
/**
* Unit tests for mod/assign/submission/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 string $filename filename for the file submission
* @return array Submission plugin object and the submission object.
*/
protected function create_file_submission($assign, $student, $filename) {
global $CFG;
// Create a file submission with the test pdf.
$submission = $assign->get_user_submission($student->id, true);
$this->setUser($student->id);
$fs = get_file_storage();
$pdfsubmission = (object) array(
'contextid' => $assign->get_context()->id,
'component' => 'assignsubmission_file',
'filearea' => ASSIGNSUBMISSION_FILE_FILEAREA,
'itemid' => $submission->id,
'filepath' => '/',
'filename' => $filename
);
$sourcefile = $CFG->dirroot.'/mod/assign/feedback/editpdf/tests/fixtures/submission.pdf';
$fi = $fs->create_file_from_pathname($pdfsubmission, $sourcefile);
$data = new \stdClass();
$plugin = $assign->get_submission_plugin_by_type('file');
$plugin->save($submission, $data);
return [$plugin, $submission];
}
/**
* Quick test to make sure that get_metadata returns something.
*/
public function test_get_metadata(): void {
$collection = new \core_privacy\local\metadata\collection('assignsubmission_file');
$collection = \assignsubmission_file\privacy\provider::get_metadata($collection);
$this->assertNotEmpty($collection);
}
/**
* Test that submission files are exported for a user.
*/
public function test_export_submission_user_data(): void {
$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();
$studentfilename = 'user1file.pdf';
list($plugin, $submission) = $this->create_file_submission($assign, $user1, $studentfilename);
$writer = \core_privacy\local\request\writer::with_context($context);
$this->assertFalse($writer->has_any_data());
// The student should have a file submission.
$exportdata = new \mod_assign\privacy\assign_plugin_request_data($context, $assign, $submission, ['Attempt 1']);
\assignsubmission_file\privacy\provider::export_submission_user_data($exportdata);
// print_object($writer);
$storedfile = $writer->get_files(['Attempt 1'])['user1file.pdf'];
$this->assertInstanceOf('stored_file', $storedfile);
$this->assertEquals($studentfilename, $storedfile->get_filename());
}
/**
* Test that all submission files are deleted for this context.
*/
public function test_delete_submission_for_context(): void {
$this->resetAfterTest();
// Create course, assignment, submission, and then a feedback comment.
$course = $this->getDataGenerator()->create_course();
// Student.
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student');
$this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student');
$assign = $this->create_instance(['course' => $course]);
$context = $assign->get_context();
$studentfilename = 'user1file.pdf';
list($plugin, $submission) = $this->create_file_submission($assign, $user1, $studentfilename);
$student2filename = 'user2file.pdf';
list($plugin2, $submission2) = $this->create_file_submission($assign, $user2, $studentfilename);
// Only need the context and assign object in this plugin for this operation.
$requestdata = new \mod_assign\privacy\assign_plugin_request_data($context, $assign);
\assignsubmission_file\privacy\provider::delete_submission_for_context($requestdata);
// This checks that there are no files in this submission.
$this->assertTrue($plugin->is_empty($submission));
$this->assertTrue($plugin2->is_empty($submission2));
}
/**
* Test that the comments for a user are deleted.
*/
public function test_delete_submission_for_userid(): void {
$this->resetAfterTest();
// Create course, assignment, submission, and then a feedback comment.
$course = $this->getDataGenerator()->create_course();
// Student.
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student');
$this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student');
$assign = $this->create_instance(['course' => $course]);
$context = $assign->get_context();
$studentfilename = 'user1file.pdf';
list($plugin, $submission) = $this->create_file_submission($assign, $user1, $studentfilename);
$student2filename = 'user2file.pdf';
list($plugin2, $submission2) = $this->create_file_submission($assign, $user2, $studentfilename);
// Only need the context and assign object in this plugin for this operation.
$requestdata = new \mod_assign\privacy\assign_plugin_request_data($context, $assign, $submission, [], $user1);
\assignsubmission_file\privacy\provider::delete_submission_for_userid($requestdata);
// This checks that there are no files in this submission.
$this->assertTrue($plugin->is_empty($submission));
// There should be files here.
$this->assertFalse($plugin2->is_empty($submission2));
}
/**
* Test deletion of bulk submissions for a context.
*/
public function test_delete_submissions(): void {
global $DB;
$this->resetAfterTest();
// Create course, assignment, submission, and then a feedback comment.
$course = $this->getDataGenerator()->create_course();
// Student.
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$user3 = $this->getDataGenerator()->create_user();
$user4 = $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');
$assign1 = $this->create_instance(['course' => $course]);
$assign2 = $this->create_instance(['course' => $course]);
$context1 = $assign1->get_context();
$context2 = $assign2->get_context();
$student1filename = 'user1file.pdf';
list($plugin1, $submission1) = $this->create_file_submission($assign1, $user1, $student1filename);
$student2filename = 'user2file.pdf';
list($plugin2, $submission2) = $this->create_file_submission($assign1, $user2, $student2filename);
$student3filename = 'user3file.pdf';
list($plugin3, $submission3) = $this->create_file_submission($assign1, $user3, $student3filename);
$student4filename = 'user4file.pdf';
list($plugin4, $submission4) = $this->create_file_submission($assign2, $user4, $student4filename);
$student5filename = 'user5file.pdf';
list($plugin5, $submission5) = $this->create_file_submission($assign2, $user3, $student5filename);
$submissionids = [
$submission1->id,
$submission3->id
];
$userids = [
$user1->id,
$user3->id
];
$data = $DB->get_records('files', ['contextid' => $context1->id, 'component' => 'assignsubmission_file']);
$this->assertCount(6, $data);
$data = $DB->get_records('assignsubmission_file', ['assignment' => $assign1->get_instance()->id]);
$this->assertCount(3, $data);
// Records in the second assignment (not being touched).
$data = $DB->get_records('assignsubmission_file', ['assignment' => $assign2->get_instance()->id]);
$this->assertCount(2, $data);
$deletedata = new \mod_assign\privacy\assign_plugin_request_data($context1, $assign1);
$deletedata->set_userids($userids);
$deletedata->populate_submissions_and_grades();
\assignsubmission_file\privacy\provider::delete_submissions($deletedata);
$data = $DB->get_records('files', ['contextid' => $context1->id, 'component' => 'assignsubmission_file']);
$this->assertCount(2, $data);
// Submission 1 and 3 have been removed. We should be left with submission2.
$data = $DB->get_records('assignsubmission_file', ['assignment' => $assign1->get_instance()->id]);
$this->assertCount(1, $data);
// This should be untouched.
$data = $DB->get_records('assignsubmission_file', ['assignment' => $assign2->get_instance()->id]);
$this->assertCount(2, $data);
}
}
+29
View File
@@ -0,0 +1,29 @@
<?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 file submission plugin
*
* @package assignsubmission_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 = 2024042200;
$plugin->requires = 2024041600;
$plugin->component = 'assignsubmission_file';
@@ -0,0 +1,66 @@
<?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 class for backup of this submission plugin
*
* @package assignsubmission_onlinetext
* @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 onlinetext submissions
*
* This just adds its filearea to the annotations and records the submissiontext and format
*
* @package assignsubmission_onlinetext
* @copyright 2012 NetSpot {@link http://www.netspot.com.au}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class backup_assignsubmission_onlinetext_subplugin extends backup_subplugin {
/**
* Returns the subplugin information to attach to submission element
*
* @return backup_subplugin_element
*/
protected function define_submission_subplugin_structure() {
// Create XML elements.
$subplugin = $this->get_subplugin_element();
$subpluginwrapper = new backup_nested_element($this->get_recommended_name());
$subpluginelement = new backup_nested_element('submission_onlinetext',
null,
array('onlinetext', 'onlineformat', 'submission'));
// Connect XML elements into the tree.
$subplugin->add_child($subpluginwrapper);
$subpluginwrapper->add_child($subpluginelement);
// Set source to populate the data.
$subpluginelement->set_source_table('assignsubmission_onlinetext',
array('submission' => backup::VAR_PARENTID));
$subpluginelement->annotate_files('assignsubmission_onlinetext',
'submissions_onlinetext',
'submission');
return $subplugin;
}
}
@@ -0,0 +1,74 @@
<?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 class for restore of this submission plugin
*
* @package assignsubmission_onlinetext
* @copyright 2012 NetSpot {@link http://www.netspot.com.au}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Restore subplugin class.
*
* Provides the necessary information needed to restore
* one assign_submission subplugin.
*
* @package assignsubmission_onlinetext
* @copyright 2012 NetSpot {@link http://www.netspot.com.au}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class restore_assignsubmission_onlinetext_subplugin extends restore_subplugin {
/**
* Returns array the paths to be handled by the subplugin at assignment level
* @return array
*/
protected function define_submission_subplugin_structure() {
$paths = array();
$elename = $this->get_namefor('submission');
// We used get_recommended_name() so this works.
$elepath = $this->get_pathfor('/submission_onlinetext');
$paths[] = new restore_path_element($elename, $elepath);
return $paths;
}
/**
* Processes one assignsubmission_onlinetext element
*
* @param mixed $data
*/
public function process_assignsubmission_onlinetext_submission($data) {
global $DB;
$data = (object)$data;
$data->assignment = $this->get_new_parentid('assign');
$oldsubmissionid = $data->submission;
// The mapping is set in the restore for the core assign activity
// when a submission node is processed.
$data->submission = $this->get_mappingid('submission', $data->submission);
$DB->insert_record('assignsubmission_onlinetext', $data);
$this->add_related_files('assignsubmission_onlinetext', 'submissions_onlinetext', 'submission', null, $oldsubmissionid);
}
}
@@ -0,0 +1,86 @@
<?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/>.
/**
* The assignsubmission_onlinetext assessable uploaded event.
*
* @package assignsubmission_onlinetext
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace assignsubmission_onlinetext\event;
defined('MOODLE_INTERNAL') || die();
/**
* The assignsubmission_onlinetext assessable uploaded event class.
*
* @property-read array $other {
* Extra information about event.
*
* - string format: (optional) content format.
* }
*
* @package assignsubmission_onlinetext
* @since Moodle 2.6
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class assessable_uploaded extends \core\event\assessable_uploaded {
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' has saved an online text submission with id '$this->objectid' " .
"in the assignment activity with course module id '$this->contextinstanceid'.";
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventassessableuploaded', 'assignsubmission_onlinetext');
}
/**
* Get URL related to the action.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/assign/view.php', array('id' => $this->contextinstanceid));
}
/**
* Init method.
*
* @return void
*/
protected function init() {
parent::init();
$this->data['objecttable'] = 'assign_submission';
}
public static function get_objectid_mapping() {
return array('db' => 'assign_submission', 'restore' => 'submission');
}
}
@@ -0,0 +1,88 @@
<?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/>.
/**
* The assignsubmission_onlinetext submission_created event.
*
* @package assignsubmission_onlinetext
* @copyright 2014 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace assignsubmission_onlinetext\event;
defined('MOODLE_INTERNAL') || die();
/**
* The assignsubmission_onlinetext submission_created event class.
*
* @property-read array $other {
* Extra information about the event.
*
* - int onlinetextwordcount: Word count of the online text submission.
* }
*
* @package assignsubmission_onlinetext
* @since Moodle 2.7
* @copyright 2014 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class submission_created extends \mod_assign\event\submission_created {
/**
* Init method.
*/
protected function init() {
parent::init();
$this->data['objecttable'] = 'assignsubmission_onlinetext';
}
/**
* Returns non-localised description of what happened.
*
* @return string
*/
public function get_description() {
$descriptionstring = "The user with id '$this->userid' created an online text submission with " .
"'{$this->other['onlinetextwordcount']}' words in the assignment with course module id " .
"'$this->contextinstanceid'";
if (!empty($this->other['groupid'])) {
$descriptionstring .= " for the group with id '{$this->other['groupid']}'.";
} else {
$descriptionstring .= ".";
}
return $descriptionstring;
}
/**
* Custom validation.
*
* @throws \coding_exception
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->other['onlinetextwordcount'])) {
throw new \coding_exception('The \'onlinetextwordcount\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
// No mapping available for 'assignsubmission_onlinetext'.
return array('db' => 'assignsubmission_onlinetext', 'restore' => \core\event\base::NOT_MAPPED);
}
}
@@ -0,0 +1,88 @@
<?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/>.
/**
* The assignsubmission_onlinetext submission_updated event.
*
* @package assignsubmission_onlinetext
* @copyright 2014 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace assignsubmission_onlinetext\event;
defined('MOODLE_INTERNAL') || die();
/**
* The assignsubmission_onlinetext submission_updated event class.
*
* @property-read array $other {
* Extra information about the event.
*
* - int onlinetextwordcount: Word count of the online text submission.
* }
*
* @package assignsubmission_onlinetext
* @since Moodle 2.7
* @copyright 2014 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class submission_updated extends \mod_assign\event\submission_updated {
/**
* Init method.
*/
protected function init() {
parent::init();
$this->data['objecttable'] = 'assignsubmission_onlinetext';
}
/**
* Returns non-localised description of what happened.
*
* @return string
*/
public function get_description() {
$descriptionstring = "The user with id '$this->userid' updated an online text submission with " .
"'{$this->other['onlinetextwordcount']}' words in the assignment with course module id " .
"'$this->contextinstanceid'";
if (!empty($this->other['groupid'])) {
$descriptionstring .= " for the group with id '{$this->other['groupid']}'.";
} else {
$descriptionstring .= ".";
}
return $descriptionstring;
}
/**
* Custom validation.
*
* @throws \coding_exception
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->other['onlinetextwordcount'])) {
throw new \coding_exception('The \'onlinetextwordcount\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
// No mapping available for 'assignsubmission_onlinetext'.
return array('db' => 'assignsubmission_onlinetext', 'restore' => \core\event\base::NOT_MAPPED);
}
}
@@ -0,0 +1,201 @@
<?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 assignsubmission_onlinetext
* @copyright 2018 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace assignsubmission_onlinetext\privacy;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/mod/assign/locallib.php');
use \core_privacy\local\metadata\collection;
use \core_privacy\local\request\writer;
use \core_privacy\local\request\contextlist;
use \mod_assign\privacy\assign_plugin_request_data;
/**
* Privacy class for requesting user data.
*
* @package assignsubmission_onlinetext
* @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\assignsubmission_provider,
\mod_assign\privacy\assignsubmission_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 {
$detail = [
'assignment' => 'privacy:metadata:assignmentid',
'submission' => 'privacy:metadata:submissionpurpose',
'onlinetext' => 'privacy:metadata:textpurpose'
];
$collection->add_database_table('assignsubmission_onlinetext', $detail, 'privacy:metadata:tablepurpose');
$collection->link_subsystem('core_files', 'privacy:metadata:filepurpose');
return $collection;
}
/**
* This is covered by mod_assign provider and the query on assign_submissions.
*
* @param int $userid The user ID that we are finding contexts for.
* @param contextlist $contextlist A context list to add sql and params to for contexts.
*/
public static function get_context_for_userid_within_submission(int $userid, contextlist $contextlist) {
// This is already fetched from mod_assign.
}
/**
* This is also covered by the mod_assign provider and it's queries.
*
* @param \mod_assign\privacy\useridlist $useridlist An object for obtaining user IDs of students.
*/
public static function get_student_user_ids(\mod_assign\privacy\useridlist $useridlist) {
// No need.
}
/**
* If you have tables that contain userids and you can generate entries in your tables without creating an
* entry in the assign_submission 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_submission_user_data(assign_plugin_request_data $exportdata) {
// We currently don't show submissions to teachers when exporting their data.
if ($exportdata->get_user() != null) {
return null;
}
// Retrieve text for this submission.
$assign = $exportdata->get_assign();
$plugin = $assign->get_plugin_by_type('assignsubmission', 'onlinetext');
$submission = $exportdata->get_pluginobject();
$editortext = $plugin->get_editor_text('onlinetext', $submission->id);
$context = $exportdata->get_context();
if (!empty($editortext)) {
$submissiontext = new \stdClass();
$currentpath = $exportdata->get_subcontext();
$currentpath[] = get_string('privacy:path', 'assignsubmission_onlinetext');
$submissiontext->text = writer::with_context($context)->rewrite_pluginfile_urls($currentpath,
'assignsubmission_onlinetext', 'submissions_onlinetext', $submission->id, $editortext);
writer::with_context($context)
->export_area_files($currentpath, 'assignsubmission_onlinetext', 'submissions_onlinetext', $submission->id)
// Add the text to the exporter.
->export_data($currentpath, $submissiontext);
// Handle plagiarism data.
$coursecontext = $context->get_course_context();
$userid = $submission->userid;
\core_plagiarism\privacy\provider::export_plagiarism_user_data($userid, $context, $currentpath, [
'cmid' => $context->instanceid,
'course' => $coursecontext->instanceid,
'userid' => $userid,
'content' => $editortext,
'assignment' => $submission->assignment
]);
}
}
/**
* 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_submission_for_context(assign_plugin_request_data $requestdata) {
global $DB;
\core_plagiarism\privacy\provider::delete_plagiarism_for_context($requestdata->get_context());
// Delete related files.
$fs = get_file_storage();
$fs->delete_area_files($requestdata->get_context()->id, 'assignsubmission_onlinetext',
ASSIGNSUBMISSION_ONLINETEXT_FILEAREA);
// Delete the records in the table.
$DB->delete_records('assignsubmission_onlinetext', ['assignment' => $requestdata->get_assignid()]);
}
/**
* A call to this method should delete user data (where practicle) from the userid and context.
*
* @param assign_plugin_request_data $deletedata Details about the user and context to focus the deletion.
*/
public static function delete_submission_for_userid(assign_plugin_request_data $deletedata) {
global $DB;
\core_plagiarism\privacy\provider::delete_plagiarism_for_user($deletedata->get_user()->id, $deletedata->get_context());
$submissionid = $deletedata->get_pluginobject()->id;
// Delete related files.
$fs = get_file_storage();
$fs->delete_area_files($deletedata->get_context()->id, 'assignsubmission_onlinetext', ASSIGNSUBMISSION_ONLINETEXT_FILEAREA,
$submissionid);
// Delete the records in the table.
$DB->delete_records('assignsubmission_onlinetext', ['assignment' => $deletedata->get_assignid(),
'submission' => $submissionid]);
}
/**
* Deletes all submissions for the submission ids / userids provided in a context.
* assign_plugin_request_data contains:
* - context
* - assign object
* - submission ids (pluginids)
* - user ids
* @param assign_plugin_request_data $deletedata A class that contains the relevant information required for deletion.
*/
public static function delete_submissions(assign_plugin_request_data $deletedata) {
global $DB;
\core_plagiarism\privacy\provider::delete_plagiarism_for_users($deletedata->get_userids(), $deletedata->get_context());
if (empty($deletedata->get_submissionids())) {
return;
}
$fs = get_file_storage();
list($sql, $params) = $DB->get_in_or_equal($deletedata->get_submissionids(), SQL_PARAMS_NAMED);
$fs->delete_area_files_select($deletedata->get_context()->id,
'assignsubmission_onlinetext', ASSIGNSUBMISSION_ONLINETEXT_FILEAREA, $sql, $params);
$params['assignid'] = $deletedata->get_assignid();
$DB->delete_records_select('assignsubmission_onlinetext', "assignment = :assignid AND submission $sql", $params);
}
}
@@ -0,0 +1,29 @@
<?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 assignsubmission_onlinetext
* @copyright 2012 NetSpot {@link http://www.netspot.com.au}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$capabilities = array(
);
@@ -0,0 +1,43 @@
<?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 submission_onlinetext module.
*
* @package assignsubmission_onlinetext
* @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();
/**
* Code run after the assignsubmission_onlinetext module database tables have been created.
* Moves the plugin to the top of the list (of 3)
* @return bool
*/
function xmldb_assignsubmission_onlinetext_install() {
global $CFG;
// Set the correct initial order for the plugins.
require_once($CFG->dirroot . '/mod/assign/adminlib.php');
$pluginmanager = new assign_plugin_manager('assignsubmission');
$pluginmanager->move_plugin('onlinetext', 'up');
$pluginmanager->move_plugin('onlinetext', 'up');
return true;
}
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="mod/assign/submission/onlinetext/db" VERSION="20120423" COMMENT="XMLDB file for Moodle mod/assign/submission/onlinetext"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../../lib/xmldb/xmldb.xsd"
>
<TABLES>
<TABLE NAME="assignsubmission_onlinetext" COMMENT="Info about onlinetext submission">
<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="submission" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="onlinetext" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="The text for this online text submission."/>
<FIELD NAME="onlineformat" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The format for this online text submission."/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="The unique id for this onlinetext submission."/>
<KEY NAME="assignment" TYPE="foreign" FIELDS="assignment" REFTABLE="assign" REFFIELDS="id" COMMENT="The assignment instance this online text submission relates to."/>
<KEY NAME="submission" TYPE="foreign" FIELDS="submission" REFTABLE="assign_submission" REFFIELDS="id" COMMENT="The submission this online text submission relates to."/>
</KEYS>
</TABLE>
</TABLES>
</XMLDB>
@@ -0,0 +1,44 @@
<?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 install
*
* @package assignsubmission_onlinetext
* @copyright 2012 NetSpot {@link http://www.netspot.com.au}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Stub for upgrade code
* @param int $oldversion
* @return bool
*/
function xmldb_assignsubmission_onlinetext_upgrade($oldversion) {
// Automatically generated Moodle v4.1.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.2.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.3.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.4.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
@@ -0,0 +1,48 @@
<?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 'assignsubmission_onlinetext', language 'en'
*
* @package assignsubmission_onlinetext
* @copyright 2012 NetSpot {@link http://www.netspot.com.au}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['allowonlinetextsubmissions'] = 'Enabled';
$string['default'] = 'Enabled by default';
$string['default_help'] = 'If set, this submission method will be enabled by default for all new assignments.';
$string['enabled'] = 'Online text';
$string['enabled_help'] = 'If enabled, students are able to type rich text directly into an editor field for their submission.';
$string['eventassessableuploaded'] = 'An online text has been uploaded.';
$string['nosubmission'] = 'Nothing has been submitted for this assignment';
$string['onlinetext'] = 'Online text';
$string['onlinetextfilename'] = 'onlinetext.html';
$string['onlinetextsubmission'] = 'Allow online text submission';
$string['numwords'] = '({$a} words)';
$string['pluginname'] = 'Online text submissions';
$string['privacy:metadata:assignmentid'] = 'Assignment ID';
$string['privacy:metadata:filepurpose'] = 'Files that are embedded in the text submission.';
$string['privacy:metadata:submissionpurpose'] = 'The submission ID that links to submissions for the user.';
$string['privacy:metadata:tablepurpose'] = 'Stores the text submission for each attempt.';
$string['privacy:metadata:textpurpose'] = 'The actual text submitted for this attempt of the assignment.';
$string['privacy:path'] = 'Submission Text';
$string['wordlimit'] = 'Word limit';
$string['wordlimit_help'] = 'If online text submissions are enabled, this is the maximum number of words that each student will be allowed to submit.';
$string['wordlimitexceeded'] = 'The word limit for this assignment is {$a->limit} words and you are attempting to submit {$a->count} words. Please review your submission and try again.';
// Deprecated since Moodle 4.3.
$string['numwordsforlog'] = 'Submission word count: {$a} words';
@@ -0,0 +1 @@
numwordsforlog,assignsubmission_onlinetext
+89
View File
@@ -0,0 +1,89 @@
<?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 submission onlinetext plugin
*
* @package assignsubmission_onlinetext
* @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 submissions 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 assignsubmission_onlinetext_pluginfile($course,
$cm,
context $context,
$filearea,
$args,
$forcedownload,
array $options=array()) {
global $DB, $CFG;
if ($context->contextlevel != CONTEXT_MODULE) {
return false;
}
require_login($course, false, $cm);
$itemid = (int)array_shift($args);
$record = $DB->get_record('assign_submission',
array('id'=>$itemid),
'userid, assignment, groupid',
MUST_EXIST);
$userid = $record->userid;
$groupid = $record->groupid;
require_once($CFG->dirroot . '/mod/assign/locallib.php');
$assign = new assign($context, $cm, $course);
if ($assign->get_instance()->id != $record->assignment) {
return false;
}
if ($assign->get_instance()->teamsubmission &&
!$assign->can_view_group_submission($groupid)) {
return false;
}
if (!$assign->get_instance()->teamsubmission &&
!$assign->can_view_submission($userid)) {
return false;
}
$relativepath = implode('/', $args);
$fullpath = "/{$context->id}/assignsubmission_onlinetext/$filearea/$itemid/$relativepath";
$fs = get_file_storage();
if (!($file = $fs->get_file_by_hash(sha1($fullpath))) || $file->is_directory()) {
return false;
}
// Download MUST be forced - security!
send_stored_file($file, 0, 0, true, $options);
}
@@ -0,0 +1,713 @@
<?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 onlinetext submission plugin
*
* This class provides all the functionality for the new assign module.
*
* @package assignsubmission_onlinetext
* @copyright 2012 NetSpot {@link http://www.netspot.com.au}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use core_external\external_single_structure;
use core_external\external_value;
defined('MOODLE_INTERNAL') || die();
// File area for online text submission assignment.
define('ASSIGNSUBMISSION_ONLINETEXT_FILEAREA', 'submissions_onlinetext');
/**
* library class for onlinetext submission plugin extending submission plugin base class
*
* @package assignsubmission_onlinetext
* @copyright 2012 NetSpot {@link http://www.netspot.com.au}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class assign_submission_onlinetext extends assign_submission_plugin {
/**
* Get the name of the online text submission plugin
* @return string
*/
public function get_name() {
return get_string('onlinetext', 'assignsubmission_onlinetext');
}
/**
* Get onlinetext submission information from the database
*
* @param int $submissionid
* @return mixed
*/
private function get_onlinetext_submission($submissionid) {
global $DB;
return $DB->get_record('assignsubmission_onlinetext', array('submission'=>$submissionid));
}
/**
* Remove a submission.
*
* @param stdClass $submission The submission
* @return boolean
*/
public function remove(stdClass $submission) {
global $DB;
$submissionid = $submission ? $submission->id : 0;
if ($submissionid) {
$DB->delete_records('assignsubmission_onlinetext', array('submission' => $submissionid));
}
return true;
}
/**
* Get the settings for onlinetext submission plugin
*
* @param MoodleQuickForm $mform The form to add elements to
* @return void
*/
public function get_settings(MoodleQuickForm $mform) {
global $CFG, $COURSE;
$defaultwordlimit = $this->get_config('wordlimit') == 0 ? '' : $this->get_config('wordlimit');
$defaultwordlimitenabled = $this->get_config('wordlimitenabled');
$options = array('size' => '6', 'maxlength' => '6');
$name = get_string('wordlimit', 'assignsubmission_onlinetext');
// Create a text box that can be enabled/disabled for onlinetext word limit.
$wordlimitgrp = array();
$wordlimitgrp[] = $mform->createElement('text', 'assignsubmission_onlinetext_wordlimit', '', $options);
$wordlimitgrp[] = $mform->createElement('checkbox', 'assignsubmission_onlinetext_wordlimit_enabled',
'', get_string('enable'));
$mform->addGroup($wordlimitgrp, 'assignsubmission_onlinetext_wordlimit_group', $name, ' ', false);
$mform->addHelpButton('assignsubmission_onlinetext_wordlimit_group',
'wordlimit',
'assignsubmission_onlinetext');
$mform->disabledIf('assignsubmission_onlinetext_wordlimit',
'assignsubmission_onlinetext_wordlimit_enabled',
'notchecked');
$mform->hideIf('assignsubmission_onlinetext_wordlimit',
'assignsubmission_onlinetext_enabled',
'notchecked');
// Add numeric rule to text field.
$wordlimitgrprules = array();
$wordlimitgrprules['assignsubmission_onlinetext_wordlimit'][] = array(null, 'numeric', null, 'client');
$mform->addGroupRule('assignsubmission_onlinetext_wordlimit_group', $wordlimitgrprules);
// Rest of group setup.
$mform->setDefault('assignsubmission_onlinetext_wordlimit', $defaultwordlimit);
$mform->setDefault('assignsubmission_onlinetext_wordlimit_enabled', $defaultwordlimitenabled);
$mform->setType('assignsubmission_onlinetext_wordlimit', PARAM_INT);
$mform->hideIf('assignsubmission_onlinetext_wordlimit_group',
'assignsubmission_onlinetext_enabled',
'notchecked');
}
/**
* Save the settings for onlinetext submission plugin
*
* @param stdClass $data
* @return bool
*/
public function save_settings(stdClass $data) {
if (empty($data->assignsubmission_onlinetext_wordlimit) || empty($data->assignsubmission_onlinetext_wordlimit_enabled)) {
$wordlimit = 0;
$wordlimitenabled = 0;
} else {
$wordlimit = $data->assignsubmission_onlinetext_wordlimit;
$wordlimitenabled = 1;
}
$this->set_config('wordlimit', $wordlimit);
$this->set_config('wordlimitenabled', $wordlimitenabled);
return true;
}
/**
* Add form elements for settings
*
* @param mixed $submission can be null
* @param MoodleQuickForm $mform
* @param stdClass $data
* @return true if elements were added to the form
*/
public function get_form_elements($submission, MoodleQuickForm $mform, stdClass $data) {
$elements = array();
$editoroptions = $this->get_edit_options();
$submissionid = $submission ? $submission->id : 0;
if (!isset($data->onlinetext)) {
$data->onlinetext = '';
}
if (!isset($data->onlinetextformat)) {
$data->onlinetextformat = editors_get_preferred_format();
}
if ($submission) {
$onlinetextsubmission = $this->get_onlinetext_submission($submission->id);
if ($onlinetextsubmission) {
$data->onlinetext = $onlinetextsubmission->onlinetext;
$data->onlinetextformat = $onlinetextsubmission->onlineformat;
}
}
$data = file_prepare_standard_editor($data,
'onlinetext',
$editoroptions,
$this->assignment->get_context(),
'assignsubmission_onlinetext',
ASSIGNSUBMISSION_ONLINETEXT_FILEAREA,
$submissionid);
$mform->addElement('editor', 'onlinetext_editor', $this->get_name(), null, $editoroptions);
return true;
}
/**
* Editor format options
*
* @return array
*/
private function get_edit_options() {
$editoroptions = array(
'noclean' => false,
'maxfiles' => EDITOR_UNLIMITED_FILES,
'maxbytes' => $this->assignment->get_course()->maxbytes,
'context' => $this->assignment->get_context(),
'return_types' => (FILE_INTERNAL | FILE_EXTERNAL | FILE_CONTROLLED_LINK),
'removeorphaneddrafts' => true // Whether or not to remove any draft files which aren't referenced in the text.
);
return $editoroptions;
}
/**
* Save data to the database and trigger plagiarism plugin,
* if enabled, to scan the uploaded content via events trigger
*
* @param stdClass $submission
* @param stdClass $data
* @return bool
*/
public function save(stdClass $submission, stdClass $data) {
global $USER, $DB;
$editoroptions = $this->get_edit_options();
$data = file_postupdate_standard_editor($data,
'onlinetext',
$editoroptions,
$this->assignment->get_context(),
'assignsubmission_onlinetext',
ASSIGNSUBMISSION_ONLINETEXT_FILEAREA,
$submission->id);
$onlinetextsubmission = $this->get_onlinetext_submission($submission->id);
$fs = get_file_storage();
$files = $fs->get_area_files($this->assignment->get_context()->id,
'assignsubmission_onlinetext',
ASSIGNSUBMISSION_ONLINETEXT_FILEAREA,
$submission->id,
'id',
false);
// Check word count before submitting anything.
$exceeded = $this->check_word_count(trim($data->onlinetext));
if ($exceeded) {
$this->set_error($exceeded);
return false;
}
$params = array(
'context' => context_module::instance($this->assignment->get_course_module()->id),
'courseid' => $this->assignment->get_course()->id,
'objectid' => $submission->id,
'other' => array(
'pathnamehashes' => array_keys($files),
'content' => trim($data->onlinetext),
'format' => $data->onlinetext_editor['format']
)
);
if (!empty($submission->userid) && ($submission->userid != $USER->id)) {
$params['relateduserid'] = $submission->userid;
}
if ($this->assignment->is_blind_marking()) {
$params['anonymous'] = 1;
}
$event = \assignsubmission_onlinetext\event\assessable_uploaded::create($params);
$event->trigger();
$groupname = null;
$groupid = 0;
// Get the group name as other fields are not transcribed in the logs and this information is important.
if (empty($submission->userid) && !empty($submission->groupid)) {
$groupname = $DB->get_field('groups', 'name', array('id' => $submission->groupid), MUST_EXIST);
$groupid = $submission->groupid;
} else {
$params['relateduserid'] = $submission->userid;
}
$count = count_words($data->onlinetext);
// Unset the objectid and other field from params for use in submission events.
unset($params['objectid']);
unset($params['other']);
$params['other'] = array(
'submissionid' => $submission->id,
'submissionattempt' => $submission->attemptnumber,
'submissionstatus' => $submission->status,
'onlinetextwordcount' => $count,
'groupid' => $groupid,
'groupname' => $groupname
);
if ($onlinetextsubmission) {
$onlinetextsubmission->onlinetext = $data->onlinetext;
$onlinetextsubmission->onlineformat = $data->onlinetext_editor['format'];
$params['objectid'] = $onlinetextsubmission->id;
$updatestatus = $DB->update_record('assignsubmission_onlinetext', $onlinetextsubmission);
$event = \assignsubmission_onlinetext\event\submission_updated::create($params);
$event->set_assign($this->assignment);
$event->trigger();
return $updatestatus;
} else {
$onlinetextsubmission = new stdClass();
$onlinetextsubmission->onlinetext = $data->onlinetext;
$onlinetextsubmission->onlineformat = $data->onlinetext_editor['format'];
$onlinetextsubmission->submission = $submission->id;
$onlinetextsubmission->assignment = $this->assignment->get_instance()->id;
$onlinetextsubmission->id = $DB->insert_record('assignsubmission_onlinetext', $onlinetextsubmission);
$params['objectid'] = $onlinetextsubmission->id;
$event = \assignsubmission_onlinetext\event\submission_created::create($params);
$event->set_assign($this->assignment);
$event->trigger();
return $onlinetextsubmission->id > 0;
}
}
/**
* Return a list of the text fields that can be imported/exported by this plugin
*
* @return array An array of field names and descriptions. (name=>description, ...)
*/
public function get_editor_fields() {
return array('onlinetext' => get_string('pluginname', 'assignsubmission_onlinetext'));
}
/**
* Get the saved text content from the editor
*
* @param string $name
* @param int $submissionid
* @return string
*/
public function get_editor_text($name, $submissionid) {
if ($name == 'onlinetext') {
$onlinetextsubmission = $this->get_onlinetext_submission($submissionid);
if ($onlinetextsubmission) {
return $onlinetextsubmission->onlinetext;
}
}
return '';
}
/**
* Get the content format for the editor
*
* @param string $name
* @param int $submissionid
* @return int
*/
public function get_editor_format($name, $submissionid) {
if ($name == 'onlinetext') {
$onlinetextsubmission = $this->get_onlinetext_submission($submissionid);
if ($onlinetextsubmission) {
return $onlinetextsubmission->onlineformat;
}
}
return 0;
}
/**
* Display onlinetext word count in the submission status table
*
* @param stdClass $submission
* @param bool $showviewlink - If the summary has been truncated set this to true
* @return string
*/
public function view_summary(stdClass $submission, & $showviewlink) {
global $CFG;
$onlinetextsubmission = $this->get_onlinetext_submission($submission->id);
// Always show the view link.
$showviewlink = true;
if ($onlinetextsubmission) {
// This contains the shortened version of the text plus an optional 'Export to portfolio' button.
$text = $this->assignment->render_editor_content(ASSIGNSUBMISSION_ONLINETEXT_FILEAREA,
$onlinetextsubmission->submission,
$this->get_type(),
'onlinetext',
'assignsubmission_onlinetext', true);
// The actual submission text.
$onlinetext = trim($onlinetextsubmission->onlinetext);
// The shortened version of the submission text.
$shorttext = shorten_text($onlinetext, 140);
$plagiarismlinks = '';
if (!empty($CFG->enableplagiarism)) {
require_once($CFG->libdir . '/plagiarismlib.php');
$plagiarismlinks .= plagiarism_get_links(array('userid' => $submission->userid,
'content' => $onlinetext,
'cmid' => $this->assignment->get_course_module()->id,
'course' => $this->assignment->get_course()->id,
'assignment' => $submission->assignment));
}
// We compare the actual text submission and the shortened version. If they are not equal, we show the word count.
if ($onlinetext != $shorttext) {
$wordcount = get_string('numwords', 'assignsubmission_onlinetext', count_words($onlinetext));
return $plagiarismlinks . $wordcount . $text;
} else {
return $plagiarismlinks . $text;
}
}
return '';
}
/**
* Produce a list of files suitable for export that represent this submission.
*
* @param stdClass $submission - For this is the submission data
* @param stdClass $user - This is the user record for this submission
* @return array - return an array of files indexed by filename
*/
public function get_files(stdClass $submission, stdClass $user) {
global $DB;
$files = array();
$onlinetextsubmission = $this->get_onlinetext_submission($submission->id);
// Note that this check is the same logic as the result from the is_empty function but we do
// not call it directly because we already have the submission record.
if ($onlinetextsubmission && !html_is_blank($onlinetextsubmission->onlinetext)) {
// Do not pass the text through format_text. The result may not be displayed in Moodle and
// may be passed to external services such as document conversion or portfolios.
$formattedtext = $this->assignment->download_rewrite_pluginfile_urls($onlinetextsubmission->onlinetext, $user, $this);
$head = '<head><meta charset="UTF-8"></head>';
$submissioncontent = '<!DOCTYPE html><html>' . $head . '<body>'. $formattedtext . '</body></html>';
$filename = get_string('onlinetextfilename', 'assignsubmission_onlinetext');
$files[$filename] = array($submissioncontent);
$fs = get_file_storage();
$fsfiles = $fs->get_area_files($this->assignment->get_context()->id,
'assignsubmission_onlinetext',
ASSIGNSUBMISSION_ONLINETEXT_FILEAREA,
$submission->id,
'timemodified',
false);
foreach ($fsfiles as $file) {
$files[$file->get_filename()] = $file;
}
}
return $files;
}
/**
* Display the saved text content from the editor in the view table
*
* @param stdClass $submission
* @return string
*/
public function view(stdClass $submission) {
global $CFG;
$result = '';
$plagiarismlinks = '';
$onlinetextsubmission = $this->get_onlinetext_submission($submission->id);
if ($onlinetextsubmission) {
// Render for portfolio API.
$result .= $this->assignment->render_editor_content(ASSIGNSUBMISSION_ONLINETEXT_FILEAREA,
$onlinetextsubmission->submission,
$this->get_type(),
'onlinetext',
'assignsubmission_onlinetext');
if (!empty($CFG->enableplagiarism)) {
require_once($CFG->libdir . '/plagiarismlib.php');
$plagiarismlinks .= plagiarism_get_links(array('userid' => $submission->userid,
'content' => trim($onlinetextsubmission->onlinetext),
'cmid' => $this->assignment->get_course_module()->id,
'course' => $this->assignment->get_course()->id,
'assignment' => $submission->assignment));
}
}
return $plagiarismlinks . $result;
}
/**
* 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 == 'online' && $version >= 2011112900) {
return true;
}
return false;
}
/**
* Upgrade the settings from the old assignment to the new plugin based one
*
* @param context $oldcontext - the database for the old assignment context
* @param stdClass $oldassignment - the database for the old assignment instance
* @param string $log record log events here
* @return bool Was it a success?
*/
public function upgrade_settings(context $oldcontext, stdClass $oldassignment, & $log) {
// No settings to upgrade.
return true;
}
/**
* Upgrade the submission 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 $submission The data record for the new submission
* @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 $submission,
& $log) {
global $DB;
$onlinetextsubmission = new stdClass();
$onlinetextsubmission->onlinetext = $oldsubmission->data1;
$onlinetextsubmission->onlineformat = $oldsubmission->data2;
$onlinetextsubmission->submission = $submission->id;
$onlinetextsubmission->assignment = $this->assignment->get_instance()->id;
if ($onlinetextsubmission->onlinetext === null) {
$onlinetextsubmission->onlinetext = '';
}
if ($onlinetextsubmission->onlineformat === null) {
$onlinetextsubmission->onlineformat = editors_get_preferred_format();
}
if (!$DB->insert_record('assignsubmission_onlinetext', $onlinetextsubmission) > 0) {
$log .= get_string('couldnotconvertsubmission', 'mod_assign', $submission->userid);
return false;
}
// Now copy the area files.
$this->assignment->copy_area_files_for_upgrade($oldcontext->id,
'mod_assignment',
'submission',
$oldsubmission->id,
$this->assignment->get_context()->id,
'assignsubmission_onlinetext',
ASSIGNSUBMISSION_ONLINETEXT_FILEAREA,
$submission->id);
return true;
}
/**
* The assignment has been deleted - cleanup
*
* @return bool
*/
public function delete_instance() {
global $DB;
$DB->delete_records('assignsubmission_onlinetext',
array('assignment'=>$this->assignment->get_instance()->id));
return true;
}
/**
* No text is set for this plugin
*
* @param stdClass $submission
* @return bool
*/
public function is_empty(stdClass $submission) {
$onlinetextsubmission = $this->get_onlinetext_submission($submission->id);
$wordcount = 0;
$hasinsertedresources = false;
if (isset($onlinetextsubmission->onlinetext)) {
$wordcount = count_words(trim($onlinetextsubmission->onlinetext));
// Check if the online text submission contains video, audio or image elements
// that can be ignored and stripped by count_words().
$hasinsertedresources = preg_match('/<\s*((video|audio)[^>]*>(.*?)<\s*\/\s*(video|audio)>)|(img[^>]*>(.*?))/',
trim($onlinetextsubmission->onlinetext));
}
return $wordcount == 0 && !$hasinsertedresources;
}
/**
* Determine if a submission is empty
*
* This is distinct from is_empty in that it is intended to be used to
* determine if a submission made before saving is empty.
*
* @param stdClass $data The submission data
* @return bool
*/
public function submission_is_empty(stdClass $data) {
if (!isset($data->onlinetext_editor)) {
return true;
}
$wordcount = 0;
$hasinsertedresources = false;
if (isset($data->onlinetext_editor['text'])) {
$wordcount = count_words(trim((string)$data->onlinetext_editor['text']));
// Check if the online text submission contains video, audio or image elements
// that can be ignored and stripped by count_words().
$hasinsertedresources = preg_match('/<\s*((video|audio)[^>]*>(.*?)<\s*\/\s*(video|audio)>)|(img[^>]*>(.*?))/',
trim((string)$data->onlinetext_editor['text']));
}
return $wordcount == 0 && !$hasinsertedresources;
}
/**
* 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(ASSIGNSUBMISSION_ONLINETEXT_FILEAREA=>$this->get_name());
}
/**
* Copy the student's submission from a previous submission. Used when a student opts to base their resubmission
* on the last submission.
* @param stdClass $sourcesubmission
* @param stdClass $destsubmission
*/
public function copy_submission(stdClass $sourcesubmission, stdClass $destsubmission) {
global $DB;
// Copy the files across (attached via the text editor).
$contextid = $this->assignment->get_context()->id;
$fs = get_file_storage();
$files = $fs->get_area_files($contextid, 'assignsubmission_onlinetext',
ASSIGNSUBMISSION_ONLINETEXT_FILEAREA, $sourcesubmission->id, 'id', false);
foreach ($files as $file) {
$fieldupdates = array('itemid' => $destsubmission->id);
$fs->create_file_from_storedfile($fieldupdates, $file);
}
// Copy the assignsubmission_onlinetext record.
$onlinetextsubmission = $this->get_onlinetext_submission($sourcesubmission->id);
if ($onlinetextsubmission) {
unset($onlinetextsubmission->id);
$onlinetextsubmission->submission = $destsubmission->id;
$DB->insert_record('assignsubmission_onlinetext', $onlinetextsubmission);
}
return true;
}
/**
* Return a description of external params suitable for uploading an onlinetext submission from a webservice.
*
* @return \core_external\external_description|null
*/
public function get_external_parameters() {
$editorparams = array('text' => new external_value(PARAM_RAW, 'The text for this submission.'),
'format' => new external_value(PARAM_INT, 'The format for this submission'),
'itemid' => new external_value(PARAM_INT, 'The draft area id for files attached to the submission'));
$editorstructure = new external_single_structure($editorparams, 'Editor structure', VALUE_OPTIONAL);
return array('onlinetext_editor' => $editorstructure);
}
/**
* Compare word count of onlinetext submission to word limit, and return result.
*
* @param string $submissiontext Onlinetext submission text from editor
* @return string Error message if limit is enabled and exceeded, otherwise null
*/
public function check_word_count($submissiontext) {
global $OUTPUT;
$wordlimitenabled = $this->get_config('wordlimitenabled');
$wordlimit = $this->get_config('wordlimit');
if ($wordlimitenabled == 0) {
return null;
}
// Count words and compare to limit.
$wordcount = count_words($submissiontext);
if ($wordcount <= $wordlimit) {
return null;
} else {
$errormsg = get_string('wordlimitexceeded', 'assignsubmission_onlinetext',
array('limit' => $wordlimit, 'count' => $wordcount));
return $OUTPUT->error_text($errormsg);
}
}
/**
* 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();
}
}
@@ -0,0 +1,28 @@
<?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 assignsubmission_onlinetext
* @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('assignsubmission_onlinetext/default',
new lang_string('default', 'assignsubmission_onlinetext'),
new lang_string('default_help', 'assignsubmission_onlinetext'), 0));
@@ -0,0 +1,165 @@
<?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/>.
/**
* Contains the event tests for the plugin.
*
* @package assignsubmission_onlinetext
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace assignsubmission_onlinetext\event;
use mod_assign_test_generator;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/mod/assign/tests/generator.php');
class events_test extends \advanced_testcase {
// Use the generator helper.
use mod_assign_test_generator;
/**
* Test that the assessable_uploaded event is fired when an online text submission is saved.
*/
public function test_assessable_uploaded(): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
$assign = $this->create_instance($course);
$context = $assign->get_context();
$cm = $assign->get_course_module();
$this->setUser($student->id);
$submission = $assign->get_user_submission($student->id, true);
$data = (object) [
'onlinetext_editor' => [
'itemid' => file_get_unused_draft_itemid(),
'text' => 'Submission text',
'format' => FORMAT_PLAIN,
],
];
$sink = $this->redirectEvents();
$plugin = $assign->get_submission_plugin_by_type('onlinetext');
$plugin->save($submission, $data);
$events = $sink->get_events();
$this->assertCount(2, $events);
$event = reset($events);
$this->assertInstanceOf('\assignsubmission_onlinetext\event\assessable_uploaded', $event);
$this->assertEquals($context->id, $event->contextid);
$this->assertEquals($submission->id, $event->objectid);
$this->assertEquals(array(), $event->other['pathnamehashes']);
$this->assertEquals(FORMAT_PLAIN, $event->other['format']);
$this->assertEquals('Submission text', $event->other['content']);
$expected = new \stdClass();
$expected->modulename = 'assign';
$expected->cmid = $cm->id;
$expected->itemid = $submission->id;
$expected->courseid = $course->id;
$expected->userid = $student->id;
$expected->content = 'Submission text';
$this->assertEventContextNotUsed($event);
}
/**
* Test that the submission_created event is fired when an onlinetext submission is saved.
*/
public function test_submission_created(): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
$assign = $this->create_instance($course);
$context = $assign->get_context();
$this->setUser($student->id);
$submission = $assign->get_user_submission($student->id, true);
$data = (object) [
'onlinetext_editor' => [
'itemid' => file_get_unused_draft_itemid(),
'text' => 'Submission text',
'format' => FORMAT_PLAIN,
],
];
$sink = $this->redirectEvents();
$plugin = $assign->get_submission_plugin_by_type('onlinetext');
$plugin->save($submission, $data);
$events = $sink->get_events();
$this->assertCount(2, $events);
$event = $events[1];
$this->assertInstanceOf('\assignsubmission_onlinetext\event\submission_created', $event);
$this->assertEquals($context->id, $event->contextid);
$this->assertEquals($course->id, $event->courseid);
$this->assertEquals($submission->id, $event->other['submissionid']);
$this->assertEquals($submission->attemptnumber, $event->other['submissionattempt']);
$this->assertEquals($submission->status, $event->other['submissionstatus']);
$this->assertEquals($submission->userid, $event->relateduserid);
}
/**
* Test that the submission_updated event is fired when an onlinetext
* submission is saved and an existing submission already exists.
*/
public function test_submission_updated(): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
$assign = $this->create_instance($course);
$context = $assign->get_context();
$this->setUser($student->id);
$submission = $assign->get_user_submission($student->id, true);
$data = (object) [
'onlinetext_editor' => [
'itemid' => file_get_unused_draft_itemid(),
'text' => 'Submission text',
'format' => FORMAT_PLAIN,
],
];
$sink = $this->redirectEvents();
$plugin = $assign->get_submission_plugin_by_type('onlinetext');
$plugin->save($submission, $data);
$sink->clear();
// Update a submission.
$plugin->save($submission, $data);
$events = $sink->get_events();
$this->assertCount(2, $events);
$event = $events[1];
$this->assertInstanceOf('\assignsubmission_onlinetext\event\submission_updated', $event);
$this->assertEquals($context->id, $event->contextid);
$this->assertEquals($course->id, $event->courseid);
$this->assertEquals($submission->id, $event->other['submissionid']);
$this->assertEquals($submission->attemptnumber, $event->other['submissionattempt']);
$this->assertEquals($submission->status, $event->other['submissionstatus']);
$this->assertEquals($submission->userid, $event->relateduserid);
}
}
@@ -0,0 +1,47 @@
<?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/>.
require_once("{$CFG->dirroot}/mod/assign/tests/generator/assignsubmission_subplugin_generator.php");
/**
* Online Text assignment submission subplugin data generator.
*
* @package assignsubmission_onlinetext
* @category test
* @copyright 2021 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class assignsubmission_onlinetext_generator extends assignsubmission_subplugin_generator {
/**
* Add submission data in the correct format for a call to `assign::save_submission()` from a table containing
* submission data for a single activity.
*
* Data should be added to the $submission object passed into the function.
*
* @param stdClass $submission The submission record to be modified
* @param assign $assign The assignment being submitted to
* @param array $data The data received
*/
public function add_submission_data(stdClass $submission, assign $assign, array $data): void {
if (array_key_exists('onlinetext', $data)) {
$submission->onlinetext_editor = [
'itemid' => file_get_unused_draft_itemid(),
'text' => $data['onlinetext'],
'format' => FORMAT_HTML,
];
}
}
}
@@ -0,0 +1,115 @@
<?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/>.
/**
* Tests for mod/assign/submission/onlinetext/locallib.php
*
* @package assignsubmission_onlinetext
* @copyright 2016 Cameron Ball
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace assignsubmission_onlinetext;
use mod_assign_test_generator;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/mod/assign/tests/generator.php');
/**
* Unit tests for mod/assign/submission/onlinetext/locallib.php
*
* @copyright 2016 Cameron Ball
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class locallib_test extends \advanced_testcase {
// Use the generator helper.
use mod_assign_test_generator;
/**
* Test submission_is_empty
*
* @dataProvider submission_is_empty_testcases
* @param string $submissiontext The online text submission text
* @param bool $expected The expected return value
*/
public function test_submission_is_empty($submissiontext, $expected): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
$assign = $this->create_instance($course, [
'assignsubmission_onlinetext_enabled' => true,
]);
$this->setUser($student->id);
$plugin = $assign->get_submission_plugin_by_type('onlinetext');
$result = $plugin->submission_is_empty((object) [
'onlinetext_editor' => [
'text' => $submissiontext,
],
]);
$this->assertTrue($result === $expected);
}
/**
* Test new_submission_empty
*
* @dataProvider submission_is_empty_testcases
* @param string $submissiontext The file submission data
* @param bool $expected The expected return value
*/
public function test_new_submission_empty($submissiontext, $expected): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
$assign = $this->create_instance($course, [
'assignsubmission_onlinetext_enabled' => true,
]);
$this->setUser($student->id);
$result = $assign->new_submission_empty((object) [
'onlinetext_editor' => [
'text' => $submissiontext,
],
]);
$this->assertTrue($result === $expected);
}
/**
* Dataprovider for the test_submission_is_empty testcase
*
* @return array of testcases
*/
public function submission_is_empty_testcases() {
return [
'Empty submission string' => ['', true],
'Empty submission null' => [null, true],
'Value 0' => [0, false],
'String 0' => ['0', false],
'Text' => ['Ai! laurië lantar lassi súrinen, yéni únótimë ve rámar aldaron!', false],
'Image' => ['<img src="test.jpg" />', false],
'Video' => ['<video controls="true"><source src="test.mp4"></video>', false],
'Audio' => ['<audio controls="true"><source src="test.mp3"></audio>', false],
];
}
}
@@ -0,0 +1,217 @@
<?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 assignsubmission_onlinetext.
*
* @package assignsubmission_onlinetext
* @copyright 2018 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace assignsubmission_onlinetext\privacy;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/mod/assign/tests/privacy/provider_test.php');
/**
* Unit tests for mod/assign/submission/onlinetext/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 string $text Submission text.
* @return array Submission plugin object and the submission object.
*/
protected function create_online_submission($assign, $student, $text) {
global $CFG;
$this->setUser($student->id);
$submission = $assign->get_user_submission($student->id, true);
$data = new \stdClass();
$data->onlinetext_editor = array(
'itemid' => file_get_unused_draft_itemid(),
'text' => $text,
'format' => FORMAT_PLAIN
);
$submission = $assign->get_user_submission($student->id, true);
$plugin = $assign->get_submission_plugin_by_type('onlinetext');
$plugin->save($submission, $data);
return [$plugin, $submission];
}
/**
* Quick test to make sure that get_metadata returns something.
*/
public function test_get_metadata(): void {
$collection = new \core_privacy\local\metadata\collection('assignsubmission_onlinetext');
$collection = \assignsubmission_onlinetext\privacy\provider::get_metadata($collection);
$this->assertNotEmpty($collection);
}
/**
* Test that submission files and text are exported for a user.
*/
public function test_export_submission_user_data(): void {
$this->resetAfterTest();
// Create course, assignment, submission, and then a feedback comment.
$course = $this->getDataGenerator()->create_course();
// Student.
$user1 = $this->getDataGenerator()->create_user();
$this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student');
$assign = $this->create_instance(['course' => $course]);
$context = $assign->get_context();
$submissiontext = 'Just some text';
list($plugin, $submission) = $this->create_online_submission($assign, $user1, $submissiontext);
$writer = \core_privacy\local\request\writer::with_context($context);
$this->assertFalse($writer->has_any_data());
// The student should have some text submitted.
$exportdata = new \mod_assign\privacy\assign_plugin_request_data($context, $assign, $submission, ['Attempt 1']);
\assignsubmission_onlinetext\privacy\provider::export_submission_user_data($exportdata);
$this->assertEquals($submissiontext, $writer->get_data(['Attempt 1',
get_string('privacy:path', 'assignsubmission_onlinetext')])->text);
}
/**
* Test that all submission files are deleted for this context.
*/
public function test_delete_submission_for_context(): void {
$this->resetAfterTest();
// Create course, assignment, submission, and then a feedback comment.
$course = $this->getDataGenerator()->create_course();
// Student.
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student');
$this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student');
$assign = $this->create_instance(['course' => $course]);
$context = $assign->get_context();
$studenttext = 'Student one\'s text.';
list($plugin, $submission) = $this->create_online_submission($assign, $user1, $studenttext);
$studenttext2 = 'Student two\'s text.';
list($plugin2, $submission2) = $this->create_online_submission($assign, $user2, $studenttext2);
// Only need the context and assign object in this plugin for this operation.
$requestdata = new \mod_assign\privacy\assign_plugin_request_data($context, $assign);
\assignsubmission_onlinetext\privacy\provider::delete_submission_for_context($requestdata);
// This checks that there is no content for these submissions.
$this->assertTrue($plugin->is_empty($submission));
$this->assertTrue($plugin2->is_empty($submission2));
}
/**
* Test that the comments for a user are deleted.
*/
public function test_delete_submission_for_userid(): void {
$this->resetAfterTest();
// Create course, assignment, submission, and then a feedback comment.
$course = $this->getDataGenerator()->create_course();
// Student.
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student');
$this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student');
$assign = $this->create_instance(['course' => $course]);
$context = $assign->get_context();
$studenttext = 'Student one\'s text.';
list($plugin, $submission) = $this->create_online_submission($assign, $user1, $studenttext);
$studenttext2 = 'Student two\'s text.';
list($plugin2, $submission2) = $this->create_online_submission($assign, $user2, $studenttext2);
// Need more data for this operation.
$requestdata = new \mod_assign\privacy\assign_plugin_request_data($context, $assign, $submission, [], $user1);
\assignsubmission_onlinetext\privacy\provider::delete_submission_for_userid($requestdata);
// This checks that there is no content for the first submission.
$this->assertTrue($plugin->is_empty($submission));
// But there is for the second submission.
$this->assertFalse($plugin2->is_empty($submission2));
}
public function test_delete_submissions(): void {
global $DB;
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$user3 = $this->getDataGenerator()->create_user();
// Only makes submissions in the second assignment.
$user4 = $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');
$assign1 = $this->create_instance(['course' => $course]);
$assign2 = $this->create_instance(['course' => $course]);
$context1 = $assign1->get_context();
$context2 = $assign2->get_context();
$student1text = 'Student one\'s text.';
list($plugin1, $submission1) = $this->create_online_submission($assign1, $user1, $student1text);
$student2text = 'Student two\'s text.';
list($plugin2, $submission2) = $this->create_online_submission($assign1, $user2, $student2text);
$student3text = 'Student two\'s text.';
list($plugin3, $submission3) = $this->create_online_submission($assign1, $user3, $student3text);
// Now for submissions in assignment two.
$student3text2 = 'Student two\'s text for the second assignment.';
list($plugin4, $submission4) = $this->create_online_submission($assign2, $user3, $student3text2);
$student4text = 'Student four\'s text.';
list($plugin5, $submission5) = $this->create_online_submission($assign2, $user4, $student4text);
$data = $DB->get_records('assignsubmission_onlinetext', ['assignment' => $assign1->get_instance()->id]);
$this->assertCount(3, $data);
// Delete the submissions for user 1 and 3.
$requestdata = new \mod_assign\privacy\assign_plugin_request_data($context1, $assign1);
$requestdata->set_userids([$user1->id, $user2->id]);
$requestdata->populate_submissions_and_grades();
\assignsubmission_onlinetext\privacy\provider::delete_submissions($requestdata);
// There should only be one record left for assignment one.
$data = $DB->get_records('assignsubmission_onlinetext', ['assignment' => $assign1->get_instance()->id]);
$this->assertCount(1, $data);
// Check that the second assignment has not been touched.
$data = $DB->get_records('assignsubmission_onlinetext', ['assignment' => $assign2->get_instance()->id]);
$this->assertCount(2, $data);
}
}
@@ -0,0 +1,29 @@
<?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 onlinetext submission plugin
*
* @package assignsubmission_onlinetext
* @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 = 2024042200;
$plugin->requires = 2024041600;
$plugin->component = 'assignsubmission_onlinetext';