1 |
efrain |
1 |
<?php
|
|
|
2 |
// This file is part of Moodle - http://moodle.org/
|
|
|
3 |
//
|
|
|
4 |
// Moodle is free software: you can redistribute it and/or modify
|
|
|
5 |
// it under the terms of the GNU General Public License as published by
|
|
|
6 |
// the Free Software Foundation, either version 3 of the License, or
|
|
|
7 |
// (at your option) any later version.
|
|
|
8 |
//
|
|
|
9 |
// Moodle is distributed in the hope that it will be useful,
|
|
|
10 |
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
11 |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
12 |
// GNU General Public License for more details.
|
|
|
13 |
//
|
|
|
14 |
// You should have received a copy of the GNU General Public License
|
|
|
15 |
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
16 |
|
|
|
17 |
/**
|
|
|
18 |
* This file contains the definition for the class assignment
|
|
|
19 |
*
|
|
|
20 |
* This class provides all the functionality for the new assign module.
|
|
|
21 |
*
|
|
|
22 |
* @package mod_assign
|
|
|
23 |
* @copyright 2012 NetSpot {@link http://www.netspot.com.au}
|
|
|
24 |
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
25 |
*/
|
|
|
26 |
|
|
|
27 |
defined('MOODLE_INTERNAL') || die();
|
|
|
28 |
|
|
|
29 |
// Assignment submission statuses.
|
|
|
30 |
define('ASSIGN_SUBMISSION_STATUS_NEW', 'new');
|
|
|
31 |
define('ASSIGN_SUBMISSION_STATUS_REOPENED', 'reopened');
|
|
|
32 |
define('ASSIGN_SUBMISSION_STATUS_DRAFT', 'draft');
|
|
|
33 |
define('ASSIGN_SUBMISSION_STATUS_SUBMITTED', 'submitted');
|
|
|
34 |
|
|
|
35 |
// Search filters for grading page.
|
|
|
36 |
define('ASSIGN_FILTER_NONE', 'none');
|
|
|
37 |
define('ASSIGN_FILTER_SUBMITTED', 'submitted');
|
|
|
38 |
define('ASSIGN_FILTER_NOT_SUBMITTED', 'notsubmitted');
|
|
|
39 |
define('ASSIGN_FILTER_SINGLE_USER', 'singleuser');
|
|
|
40 |
define('ASSIGN_FILTER_REQUIRE_GRADING', 'requiregrading');
|
|
|
41 |
define('ASSIGN_FILTER_GRANTED_EXTENSION', 'grantedextension');
|
|
|
42 |
define('ASSIGN_FILTER_DRAFT', 'draft');
|
|
|
43 |
|
|
|
44 |
// Marker filter for grading page.
|
|
|
45 |
define('ASSIGN_MARKER_FILTER_NO_MARKER', -1);
|
|
|
46 |
|
|
|
47 |
// Reopen attempt methods.
|
|
|
48 |
define('ASSIGN_ATTEMPT_REOPEN_METHOD_NONE', 'none');
|
|
|
49 |
define('ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL', 'manual');
|
|
|
50 |
define('ASSIGN_ATTEMPT_REOPEN_METHOD_UNTILPASS', 'untilpass');
|
|
|
51 |
|
|
|
52 |
// Special value means allow unlimited attempts.
|
|
|
53 |
define('ASSIGN_UNLIMITED_ATTEMPTS', -1);
|
|
|
54 |
|
|
|
55 |
// Special value means no grade has been set.
|
|
|
56 |
define('ASSIGN_GRADE_NOT_SET', -1);
|
|
|
57 |
|
|
|
58 |
// Grading states.
|
|
|
59 |
define('ASSIGN_GRADING_STATUS_GRADED', 'graded');
|
|
|
60 |
define('ASSIGN_GRADING_STATUS_NOT_GRADED', 'notgraded');
|
|
|
61 |
|
|
|
62 |
// Marking workflow states.
|
|
|
63 |
define('ASSIGN_MARKING_WORKFLOW_STATE_NOTMARKED', 'notmarked');
|
|
|
64 |
define('ASSIGN_MARKING_WORKFLOW_STATE_INMARKING', 'inmarking');
|
|
|
65 |
define('ASSIGN_MARKING_WORKFLOW_STATE_READYFORREVIEW', 'readyforreview');
|
|
|
66 |
define('ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW', 'inreview');
|
|
|
67 |
define('ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE', 'readyforrelease');
|
|
|
68 |
define('ASSIGN_MARKING_WORKFLOW_STATE_RELEASED', 'released');
|
|
|
69 |
|
|
|
70 |
/** ASSIGN_MAX_EVENT_LENGTH = 432000 ; 5 days maximum */
|
|
|
71 |
define("ASSIGN_MAX_EVENT_LENGTH", "432000");
|
|
|
72 |
|
|
|
73 |
// Name of file area for intro attachments.
|
|
|
74 |
define('ASSIGN_INTROATTACHMENT_FILEAREA', 'introattachment');
|
|
|
75 |
|
|
|
76 |
// Name of file area for activity attachments.
|
|
|
77 |
define('ASSIGN_ACTIVITYATTACHMENT_FILEAREA', 'activityattachment');
|
|
|
78 |
|
|
|
79 |
// Event types.
|
|
|
80 |
define('ASSIGN_EVENT_TYPE_DUE', 'due');
|
|
|
81 |
define('ASSIGN_EVENT_TYPE_GRADINGDUE', 'gradingdue');
|
|
|
82 |
define('ASSIGN_EVENT_TYPE_OPEN', 'open');
|
|
|
83 |
define('ASSIGN_EVENT_TYPE_CLOSE', 'close');
|
|
|
84 |
|
|
|
85 |
require_once($CFG->libdir . '/accesslib.php');
|
|
|
86 |
require_once($CFG->libdir . '/formslib.php');
|
|
|
87 |
require_once($CFG->dirroot . '/repository/lib.php');
|
|
|
88 |
require_once($CFG->dirroot . '/mod/assign/mod_form.php');
|
|
|
89 |
require_once($CFG->libdir . '/gradelib.php');
|
|
|
90 |
require_once($CFG->dirroot . '/grade/grading/lib.php');
|
|
|
91 |
require_once($CFG->dirroot . '/mod/assign/feedbackplugin.php');
|
|
|
92 |
require_once($CFG->dirroot . '/mod/assign/submissionplugin.php');
|
|
|
93 |
require_once($CFG->dirroot . '/mod/assign/renderable.php');
|
|
|
94 |
require_once($CFG->dirroot . '/mod/assign/gradingtable.php');
|
|
|
95 |
require_once($CFG->libdir . '/portfolio/caller.php');
|
|
|
96 |
|
|
|
97 |
use mod_assign\event\submission_removed;
|
|
|
98 |
use mod_assign\event\submission_status_updated;
|
|
|
99 |
use \mod_assign\output\grading_app;
|
|
|
100 |
use \mod_assign\output\assign_header;
|
|
|
101 |
use \mod_assign\output\assign_submission_status;
|
|
|
102 |
use mod_assign\output\timelimit_panel;
|
|
|
103 |
use mod_assign\downloader;
|
|
|
104 |
|
|
|
105 |
/**
|
|
|
106 |
* Standard base class for mod_assign (assignment types).
|
|
|
107 |
*
|
|
|
108 |
* @package mod_assign
|
|
|
109 |
* @copyright 2012 NetSpot {@link http://www.netspot.com.au}
|
|
|
110 |
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
111 |
*/
|
1254 |
ariadna |
112 |
class assign
|
|
|
113 |
{
|
1 |
efrain |
114 |
|
|
|
115 |
/** @var stdClass the assignment record that contains the global settings for this assign instance */
|
|
|
116 |
private $instance;
|
|
|
117 |
|
|
|
118 |
/** @var array $var array an array containing per-user assignment records, each having calculated properties (e.g. dates) */
|
|
|
119 |
private $userinstances = [];
|
|
|
120 |
|
|
|
121 |
/** @var grade_item the grade_item record for this assign instance's primary grade item. */
|
|
|
122 |
private $gradeitem;
|
|
|
123 |
|
|
|
124 |
/** @var context the context of the course module for this assign instance
|
|
|
125 |
* (or just the course if we are creating a new one)
|
|
|
126 |
*/
|
|
|
127 |
private $context;
|
|
|
128 |
|
|
|
129 |
/** @var stdClass the course this assign instance belongs to */
|
|
|
130 |
private $course;
|
|
|
131 |
|
|
|
132 |
/** @var stdClass the admin config for all assign instances */
|
|
|
133 |
private $adminconfig;
|
|
|
134 |
|
|
|
135 |
/** @var assign_renderer the custom renderer for this module */
|
|
|
136 |
private $output;
|
|
|
137 |
|
|
|
138 |
/** @var cm_info the course module for this assign instance */
|
|
|
139 |
private $coursemodule;
|
|
|
140 |
|
|
|
141 |
/** @var array cache for things like the coursemodule name or the scale menu -
|
|
|
142 |
* only lives for a single request.
|
|
|
143 |
*/
|
|
|
144 |
private $cache;
|
|
|
145 |
|
|
|
146 |
/** @var array list of the installed submission plugins */
|
|
|
147 |
private $submissionplugins;
|
|
|
148 |
|
|
|
149 |
/** @var array list of the installed feedback plugins */
|
|
|
150 |
private $feedbackplugins;
|
|
|
151 |
|
|
|
152 |
/** @var string action to be used to return to this page
|
|
|
153 |
* (without repeating any form submissions etc).
|
|
|
154 |
*/
|
|
|
155 |
private $returnaction = 'view';
|
|
|
156 |
|
|
|
157 |
/** @var array params to be used to return to this page */
|
|
|
158 |
private $returnparams = array();
|
|
|
159 |
|
|
|
160 |
/** @var string modulename prevents excessive calls to get_string */
|
|
|
161 |
private static $modulename = null;
|
|
|
162 |
|
|
|
163 |
/** @var string modulenameplural prevents excessive calls to get_string */
|
|
|
164 |
private static $modulenameplural = null;
|
|
|
165 |
|
|
|
166 |
/** @var array of marking workflow states for the current user */
|
|
|
167 |
private $markingworkflowstates = null;
|
|
|
168 |
|
|
|
169 |
/** @var array of all marking workflow states */
|
|
|
170 |
private $allmarkingworkflowstates = null;
|
|
|
171 |
|
|
|
172 |
/** @var bool whether to exclude users with inactive enrolment */
|
|
|
173 |
private $showonlyactiveenrol = null;
|
|
|
174 |
|
|
|
175 |
/** @var string A key used to identify userlists created by this object. */
|
|
|
176 |
private $useridlistid = null;
|
|
|
177 |
|
|
|
178 |
/** @var array cached list of participants for this assignment. The cache key will be group, showactive and the context id */
|
|
|
179 |
private $participants = array();
|
|
|
180 |
|
|
|
181 |
/** @var array cached list of user groups when team submissions are enabled. The cache key will be the user. */
|
|
|
182 |
private $usersubmissiongroups = array();
|
|
|
183 |
|
|
|
184 |
/** @var array cached list of user groups. The cache key will be the user. */
|
|
|
185 |
private $usergroups = array();
|
|
|
186 |
|
|
|
187 |
/** @var array cached list of IDs of users who share group membership with the user. The cache key will be the user. */
|
|
|
188 |
private $sharedgroupmembers = array();
|
|
|
189 |
|
|
|
190 |
/**
|
|
|
191 |
* @var stdClass The most recent team submission. Used to determine additional attempt numbers and whether
|
|
|
192 |
* to update the gradebook.
|
|
|
193 |
*/
|
|
|
194 |
private $mostrecentteamsubmission = null;
|
|
|
195 |
|
|
|
196 |
/** @var array Array of error messages encountered during the execution of assignment related operations. */
|
|
|
197 |
private $errors = array();
|
|
|
198 |
|
|
|
199 |
/** @var mixed This var can vary between false for no overrides to a stdClass of the overrides for a group */
|
|
|
200 |
private $overridedata;
|
|
|
201 |
|
|
|
202 |
/** @var float grade value. */
|
|
|
203 |
public $grade;
|
|
|
204 |
|
|
|
205 |
/**
|
|
|
206 |
* Constructor for the base assign class.
|
|
|
207 |
*
|
|
|
208 |
* Note: For $coursemodule you can supply a stdclass if you like, but it
|
|
|
209 |
* will be more efficient to supply a cm_info object.
|
|
|
210 |
*
|
|
|
211 |
* @param mixed $coursemodulecontext context|null the course module context
|
|
|
212 |
* (or the course context if the coursemodule has not been
|
|
|
213 |
* created yet).
|
|
|
214 |
* @param mixed $coursemodule the current course module if it was already loaded,
|
|
|
215 |
* otherwise this class will load one from the context as required.
|
|
|
216 |
* @param mixed $course the current course if it was already loaded,
|
|
|
217 |
* otherwise this class will load one from the context as required.
|
|
|
218 |
*/
|
1254 |
ariadna |
219 |
public function __construct($coursemodulecontext, $coursemodule, $course)
|
|
|
220 |
{
|
1 |
efrain |
221 |
$this->context = $coursemodulecontext;
|
|
|
222 |
$this->course = $course;
|
|
|
223 |
|
|
|
224 |
// Ensure that $this->coursemodule is a cm_info object (or null).
|
|
|
225 |
$this->coursemodule = cm_info::create($coursemodule);
|
|
|
226 |
|
|
|
227 |
// Temporary cache only lives for a single request - used to reduce db lookups.
|
|
|
228 |
$this->cache = array();
|
|
|
229 |
|
|
|
230 |
$this->submissionplugins = $this->load_plugins('assignsubmission');
|
|
|
231 |
$this->feedbackplugins = $this->load_plugins('assignfeedback');
|
|
|
232 |
|
|
|
233 |
// Extra entropy is required for uniqid() to work on cygwin.
|
|
|
234 |
$this->useridlistid = clean_param(uniqid('', true), PARAM_ALPHANUM);
|
|
|
235 |
}
|
|
|
236 |
|
|
|
237 |
/**
|
|
|
238 |
* Set the action and parameters that can be used to return to the current page.
|
|
|
239 |
*
|
|
|
240 |
* @param string $action The action for the current page
|
|
|
241 |
* @param array $params An array of name value pairs which form the parameters
|
|
|
242 |
* to return to the current page.
|
|
|
243 |
* @return void
|
|
|
244 |
*/
|
1254 |
ariadna |
245 |
public function register_return_link($action, $params)
|
|
|
246 |
{
|
1 |
efrain |
247 |
global $PAGE;
|
|
|
248 |
$params['action'] = $action;
|
|
|
249 |
$cm = $this->get_course_module();
|
|
|
250 |
if ($cm) {
|
|
|
251 |
$currenturl = new moodle_url('/mod/assign/view.php', array('id' => $cm->id));
|
|
|
252 |
} else {
|
|
|
253 |
$currenturl = new moodle_url('/mod/assign/index.php', array('id' => $this->get_course()->id));
|
|
|
254 |
}
|
|
|
255 |
|
|
|
256 |
$currenturl->params($params);
|
|
|
257 |
$PAGE->set_url($currenturl);
|
|
|
258 |
}
|
|
|
259 |
|
|
|
260 |
/**
|
|
|
261 |
* Return an action that can be used to get back to the current page.
|
|
|
262 |
*
|
|
|
263 |
* @return string action
|
|
|
264 |
*/
|
1254 |
ariadna |
265 |
public function get_return_action()
|
|
|
266 |
{
|
1 |
efrain |
267 |
global $PAGE;
|
|
|
268 |
|
|
|
269 |
// Web services don't set a URL, we should avoid debugging when ussing the url object.
|
|
|
270 |
if (!WS_SERVER) {
|
|
|
271 |
$params = $PAGE->url->params();
|
|
|
272 |
}
|
|
|
273 |
|
|
|
274 |
if (!empty($params['action'])) {
|
|
|
275 |
return $params['action'];
|
|
|
276 |
}
|
|
|
277 |
return '';
|
|
|
278 |
}
|
|
|
279 |
|
|
|
280 |
/**
|
|
|
281 |
* Based on the current assignment settings should we display the intro.
|
|
|
282 |
*
|
|
|
283 |
* @return bool showintro
|
|
|
284 |
*/
|
1254 |
ariadna |
285 |
public function show_intro()
|
|
|
286 |
{
|
|
|
287 |
if (
|
|
|
288 |
$this->get_instance()->alwaysshowdescription ||
|
|
|
289 |
time() > $this->get_instance()->allowsubmissionsfromdate
|
|
|
290 |
) {
|
1 |
efrain |
291 |
return true;
|
|
|
292 |
}
|
|
|
293 |
return false;
|
|
|
294 |
}
|
|
|
295 |
|
|
|
296 |
/**
|
|
|
297 |
* Return a list of parameters that can be used to get back to the current page.
|
|
|
298 |
*
|
|
|
299 |
* @return array params
|
|
|
300 |
*/
|
1254 |
ariadna |
301 |
public function get_return_params()
|
|
|
302 |
{
|
1 |
efrain |
303 |
global $PAGE;
|
|
|
304 |
|
|
|
305 |
$params = array();
|
|
|
306 |
if (!WS_SERVER) {
|
|
|
307 |
$params = $PAGE->url->params();
|
|
|
308 |
}
|
|
|
309 |
unset($params['id']);
|
|
|
310 |
unset($params['action']);
|
|
|
311 |
return $params;
|
|
|
312 |
}
|
|
|
313 |
|
|
|
314 |
/**
|
|
|
315 |
* Set the submitted form data.
|
|
|
316 |
*
|
|
|
317 |
* @param stdClass $data The form data (instance)
|
|
|
318 |
*/
|
1254 |
ariadna |
319 |
public function set_instance(stdClass $data)
|
|
|
320 |
{
|
1 |
efrain |
321 |
$this->instance = $data;
|
|
|
322 |
}
|
|
|
323 |
|
|
|
324 |
/**
|
|
|
325 |
* Set the context.
|
|
|
326 |
*
|
|
|
327 |
* @param context $context The new context
|
|
|
328 |
*/
|
1254 |
ariadna |
329 |
public function set_context(context $context)
|
|
|
330 |
{
|
1 |
efrain |
331 |
$this->context = $context;
|
|
|
332 |
}
|
|
|
333 |
|
|
|
334 |
/**
|
|
|
335 |
* Set the course data.
|
|
|
336 |
*
|
|
|
337 |
* @param stdClass $course The course data
|
|
|
338 |
*/
|
1254 |
ariadna |
339 |
public function set_course(stdClass $course)
|
|
|
340 |
{
|
1 |
efrain |
341 |
$this->course = $course;
|
|
|
342 |
}
|
|
|
343 |
|
|
|
344 |
/**
|
|
|
345 |
* Set error message.
|
|
|
346 |
*
|
|
|
347 |
* @param string $message The error message
|
|
|
348 |
*/
|
1254 |
ariadna |
349 |
protected function set_error_message(string $message)
|
|
|
350 |
{
|
1 |
efrain |
351 |
$this->errors[] = $message;
|
|
|
352 |
}
|
|
|
353 |
|
|
|
354 |
/**
|
|
|
355 |
* Get error messages.
|
|
|
356 |
*
|
|
|
357 |
* @return array The array of error messages
|
|
|
358 |
*/
|
1254 |
ariadna |
359 |
protected function get_error_messages(): array
|
|
|
360 |
{
|
1 |
efrain |
361 |
return $this->errors;
|
|
|
362 |
}
|
|
|
363 |
|
|
|
364 |
/**
|
|
|
365 |
* Get list of feedback plugins installed.
|
|
|
366 |
*
|
|
|
367 |
* @return array
|
|
|
368 |
*/
|
1254 |
ariadna |
369 |
public function get_feedback_plugins()
|
|
|
370 |
{
|
1 |
efrain |
371 |
return $this->feedbackplugins;
|
|
|
372 |
}
|
|
|
373 |
|
|
|
374 |
/**
|
|
|
375 |
* Get list of submission plugins installed.
|
|
|
376 |
*
|
|
|
377 |
* @return array
|
|
|
378 |
*/
|
1254 |
ariadna |
379 |
public function get_submission_plugins()
|
|
|
380 |
{
|
1 |
efrain |
381 |
return $this->submissionplugins;
|
|
|
382 |
}
|
|
|
383 |
|
|
|
384 |
/**
|
|
|
385 |
* Is blind marking enabled and reveal identities not set yet?
|
|
|
386 |
*
|
|
|
387 |
* @return bool
|
|
|
388 |
*/
|
1254 |
ariadna |
389 |
public function is_blind_marking()
|
|
|
390 |
{
|
1 |
efrain |
391 |
return $this->get_instance()->blindmarking && !$this->get_instance()->revealidentities;
|
|
|
392 |
}
|
|
|
393 |
|
|
|
394 |
/**
|
|
|
395 |
* Is hidden grading enabled?
|
|
|
396 |
*
|
|
|
397 |
* This just checks the assignment settings. Remember to check
|
|
|
398 |
* the user has the 'showhiddengrader' capability too
|
|
|
399 |
*
|
|
|
400 |
* @return bool
|
|
|
401 |
*/
|
1254 |
ariadna |
402 |
public function is_hidden_grader()
|
|
|
403 |
{
|
1 |
efrain |
404 |
return $this->get_instance()->hidegrader;
|
|
|
405 |
}
|
|
|
406 |
|
|
|
407 |
/**
|
|
|
408 |
* Does an assignment have submission(s) or grade(s) already?
|
|
|
409 |
*
|
|
|
410 |
* @return bool
|
|
|
411 |
*/
|
1254 |
ariadna |
412 |
public function has_submissions_or_grades()
|
|
|
413 |
{
|
1 |
efrain |
414 |
$allgrades = $this->count_grades();
|
|
|
415 |
$allsubmissions = $this->count_submissions();
|
|
|
416 |
if (($allgrades == 0) && ($allsubmissions == 0)) {
|
|
|
417 |
return false;
|
|
|
418 |
}
|
|
|
419 |
return true;
|
|
|
420 |
}
|
|
|
421 |
|
|
|
422 |
/**
|
|
|
423 |
* Get a specific submission plugin by its type.
|
|
|
424 |
*
|
|
|
425 |
* @param string $subtype assignsubmission | assignfeedback
|
|
|
426 |
* @param string $type
|
|
|
427 |
* @return mixed assign_plugin|null
|
|
|
428 |
*/
|
1254 |
ariadna |
429 |
public function get_plugin_by_type($subtype, $type)
|
|
|
430 |
{
|
1 |
efrain |
431 |
$shortsubtype = substr($subtype, strlen('assign'));
|
|
|
432 |
$name = $shortsubtype . 'plugins';
|
|
|
433 |
if ($name != 'feedbackplugins' && $name != 'submissionplugins') {
|
|
|
434 |
return null;
|
|
|
435 |
}
|
|
|
436 |
$pluginlist = $this->$name;
|
|
|
437 |
foreach ($pluginlist as $plugin) {
|
|
|
438 |
if ($plugin->get_type() == $type) {
|
|
|
439 |
return $plugin;
|
|
|
440 |
}
|
|
|
441 |
}
|
|
|
442 |
return null;
|
|
|
443 |
}
|
|
|
444 |
|
|
|
445 |
/**
|
|
|
446 |
* Get a feedback plugin by type.
|
|
|
447 |
*
|
|
|
448 |
* @param string $type - The type of plugin e.g comments
|
|
|
449 |
* @return mixed assign_feedback_plugin|null
|
|
|
450 |
*/
|
1254 |
ariadna |
451 |
public function get_feedback_plugin_by_type($type)
|
|
|
452 |
{
|
1 |
efrain |
453 |
return $this->get_plugin_by_type('assignfeedback', $type);
|
|
|
454 |
}
|
|
|
455 |
|
|
|
456 |
/**
|
|
|
457 |
* Get a submission plugin by type.
|
|
|
458 |
*
|
|
|
459 |
* @param string $type - The type of plugin e.g comments
|
|
|
460 |
* @return mixed assign_submission_plugin|null
|
|
|
461 |
*/
|
1254 |
ariadna |
462 |
public function get_submission_plugin_by_type($type)
|
|
|
463 |
{
|
1 |
efrain |
464 |
return $this->get_plugin_by_type('assignsubmission', $type);
|
|
|
465 |
}
|
|
|
466 |
|
|
|
467 |
/**
|
|
|
468 |
* Load the plugins from the sub folders under subtype.
|
|
|
469 |
*
|
|
|
470 |
* @param string $subtype - either submission or feedback
|
|
|
471 |
* @return array - The sorted list of plugins
|
|
|
472 |
*/
|
1254 |
ariadna |
473 |
public function load_plugins($subtype)
|
|
|
474 |
{
|
1 |
efrain |
475 |
global $CFG;
|
|
|
476 |
$result = array();
|
|
|
477 |
|
|
|
478 |
$names = core_component::get_plugin_list($subtype);
|
|
|
479 |
|
|
|
480 |
foreach ($names as $name => $path) {
|
|
|
481 |
if (file_exists($path . '/locallib.php')) {
|
|
|
482 |
require_once($path . '/locallib.php');
|
|
|
483 |
|
|
|
484 |
$shortsubtype = substr($subtype, strlen('assign'));
|
|
|
485 |
$pluginclass = 'assign_' . $shortsubtype . '_' . $name;
|
|
|
486 |
|
|
|
487 |
$plugin = new $pluginclass($this, $name);
|
|
|
488 |
|
|
|
489 |
if ($plugin instanceof assign_plugin) {
|
|
|
490 |
$idx = $plugin->get_sort_order();
|
|
|
491 |
while (array_key_exists($idx, $result)) {
|
1254 |
ariadna |
492 |
$idx += 1;
|
1 |
efrain |
493 |
}
|
|
|
494 |
$result[$idx] = $plugin;
|
|
|
495 |
}
|
|
|
496 |
}
|
|
|
497 |
}
|
|
|
498 |
ksort($result);
|
|
|
499 |
return $result;
|
|
|
500 |
}
|
|
|
501 |
|
|
|
502 |
/**
|
|
|
503 |
* Display the assignment, used by view.php
|
|
|
504 |
*
|
|
|
505 |
* The assignment is displayed differently depending on your role,
|
|
|
506 |
* the settings for the assignment and the status of the assignment.
|
|
|
507 |
*
|
|
|
508 |
* @param string $action The current action if any.
|
|
|
509 |
* @param array $args Optional arguments to pass to the view (instead of getting them from GET and POST).
|
|
|
510 |
* @return string - The page output.
|
|
|
511 |
*/
|
1254 |
ariadna |
512 |
public function view($action = '', $args = array())
|
|
|
513 |
{
|
1 |
efrain |
514 |
global $PAGE;
|
|
|
515 |
|
|
|
516 |
$o = '';
|
|
|
517 |
$mform = null;
|
|
|
518 |
$notices = array();
|
|
|
519 |
$nextpageparams = array();
|
|
|
520 |
|
|
|
521 |
if (!empty($this->get_course_module()->id)) {
|
|
|
522 |
$nextpageparams['id'] = $this->get_course_module()->id;
|
|
|
523 |
}
|
|
|
524 |
|
|
|
525 |
if (empty($action)) {
|
|
|
526 |
$PAGE->add_body_class('limitedwidth');
|
|
|
527 |
}
|
|
|
528 |
|
|
|
529 |
// Handle form submissions first.
|
|
|
530 |
if ($action == 'savesubmission') {
|
|
|
531 |
$action = 'editsubmission';
|
|
|
532 |
if ($this->process_save_submission($mform, $notices)) {
|
|
|
533 |
$action = 'redirect';
|
|
|
534 |
if ($this->can_grade()) {
|
|
|
535 |
$nextpageparams['action'] = 'grading';
|
|
|
536 |
} else {
|
|
|
537 |
$nextpageparams['action'] = 'view';
|
|
|
538 |
}
|
|
|
539 |
}
|
|
|
540 |
} else if ($action == 'editprevioussubmission') {
|
|
|
541 |
$action = 'editsubmission';
|
|
|
542 |
if ($this->process_copy_previous_attempt($notices)) {
|
|
|
543 |
$action = 'redirect';
|
|
|
544 |
$nextpageparams['action'] = 'editsubmission';
|
|
|
545 |
}
|
|
|
546 |
} else if ($action == 'lock') {
|
|
|
547 |
$this->process_lock_submission();
|
|
|
548 |
$action = 'redirect';
|
|
|
549 |
$nextpageparams['action'] = 'grading';
|
|
|
550 |
} else if ($action == 'removesubmission') {
|
|
|
551 |
$this->process_remove_submission();
|
|
|
552 |
$action = 'redirect';
|
|
|
553 |
if ($this->can_grade()) {
|
|
|
554 |
$nextpageparams['action'] = 'grading';
|
|
|
555 |
} else {
|
|
|
556 |
$nextpageparams['action'] = 'view';
|
|
|
557 |
}
|
|
|
558 |
} else if ($action == 'addattempt') {
|
|
|
559 |
$this->process_add_attempt(required_param('userid', PARAM_INT));
|
|
|
560 |
$action = 'redirect';
|
|
|
561 |
$nextpageparams['action'] = 'grading';
|
|
|
562 |
} else if ($action == 'reverttodraft') {
|
|
|
563 |
$this->process_revert_to_draft();
|
|
|
564 |
$action = 'redirect';
|
|
|
565 |
$nextpageparams['action'] = 'grading';
|
|
|
566 |
} else if ($action == 'unlock') {
|
|
|
567 |
$this->process_unlock_submission();
|
|
|
568 |
$action = 'redirect';
|
|
|
569 |
$nextpageparams['action'] = 'grading';
|
|
|
570 |
} else if ($action == 'setbatchmarkingworkflowstate') {
|
|
|
571 |
$this->process_set_batch_marking_workflow_state();
|
|
|
572 |
$action = 'redirect';
|
|
|
573 |
$nextpageparams['action'] = 'grading';
|
|
|
574 |
} else if ($action == 'setbatchmarkingallocation') {
|
|
|
575 |
$this->process_set_batch_marking_allocation();
|
|
|
576 |
$action = 'redirect';
|
|
|
577 |
$nextpageparams['action'] = 'grading';
|
|
|
578 |
} else if ($action == 'confirmsubmit') {
|
|
|
579 |
$action = 'submit';
|
|
|
580 |
if ($this->process_submit_for_grading($mform, $notices)) {
|
|
|
581 |
$action = 'redirect';
|
|
|
582 |
$nextpageparams['action'] = 'view';
|
|
|
583 |
} else if ($notices) {
|
|
|
584 |
$action = 'viewsubmitforgradingerror';
|
|
|
585 |
}
|
|
|
586 |
} else if ($action == 'submitotherforgrading') {
|
|
|
587 |
if ($this->process_submit_other_for_grading($mform, $notices)) {
|
|
|
588 |
$action = 'redirect';
|
|
|
589 |
$nextpageparams['action'] = 'grading';
|
|
|
590 |
} else {
|
|
|
591 |
$action = 'viewsubmitforgradingerror';
|
|
|
592 |
}
|
|
|
593 |
} else if ($action == 'gradingbatchoperation') {
|
|
|
594 |
$action = $this->process_grading_batch_operation($mform);
|
|
|
595 |
if ($action == 'grading') {
|
|
|
596 |
$action = 'redirect';
|
|
|
597 |
$nextpageparams['action'] = 'grading';
|
|
|
598 |
}
|
|
|
599 |
} else if ($action == 'submitgrade') {
|
|
|
600 |
if (optional_param('saveandshownext', null, PARAM_RAW)) {
|
|
|
601 |
// Save and show next.
|
|
|
602 |
$action = 'grade';
|
|
|
603 |
if ($this->process_save_grade($mform)) {
|
|
|
604 |
$action = 'redirect';
|
|
|
605 |
$nextpageparams['action'] = 'grade';
|
|
|
606 |
$nextpageparams['rownum'] = optional_param('rownum', 0, PARAM_INT) + 1;
|
|
|
607 |
$nextpageparams['useridlistid'] = optional_param('useridlistid', $this->get_useridlist_key_id(), PARAM_ALPHANUM);
|
|
|
608 |
}
|
|
|
609 |
} else if (optional_param('nosaveandprevious', null, PARAM_RAW)) {
|
|
|
610 |
$action = 'redirect';
|
|
|
611 |
$nextpageparams['action'] = 'grade';
|
|
|
612 |
$nextpageparams['rownum'] = optional_param('rownum', 0, PARAM_INT) - 1;
|
|
|
613 |
$nextpageparams['useridlistid'] = optional_param('useridlistid', $this->get_useridlist_key_id(), PARAM_ALPHANUM);
|
|
|
614 |
} else if (optional_param('nosaveandnext', null, PARAM_RAW)) {
|
|
|
615 |
$action = 'redirect';
|
|
|
616 |
$nextpageparams['action'] = 'grade';
|
|
|
617 |
$nextpageparams['rownum'] = optional_param('rownum', 0, PARAM_INT) + 1;
|
|
|
618 |
$nextpageparams['useridlistid'] = optional_param('useridlistid', $this->get_useridlist_key_id(), PARAM_ALPHANUM);
|
|
|
619 |
} else if (optional_param('savegrade', null, PARAM_RAW)) {
|
|
|
620 |
// Save changes button.
|
|
|
621 |
$action = 'grade';
|
|
|
622 |
if ($this->process_save_grade($mform)) {
|
|
|
623 |
$action = 'redirect';
|
|
|
624 |
$nextpageparams['action'] = 'savegradingresult';
|
|
|
625 |
}
|
|
|
626 |
} else {
|
|
|
627 |
// Cancel button.
|
|
|
628 |
$action = 'redirect';
|
|
|
629 |
$nextpageparams['action'] = 'grading';
|
|
|
630 |
}
|
|
|
631 |
} else if ($action == 'quickgrade') {
|
|
|
632 |
$message = $this->process_save_quick_grades();
|
|
|
633 |
$action = 'quickgradingresult';
|
|
|
634 |
} else if ($action == 'saveoptions') {
|
|
|
635 |
$this->process_save_grading_options();
|
|
|
636 |
$action = 'redirect';
|
|
|
637 |
$nextpageparams['action'] = 'grading';
|
|
|
638 |
} else if ($action == 'saveextension') {
|
|
|
639 |
$action = 'grantextension';
|
|
|
640 |
if ($this->process_save_extension($mform)) {
|
|
|
641 |
$action = 'redirect';
|
|
|
642 |
$nextpageparams['action'] = 'grading';
|
|
|
643 |
}
|
|
|
644 |
} else if ($action == 'revealidentitiesconfirm') {
|
|
|
645 |
$this->process_reveal_identities();
|
|
|
646 |
$action = 'redirect';
|
|
|
647 |
$nextpageparams['action'] = 'grading';
|
|
|
648 |
}
|
|
|
649 |
|
1254 |
ariadna |
650 |
$returnparams = array(
|
|
|
651 |
'rownum' => optional_param('rownum', 0, PARAM_INT),
|
|
|
652 |
'useridlistid' => optional_param('useridlistid', $this->get_useridlist_key_id(), PARAM_ALPHANUM)
|
|
|
653 |
);
|
1 |
efrain |
654 |
$this->register_return_link($action, $returnparams);
|
|
|
655 |
|
|
|
656 |
// Include any page action as part of the body tag CSS id.
|
|
|
657 |
if (!empty($action)) {
|
|
|
658 |
$PAGE->set_pagetype('mod-assign-' . $action);
|
|
|
659 |
}
|
|
|
660 |
// Now show the right view page.
|
|
|
661 |
if ($action == 'redirect') {
|
|
|
662 |
$nextpageurl = new moodle_url('/mod/assign/view.php', $nextpageparams);
|
|
|
663 |
$messages = '';
|
|
|
664 |
$messagetype = \core\output\notification::NOTIFY_INFO;
|
|
|
665 |
$errors = $this->get_error_messages();
|
|
|
666 |
if (!empty($errors)) {
|
|
|
667 |
$messages = html_writer::alist($errors, ['class' => 'mb-1 mt-1']);
|
|
|
668 |
$messagetype = \core\output\notification::NOTIFY_ERROR;
|
|
|
669 |
}
|
|
|
670 |
redirect($nextpageurl, $messages, null, $messagetype);
|
|
|
671 |
return;
|
|
|
672 |
} else if ($action == 'savegradingresult') {
|
|
|
673 |
$message = get_string('gradingchangessaved', 'assign');
|
|
|
674 |
$o .= $this->view_savegrading_result($message);
|
|
|
675 |
} else if ($action == 'quickgradingresult') {
|
|
|
676 |
$mform = null;
|
|
|
677 |
$o .= $this->view_quickgrading_result($message);
|
|
|
678 |
} else if ($action == 'gradingpanel') {
|
|
|
679 |
$o .= $this->view_single_grading_panel($args);
|
|
|
680 |
} else if ($action == 'grade') {
|
|
|
681 |
$o .= $this->view_single_grade_page($mform);
|
|
|
682 |
} else if ($action == 'viewpluginassignfeedback') {
|
|
|
683 |
$o .= $this->view_plugin_content('assignfeedback');
|
|
|
684 |
} else if ($action == 'viewpluginassignsubmission') {
|
|
|
685 |
$o .= $this->view_plugin_content('assignsubmission');
|
|
|
686 |
} else if ($action == 'editsubmission') {
|
|
|
687 |
$PAGE->add_body_class('limitedwidth');
|
|
|
688 |
$o .= $this->view_edit_submission_page($mform, $notices);
|
|
|
689 |
} else if ($action == 'grader') {
|
|
|
690 |
$o .= $this->view_grader();
|
|
|
691 |
} else if ($action == 'grading') {
|
|
|
692 |
$o .= $this->view_grading_page();
|
|
|
693 |
} else if ($action == 'downloadall') {
|
|
|
694 |
$o .= $this->download_submissions();
|
|
|
695 |
} else if ($action == 'submit') {
|
|
|
696 |
$PAGE->add_body_class('limitedwidth');
|
|
|
697 |
$o .= $this->check_submit_for_grading($mform);
|
|
|
698 |
} else if ($action == 'grantextension') {
|
|
|
699 |
$o .= $this->view_grant_extension($mform);
|
|
|
700 |
} else if ($action == 'revealidentities') {
|
|
|
701 |
$o .= $this->view_reveal_identities_confirm($mform);
|
|
|
702 |
} else if ($action == 'removesubmissionconfirm') {
|
|
|
703 |
$PAGE->add_body_class('limitedwidth');
|
|
|
704 |
$o .= $this->view_remove_submission_confirm();
|
|
|
705 |
} else if ($action == 'plugingradingbatchoperation') {
|
|
|
706 |
$o .= $this->view_plugin_grading_batch_operation($mform);
|
|
|
707 |
} else if ($action == 'viewpluginpage') {
|
1254 |
ariadna |
708 |
$o .= $this->view_plugin_page();
|
1 |
efrain |
709 |
} else if ($action == 'viewcourseindex') {
|
1254 |
ariadna |
710 |
$o .= $this->view_course_index();
|
1 |
efrain |
711 |
} else if ($action == 'viewbatchsetmarkingworkflowstate') {
|
1254 |
ariadna |
712 |
$o .= $this->view_batch_set_workflow_state($mform);
|
1 |
efrain |
713 |
} else if ($action == 'viewbatchmarkingallocation') {
|
|
|
714 |
$o .= $this->view_batch_markingallocation($mform);
|
|
|
715 |
} else if ($action == 'viewsubmitforgradingerror') {
|
|
|
716 |
$o .= $this->view_error_page(get_string('submitforgrading', 'assign'), $notices);
|
|
|
717 |
} else if ($action == 'fixrescalednullgrades') {
|
|
|
718 |
$o .= $this->view_fix_rescaled_null_grades();
|
|
|
719 |
} else {
|
|
|
720 |
$PAGE->add_body_class('limitedwidth');
|
|
|
721 |
$o .= $this->view_submission_page();
|
|
|
722 |
}
|
|
|
723 |
|
|
|
724 |
return $o;
|
|
|
725 |
}
|
|
|
726 |
|
|
|
727 |
/**
|
|
|
728 |
* Add this instance to the database.
|
|
|
729 |
*
|
|
|
730 |
* @param stdClass $formdata The data submitted from the form
|
|
|
731 |
* @param bool $callplugins This is used to skip the plugin code
|
|
|
732 |
* when upgrading an old assignment to a new one (the plugins get called manually)
|
|
|
733 |
* @return mixed false if an error occurs or the int id of the new instance
|
|
|
734 |
*/
|
1254 |
ariadna |
735 |
public function add_instance(stdClass $formdata, $callplugins)
|
|
|
736 |
{
|
1 |
efrain |
737 |
global $DB;
|
|
|
738 |
$adminconfig = $this->get_admin_config();
|
|
|
739 |
|
|
|
740 |
$err = '';
|
|
|
741 |
|
|
|
742 |
// Add the database record.
|
|
|
743 |
$update = new stdClass();
|
|
|
744 |
$update->name = $formdata->name;
|
|
|
745 |
$update->timemodified = time();
|
|
|
746 |
$update->timecreated = time();
|
|
|
747 |
$update->course = $formdata->course;
|
|
|
748 |
$update->courseid = $formdata->course;
|
|
|
749 |
$update->intro = $formdata->intro;
|
|
|
750 |
$update->introformat = $formdata->introformat;
|
|
|
751 |
$update->alwaysshowdescription = !empty($formdata->alwaysshowdescription);
|
|
|
752 |
if (isset($formdata->activityeditor)) {
|
|
|
753 |
$update->activity = $this->save_editor_draft_files($formdata);
|
|
|
754 |
$update->activityformat = $formdata->activityeditor['format'];
|
|
|
755 |
}
|
|
|
756 |
if (isset($formdata->submissionattachments)) {
|
|
|
757 |
$update->submissionattachments = $formdata->submissionattachments;
|
|
|
758 |
}
|
|
|
759 |
$update->submissiondrafts = $formdata->submissiondrafts;
|
|
|
760 |
$update->requiresubmissionstatement = $formdata->requiresubmissionstatement;
|
|
|
761 |
$update->sendnotifications = $formdata->sendnotifications;
|
|
|
762 |
$update->sendlatenotifications = $formdata->sendlatenotifications;
|
|
|
763 |
$update->sendstudentnotifications = $adminconfig->sendstudentnotifications;
|
|
|
764 |
if (isset($formdata->sendstudentnotifications)) {
|
|
|
765 |
$update->sendstudentnotifications = $formdata->sendstudentnotifications;
|
|
|
766 |
}
|
|
|
767 |
$update->duedate = $formdata->duedate;
|
|
|
768 |
$update->cutoffdate = $formdata->cutoffdate;
|
|
|
769 |
$update->gradingduedate = $formdata->gradingduedate;
|
|
|
770 |
if (isset($formdata->timelimit)) {
|
|
|
771 |
$update->timelimit = $formdata->timelimit;
|
|
|
772 |
}
|
|
|
773 |
$update->allowsubmissionsfromdate = $formdata->allowsubmissionsfromdate;
|
|
|
774 |
$update->grade = $formdata->grade;
|
|
|
775 |
$update->completionsubmit = !empty($formdata->completionsubmit);
|
|
|
776 |
$update->teamsubmission = $formdata->teamsubmission;
|
|
|
777 |
$update->requireallteammemberssubmit = $formdata->requireallteammemberssubmit;
|
|
|
778 |
if (isset($formdata->teamsubmissiongroupingid)) {
|
|
|
779 |
$update->teamsubmissiongroupingid = $formdata->teamsubmissiongroupingid;
|
|
|
780 |
}
|
|
|
781 |
$update->blindmarking = $formdata->blindmarking;
|
|
|
782 |
if (isset($formdata->hidegrader)) {
|
|
|
783 |
$update->hidegrader = $formdata->hidegrader;
|
|
|
784 |
}
|
|
|
785 |
$update->attemptreopenmethod = ASSIGN_ATTEMPT_REOPEN_METHOD_NONE;
|
|
|
786 |
if (!empty($formdata->attemptreopenmethod)) {
|
|
|
787 |
$update->attemptreopenmethod = $formdata->attemptreopenmethod;
|
|
|
788 |
}
|
|
|
789 |
if (!empty($formdata->maxattempts)) {
|
|
|
790 |
$update->maxattempts = $formdata->maxattempts;
|
|
|
791 |
}
|
|
|
792 |
if (isset($formdata->preventsubmissionnotingroup)) {
|
|
|
793 |
$update->preventsubmissionnotingroup = $formdata->preventsubmissionnotingroup;
|
|
|
794 |
}
|
|
|
795 |
$update->markingworkflow = $formdata->markingworkflow;
|
|
|
796 |
$update->markingallocation = $formdata->markingallocation;
|
|
|
797 |
if (empty($update->markingworkflow)) { // If marking workflow is disabled, make sure allocation is disabled.
|
|
|
798 |
$update->markingallocation = 0;
|
|
|
799 |
}
|
|
|
800 |
if (isset($formdata->markinganonymous)) {
|
|
|
801 |
// If marking workflow is disabled, or anonymous submissions is disabled then make sure marking anonymous is disabled.
|
|
|
802 |
if (empty($update->markingworkflow) || empty($update->blindmarking)) {
|
|
|
803 |
$update->markinganonymous = 0;
|
|
|
804 |
} else {
|
|
|
805 |
$update->markinganonymous = $formdata->markinganonymous;
|
|
|
806 |
}
|
|
|
807 |
}
|
|
|
808 |
$returnid = $DB->insert_record('assign', $update);
|
1254 |
ariadna |
809 |
$this->instance = $DB->get_record('assign', array('id' => $returnid), '*', MUST_EXIST);
|
1 |
efrain |
810 |
// Cache the course record.
|
1254 |
ariadna |
811 |
$this->course = $DB->get_record('course', array('id' => $formdata->course), '*', MUST_EXIST);
|
1 |
efrain |
812 |
|
|
|
813 |
$this->save_intro_draft_files($formdata);
|
|
|
814 |
$this->save_editor_draft_files($formdata);
|
|
|
815 |
|
|
|
816 |
if ($callplugins) {
|
|
|
817 |
// Call save_settings hook for submission plugins.
|
|
|
818 |
foreach ($this->submissionplugins as $plugin) {
|
|
|
819 |
if (!$this->update_plugin_instance($plugin, $formdata)) {
|
|
|
820 |
throw new \moodle_exception($plugin->get_error());
|
|
|
821 |
return false;
|
|
|
822 |
}
|
|
|
823 |
}
|
|
|
824 |
foreach ($this->feedbackplugins as $plugin) {
|
|
|
825 |
if (!$this->update_plugin_instance($plugin, $formdata)) {
|
|
|
826 |
throw new \moodle_exception($plugin->get_error());
|
|
|
827 |
return false;
|
|
|
828 |
}
|
|
|
829 |
}
|
|
|
830 |
|
|
|
831 |
// In the case of upgrades the coursemodule has not been set,
|
|
|
832 |
// so we need to wait before calling these two.
|
|
|
833 |
$this->update_calendar($formdata->coursemodule);
|
|
|
834 |
if (!empty($formdata->completionexpected)) {
|
1254 |
ariadna |
835 |
\core_completion\api::update_completion_date_event(
|
|
|
836 |
$formdata->coursemodule,
|
|
|
837 |
'assign',
|
|
|
838 |
$this->instance,
|
|
|
839 |
$formdata->completionexpected
|
|
|
840 |
);
|
1 |
efrain |
841 |
}
|
|
|
842 |
$this->update_gradebook(false, $formdata->coursemodule);
|
|
|
843 |
}
|
|
|
844 |
|
|
|
845 |
$update = new stdClass();
|
|
|
846 |
$update->id = $this->get_instance()->id;
|
1254 |
ariadna |
847 |
$update->nosubmissions = (!$this->is_any_submission_plugin_enabled()) ? 1 : 0;
|
1 |
efrain |
848 |
$DB->update_record('assign', $update);
|
|
|
849 |
|
|
|
850 |
return $returnid;
|
|
|
851 |
}
|
|
|
852 |
|
|
|
853 |
/**
|
|
|
854 |
* Delete all grades from the gradebook for this assignment.
|
|
|
855 |
*
|
|
|
856 |
* @return bool
|
|
|
857 |
*/
|
1254 |
ariadna |
858 |
protected function delete_grades()
|
|
|
859 |
{
|
1 |
efrain |
860 |
global $CFG;
|
|
|
861 |
|
1254 |
ariadna |
862 |
$result = grade_update(
|
|
|
863 |
'mod/assign',
|
|
|
864 |
$this->get_course()->id,
|
|
|
865 |
'mod',
|
|
|
866 |
'assign',
|
|
|
867 |
$this->get_instance()->id,
|
|
|
868 |
0,
|
|
|
869 |
null,
|
|
|
870 |
array('deleted' => 1)
|
|
|
871 |
);
|
1 |
efrain |
872 |
return $result == GRADE_UPDATE_OK;
|
|
|
873 |
}
|
|
|
874 |
|
|
|
875 |
/**
|
|
|
876 |
* Delete this instance from the database.
|
|
|
877 |
*
|
|
|
878 |
* @return bool false if an error occurs
|
|
|
879 |
*/
|
1254 |
ariadna |
880 |
public function delete_instance()
|
|
|
881 |
{
|
1 |
efrain |
882 |
global $DB;
|
|
|
883 |
$result = true;
|
|
|
884 |
|
|
|
885 |
foreach ($this->submissionplugins as $plugin) {
|
|
|
886 |
if (!$plugin->delete_instance()) {
|
|
|
887 |
throw new \moodle_exception($plugin->get_error());
|
|
|
888 |
$result = false;
|
|
|
889 |
}
|
|
|
890 |
}
|
|
|
891 |
foreach ($this->feedbackplugins as $plugin) {
|
|
|
892 |
if (!$plugin->delete_instance()) {
|
|
|
893 |
throw new \moodle_exception($plugin->get_error());
|
|
|
894 |
$result = false;
|
|
|
895 |
}
|
|
|
896 |
}
|
|
|
897 |
|
|
|
898 |
// Delete files associated with this assignment.
|
|
|
899 |
$fs = get_file_storage();
|
1254 |
ariadna |
900 |
if (! $fs->delete_area_files($this->context->id)) {
|
1 |
efrain |
901 |
$result = false;
|
|
|
902 |
}
|
|
|
903 |
|
|
|
904 |
$this->delete_all_overrides();
|
|
|
905 |
|
|
|
906 |
// Delete_records will throw an exception if it fails - so no need for error checking here.
|
|
|
907 |
$DB->delete_records('assign_submission', array('assignment' => $this->get_instance()->id));
|
|
|
908 |
$DB->delete_records('assign_grades', array('assignment' => $this->get_instance()->id));
|
|
|
909 |
$DB->delete_records('assign_plugin_config', array('assignment' => $this->get_instance()->id));
|
|
|
910 |
$DB->delete_records('assign_user_flags', array('assignment' => $this->get_instance()->id));
|
|
|
911 |
$DB->delete_records('assign_user_mapping', array('assignment' => $this->get_instance()->id));
|
|
|
912 |
|
|
|
913 |
// Delete items from the gradebook.
|
|
|
914 |
if (! $this->delete_grades()) {
|
|
|
915 |
$result = false;
|
|
|
916 |
}
|
|
|
917 |
|
|
|
918 |
// Delete the instance.
|
|
|
919 |
// We must delete the module record after we delete the grade item.
|
1254 |
ariadna |
920 |
$DB->delete_records('assign', array('id' => $this->get_instance()->id));
|
1 |
efrain |
921 |
|
|
|
922 |
return $result;
|
|
|
923 |
}
|
|
|
924 |
|
|
|
925 |
/**
|
|
|
926 |
* Deletes a assign override from the database and clears any corresponding calendar events
|
|
|
927 |
*
|
|
|
928 |
* @param int $overrideid The id of the override being deleted
|
|
|
929 |
* @return bool true on success
|
|
|
930 |
*/
|
1254 |
ariadna |
931 |
public function delete_override($overrideid)
|
|
|
932 |
{
|
1 |
efrain |
933 |
global $CFG, $DB;
|
|
|
934 |
|
|
|
935 |
require_once($CFG->dirroot . '/calendar/lib.php');
|
|
|
936 |
|
|
|
937 |
$cm = $this->get_course_module();
|
|
|
938 |
if (empty($cm)) {
|
|
|
939 |
$instance = $this->get_instance();
|
|
|
940 |
$cm = get_coursemodule_from_instance('assign', $instance->id, $instance->course);
|
|
|
941 |
}
|
|
|
942 |
|
|
|
943 |
$override = $DB->get_record('assign_overrides', array('id' => $overrideid), '*', MUST_EXIST);
|
|
|
944 |
|
|
|
945 |
// Delete the events.
|
|
|
946 |
$conds = array('modulename' => 'assign', 'instance' => $this->get_instance()->id);
|
|
|
947 |
if (isset($override->userid)) {
|
|
|
948 |
$conds['userid'] = $override->userid;
|
|
|
949 |
$cachekey = "{$cm->instance}_u_{$override->userid}";
|
|
|
950 |
} else {
|
|
|
951 |
$conds['groupid'] = $override->groupid;
|
|
|
952 |
$cachekey = "{$cm->instance}_g_{$override->groupid}";
|
|
|
953 |
}
|
|
|
954 |
$events = $DB->get_records('event', $conds);
|
|
|
955 |
foreach ($events as $event) {
|
|
|
956 |
$eventold = calendar_event::load($event);
|
|
|
957 |
$eventold->delete();
|
|
|
958 |
}
|
|
|
959 |
|
|
|
960 |
$DB->delete_records('assign_overrides', array('id' => $overrideid));
|
|
|
961 |
cache::make('mod_assign', 'overrides')->delete($cachekey);
|
|
|
962 |
|
|
|
963 |
// Set the common parameters for one of the events we will be triggering.
|
|
|
964 |
$params = array(
|
|
|
965 |
'objectid' => $override->id,
|
|
|
966 |
'context' => context_module::instance($cm->id),
|
|
|
967 |
'other' => array(
|
|
|
968 |
'assignid' => $override->assignid
|
|
|
969 |
)
|
|
|
970 |
);
|
|
|
971 |
// Determine which override deleted event to fire.
|
|
|
972 |
if (!empty($override->userid)) {
|
|
|
973 |
$params['relateduserid'] = $override->userid;
|
|
|
974 |
$event = \mod_assign\event\user_override_deleted::create($params);
|
|
|
975 |
} else {
|
|
|
976 |
$params['other']['groupid'] = $override->groupid;
|
|
|
977 |
$event = \mod_assign\event\group_override_deleted::create($params);
|
|
|
978 |
}
|
|
|
979 |
|
|
|
980 |
// Trigger the override deleted event.
|
|
|
981 |
$event->add_record_snapshot('assign_overrides', $override);
|
|
|
982 |
$event->trigger();
|
|
|
983 |
|
|
|
984 |
return true;
|
|
|
985 |
}
|
|
|
986 |
|
|
|
987 |
/**
|
|
|
988 |
* Deletes all assign overrides from the database and clears any corresponding calendar events
|
|
|
989 |
*/
|
1254 |
ariadna |
990 |
public function delete_all_overrides()
|
|
|
991 |
{
|
1 |
efrain |
992 |
global $DB;
|
|
|
993 |
|
|
|
994 |
$overrides = $DB->get_records('assign_overrides', array('assignid' => $this->get_instance()->id), 'id');
|
|
|
995 |
foreach ($overrides as $override) {
|
|
|
996 |
$this->delete_override($override->id);
|
|
|
997 |
}
|
|
|
998 |
}
|
|
|
999 |
|
|
|
1000 |
/**
|
|
|
1001 |
* Updates the assign properties with override information for a user.
|
|
|
1002 |
*
|
|
|
1003 |
* Algorithm: For each assign setting, if there is a matching user-specific override,
|
|
|
1004 |
* then use that otherwise, if there are group-specific overrides, return the most
|
|
|
1005 |
* lenient combination of them. If neither applies, leave the assign setting unchanged.
|
|
|
1006 |
*
|
|
|
1007 |
* @param int $userid The userid.
|
|
|
1008 |
*/
|
1254 |
ariadna |
1009 |
public function update_effective_access($userid)
|
|
|
1010 |
{
|
1 |
efrain |
1011 |
|
|
|
1012 |
$override = $this->override_exists($userid);
|
|
|
1013 |
|
|
|
1014 |
// Merge with assign defaults.
|
|
|
1015 |
$keys = array('duedate', 'cutoffdate', 'allowsubmissionsfromdate', 'timelimit');
|
|
|
1016 |
foreach ($keys as $key) {
|
|
|
1017 |
if (isset($override->{$key})) {
|
|
|
1018 |
$this->get_instance($userid)->{$key} = $override->{$key};
|
|
|
1019 |
}
|
|
|
1020 |
}
|
|
|
1021 |
}
|
|
|
1022 |
|
|
|
1023 |
/**
|
|
|
1024 |
* Returns whether an assign has any overrides.
|
|
|
1025 |
*
|
|
|
1026 |
* @return true if any, false if not
|
|
|
1027 |
*/
|
1254 |
ariadna |
1028 |
public function has_overrides()
|
|
|
1029 |
{
|
1 |
efrain |
1030 |
global $DB;
|
|
|
1031 |
|
|
|
1032 |
$override = $DB->record_exists('assign_overrides', array('assignid' => $this->get_instance()->id));
|
|
|
1033 |
|
|
|
1034 |
if ($override) {
|
|
|
1035 |
return true;
|
|
|
1036 |
}
|
|
|
1037 |
|
|
|
1038 |
return false;
|
|
|
1039 |
}
|
|
|
1040 |
|
|
|
1041 |
/**
|
|
|
1042 |
* Returns user override
|
|
|
1043 |
*
|
|
|
1044 |
* Algorithm: For each assign setting, if there is a matching user-specific override,
|
|
|
1045 |
* then use that otherwise, if there are group-specific overrides, use the one with the
|
|
|
1046 |
* lowest sort order. If neither applies, leave the assign setting unchanged.
|
|
|
1047 |
*
|
|
|
1048 |
* @param int $userid The userid.
|
|
|
1049 |
* @return stdClass The override
|
|
|
1050 |
*/
|
1254 |
ariadna |
1051 |
public function override_exists($userid)
|
|
|
1052 |
{
|
1 |
efrain |
1053 |
global $DB;
|
|
|
1054 |
|
|
|
1055 |
// Gets an assoc array containing the keys for defined user overrides only.
|
1254 |
ariadna |
1056 |
$getuseroverride = function ($userid) use ($DB) {
|
1 |
efrain |
1057 |
$useroverride = $DB->get_record('assign_overrides', ['assignid' => $this->get_instance()->id, 'userid' => $userid]);
|
|
|
1058 |
return $useroverride ? get_object_vars($useroverride) : [];
|
|
|
1059 |
};
|
|
|
1060 |
|
|
|
1061 |
// Gets an assoc array containing the keys for defined group overrides only.
|
1254 |
ariadna |
1062 |
$getgroupoverride = function ($userid) use ($DB) {
|
1 |
efrain |
1063 |
$groupings = groups_get_user_groups($this->get_instance()->course, $userid);
|
|
|
1064 |
|
|
|
1065 |
if (empty($groupings[0])) {
|
|
|
1066 |
return [];
|
|
|
1067 |
}
|
|
|
1068 |
|
|
|
1069 |
// Select all overrides that apply to the User's groups.
|
|
|
1070 |
list($extra, $params) = $DB->get_in_or_equal(array_values($groupings[0]));
|
|
|
1071 |
$sql = "SELECT * FROM {assign_overrides}
|
|
|
1072 |
WHERE groupid $extra AND assignid = ? ORDER BY sortorder ASC";
|
|
|
1073 |
$params[] = $this->get_instance()->id;
|
|
|
1074 |
$groupoverride = $DB->get_record_sql($sql, $params, IGNORE_MULTIPLE);
|
|
|
1075 |
|
|
|
1076 |
return $groupoverride ? get_object_vars($groupoverride) : [];
|
|
|
1077 |
};
|
|
|
1078 |
|
|
|
1079 |
// Later arguments clobber earlier ones with array_merge. The two helper functions
|
|
|
1080 |
// return arrays containing keys for only the defined overrides. So we get the
|
|
|
1081 |
// desired behaviour as per the algorithm.
|
|
|
1082 |
return (object)array_merge(
|
|
|
1083 |
['timelimit' => null, 'duedate' => null, 'cutoffdate' => null, 'allowsubmissionsfromdate' => null],
|
|
|
1084 |
$getgroupoverride($userid),
|
|
|
1085 |
$getuseroverride($userid)
|
|
|
1086 |
);
|
|
|
1087 |
}
|
|
|
1088 |
|
|
|
1089 |
/**
|
|
|
1090 |
* Check if the given calendar_event is either a user or group override
|
|
|
1091 |
* event.
|
|
|
1092 |
*
|
|
|
1093 |
* @return bool
|
|
|
1094 |
*/
|
1254 |
ariadna |
1095 |
public function is_override_calendar_event(\calendar_event $event)
|
|
|
1096 |
{
|
1 |
efrain |
1097 |
global $DB;
|
|
|
1098 |
|
|
|
1099 |
if (!isset($event->modulename)) {
|
|
|
1100 |
return false;
|
|
|
1101 |
}
|
|
|
1102 |
|
|
|
1103 |
if ($event->modulename != 'assign') {
|
|
|
1104 |
return false;
|
|
|
1105 |
}
|
|
|
1106 |
|
|
|
1107 |
if (!isset($event->instance)) {
|
|
|
1108 |
return false;
|
|
|
1109 |
}
|
|
|
1110 |
|
|
|
1111 |
if (!isset($event->userid) && !isset($event->groupid)) {
|
|
|
1112 |
return false;
|
|
|
1113 |
}
|
|
|
1114 |
|
|
|
1115 |
$overrideparams = [
|
|
|
1116 |
'assignid' => $event->instance
|
|
|
1117 |
];
|
|
|
1118 |
|
|
|
1119 |
if (isset($event->groupid)) {
|
|
|
1120 |
$overrideparams['groupid'] = $event->groupid;
|
|
|
1121 |
} else if (isset($event->userid)) {
|
|
|
1122 |
$overrideparams['userid'] = $event->userid;
|
|
|
1123 |
}
|
|
|
1124 |
|
|
|
1125 |
if ($DB->get_record('assign_overrides', $overrideparams)) {
|
|
|
1126 |
return true;
|
|
|
1127 |
} else {
|
|
|
1128 |
return false;
|
|
|
1129 |
}
|
|
|
1130 |
}
|
|
|
1131 |
|
|
|
1132 |
/**
|
|
|
1133 |
* This function calculates the minimum and maximum cutoff values for the timestart of
|
|
|
1134 |
* the given event.
|
|
|
1135 |
*
|
|
|
1136 |
* It will return an array with two values, the first being the minimum cutoff value and
|
|
|
1137 |
* the second being the maximum cutoff value. Either or both values can be null, which
|
|
|
1138 |
* indicates there is no minimum or maximum, respectively.
|
|
|
1139 |
*
|
|
|
1140 |
* If a cutoff is required then the function must return an array containing the cutoff
|
|
|
1141 |
* timestamp and error string to display to the user if the cutoff value is violated.
|
|
|
1142 |
*
|
|
|
1143 |
* A minimum and maximum cutoff return value will look like:
|
|
|
1144 |
* [
|
|
|
1145 |
* [1505704373, 'The due date must be after the sbumission start date'],
|
|
|
1146 |
* [1506741172, 'The due date must be before the cutoff date']
|
|
|
1147 |
* ]
|
|
|
1148 |
*
|
|
|
1149 |
* If the event does not have a valid timestart range then [false, false] will
|
|
|
1150 |
* be returned.
|
|
|
1151 |
*
|
|
|
1152 |
* @param calendar_event $event The calendar event to get the time range for
|
|
|
1153 |
* @return array
|
|
|
1154 |
*/
|
1254 |
ariadna |
1155 |
function get_valid_calendar_event_timestart_range(\calendar_event $event)
|
|
|
1156 |
{
|
1 |
efrain |
1157 |
$instance = $this->get_instance();
|
|
|
1158 |
$submissionsfromdate = $instance->allowsubmissionsfromdate;
|
|
|
1159 |
$cutoffdate = $instance->cutoffdate;
|
|
|
1160 |
$duedate = $instance->duedate;
|
|
|
1161 |
$gradingduedate = $instance->gradingduedate;
|
|
|
1162 |
$mindate = null;
|
|
|
1163 |
$maxdate = null;
|
|
|
1164 |
|
|
|
1165 |
if ($event->eventtype == ASSIGN_EVENT_TYPE_DUE) {
|
|
|
1166 |
// This check is in here because due date events are currently
|
|
|
1167 |
// the only events that can be overridden, so we can save a DB
|
|
|
1168 |
// query if we don't bother checking other events.
|
|
|
1169 |
if ($this->is_override_calendar_event($event)) {
|
|
|
1170 |
// This is an override event so there is no valid timestart
|
|
|
1171 |
// range to set it to.
|
|
|
1172 |
return [false, false];
|
|
|
1173 |
}
|
|
|
1174 |
|
|
|
1175 |
if ($submissionsfromdate) {
|
|
|
1176 |
$mindate = [
|
|
|
1177 |
$submissionsfromdate,
|
|
|
1178 |
get_string('duedatevalidation', 'assign'),
|
|
|
1179 |
];
|
|
|
1180 |
}
|
|
|
1181 |
|
|
|
1182 |
if ($cutoffdate) {
|
|
|
1183 |
$maxdate = [
|
|
|
1184 |
$cutoffdate,
|
|
|
1185 |
get_string('cutoffdatevalidation', 'assign'),
|
|
|
1186 |
];
|
|
|
1187 |
}
|
|
|
1188 |
|
|
|
1189 |
if ($gradingduedate) {
|
|
|
1190 |
// If we don't have a cutoff date or we've got a grading due date
|
|
|
1191 |
// that is earlier than the cutoff then we should use that as the
|
|
|
1192 |
// upper limit for the due date.
|
|
|
1193 |
if (!$cutoffdate || $gradingduedate < $cutoffdate) {
|
|
|
1194 |
$maxdate = [
|
|
|
1195 |
$gradingduedate,
|
|
|
1196 |
get_string('gradingdueduedatevalidation', 'assign'),
|
|
|
1197 |
];
|
|
|
1198 |
}
|
|
|
1199 |
}
|
|
|
1200 |
} else if ($event->eventtype == ASSIGN_EVENT_TYPE_GRADINGDUE) {
|
|
|
1201 |
if ($duedate) {
|
|
|
1202 |
$mindate = [
|
|
|
1203 |
$duedate,
|
|
|
1204 |
get_string('gradingdueduedatevalidation', 'assign'),
|
|
|
1205 |
];
|
|
|
1206 |
} else if ($submissionsfromdate) {
|
|
|
1207 |
$mindate = [
|
|
|
1208 |
$submissionsfromdate,
|
|
|
1209 |
get_string('gradingduefromdatevalidation', 'assign'),
|
|
|
1210 |
];
|
|
|
1211 |
}
|
|
|
1212 |
}
|
|
|
1213 |
|
|
|
1214 |
return [$mindate, $maxdate];
|
|
|
1215 |
}
|
|
|
1216 |
|
|
|
1217 |
/**
|
|
|
1218 |
* Actual implementation of the reset course functionality, delete all the
|
|
|
1219 |
* assignment submissions for course $data->courseid.
|
|
|
1220 |
*
|
|
|
1221 |
* @param stdClass $data the data submitted from the reset course.
|
|
|
1222 |
* @return array status array
|
|
|
1223 |
*/
|
1254 |
ariadna |
1224 |
public function reset_userdata($data)
|
|
|
1225 |
{
|
1 |
efrain |
1226 |
global $CFG, $DB;
|
|
|
1227 |
|
|
|
1228 |
$componentstr = get_string('modulenameplural', 'assign');
|
|
|
1229 |
$status = array();
|
|
|
1230 |
|
|
|
1231 |
$fs = get_file_storage();
|
|
|
1232 |
if (!empty($data->reset_assign_submissions)) {
|
|
|
1233 |
// Delete files associated with this assignment.
|
|
|
1234 |
foreach ($this->submissionplugins as $plugin) {
|
|
|
1235 |
$fileareas = array();
|
|
|
1236 |
$plugincomponent = $plugin->get_subtype() . '_' . $plugin->get_type();
|
|
|
1237 |
$fileareas = $plugin->get_file_areas();
|
|
|
1238 |
foreach ($fileareas as $filearea => $notused) {
|
|
|
1239 |
$fs->delete_area_files($this->context->id, $plugincomponent, $filearea);
|
|
|
1240 |
}
|
|
|
1241 |
|
|
|
1242 |
if (!$plugin->delete_instance()) {
|
1254 |
ariadna |
1243 |
$status[] = array(
|
|
|
1244 |
'component' => $componentstr,
|
|
|
1245 |
'item' => get_string('deleteallsubmissions', 'assign'),
|
|
|
1246 |
'error' => $plugin->get_error()
|
|
|
1247 |
);
|
1 |
efrain |
1248 |
}
|
|
|
1249 |
}
|
|
|
1250 |
|
|
|
1251 |
foreach ($this->feedbackplugins as $plugin) {
|
|
|
1252 |
$fileareas = array();
|
|
|
1253 |
$plugincomponent = $plugin->get_subtype() . '_' . $plugin->get_type();
|
|
|
1254 |
$fileareas = $plugin->get_file_areas();
|
|
|
1255 |
foreach ($fileareas as $filearea => $notused) {
|
|
|
1256 |
$fs->delete_area_files($this->context->id, $plugincomponent, $filearea);
|
|
|
1257 |
}
|
|
|
1258 |
|
|
|
1259 |
if (!$plugin->delete_instance()) {
|
1254 |
ariadna |
1260 |
$status[] = array(
|
|
|
1261 |
'component' => $componentstr,
|
|
|
1262 |
'item' => get_string('deleteallsubmissions', 'assign'),
|
|
|
1263 |
'error' => $plugin->get_error()
|
|
|
1264 |
);
|
1 |
efrain |
1265 |
}
|
|
|
1266 |
}
|
|
|
1267 |
|
|
|
1268 |
$assignids = $DB->get_records('assign', array('course' => $data->courseid), '', 'id');
|
|
|
1269 |
list($sql, $params) = $DB->get_in_or_equal(array_keys($assignids));
|
|
|
1270 |
|
|
|
1271 |
$DB->delete_records_select('assign_submission', "assignment $sql", $params);
|
|
|
1272 |
$DB->delete_records_select('assign_user_flags', "assignment $sql", $params);
|
|
|
1273 |
|
1254 |
ariadna |
1274 |
$status[] = array(
|
|
|
1275 |
'component' => $componentstr,
|
|
|
1276 |
'item' => get_string('deleteallsubmissions', 'assign'),
|
|
|
1277 |
'error' => false
|
|
|
1278 |
);
|
1 |
efrain |
1279 |
|
|
|
1280 |
if (!empty($data->reset_gradebook_grades)) {
|
|
|
1281 |
$DB->delete_records_select('assign_grades', "assignment $sql", $params);
|
|
|
1282 |
// Remove all grades from gradebook.
|
1254 |
ariadna |
1283 |
require_once($CFG->dirroot . '/mod/assign/lib.php');
|
1 |
efrain |
1284 |
assign_reset_gradebook($data->courseid);
|
|
|
1285 |
}
|
|
|
1286 |
|
|
|
1287 |
// Reset revealidentities for assign if blindmarking is enabled.
|
|
|
1288 |
if ($this->get_instance()->blindmarking) {
|
|
|
1289 |
$DB->set_field('assign', 'revealidentities', 0, array('id' => $this->get_instance()->id));
|
|
|
1290 |
}
|
|
|
1291 |
}
|
|
|
1292 |
|
|
|
1293 |
$purgeoverrides = false;
|
|
|
1294 |
|
|
|
1295 |
// Remove user overrides.
|
|
|
1296 |
if (!empty($data->reset_assign_user_overrides)) {
|
1254 |
ariadna |
1297 |
$DB->delete_records_select(
|
|
|
1298 |
'assign_overrides',
|
|
|
1299 |
'assignid IN (SELECT id FROM {assign} WHERE course = ?) AND userid IS NOT NULL',
|
|
|
1300 |
array($data->courseid)
|
|
|
1301 |
);
|
1 |
efrain |
1302 |
$status[] = array(
|
|
|
1303 |
'component' => $componentstr,
|
|
|
1304 |
'item' => get_string('useroverridesdeleted', 'assign'),
|
1254 |
ariadna |
1305 |
'error' => false
|
|
|
1306 |
);
|
1 |
efrain |
1307 |
$purgeoverrides = true;
|
|
|
1308 |
}
|
|
|
1309 |
// Remove group overrides.
|
|
|
1310 |
if (!empty($data->reset_assign_group_overrides)) {
|
1254 |
ariadna |
1311 |
$DB->delete_records_select(
|
|
|
1312 |
'assign_overrides',
|
|
|
1313 |
'assignid IN (SELECT id FROM {assign} WHERE course = ?) AND groupid IS NOT NULL',
|
|
|
1314 |
array($data->courseid)
|
|
|
1315 |
);
|
1 |
efrain |
1316 |
$status[] = array(
|
|
|
1317 |
'component' => $componentstr,
|
|
|
1318 |
'item' => get_string('groupoverridesdeleted', 'assign'),
|
1254 |
ariadna |
1319 |
'error' => false
|
|
|
1320 |
);
|
1 |
efrain |
1321 |
$purgeoverrides = true;
|
|
|
1322 |
}
|
|
|
1323 |
|
|
|
1324 |
// Updating dates - shift may be negative too.
|
|
|
1325 |
if ($data->timeshift) {
|
1254 |
ariadna |
1326 |
$DB->execute(
|
|
|
1327 |
"UPDATE {assign_overrides}
|
1 |
efrain |
1328 |
SET allowsubmissionsfromdate = allowsubmissionsfromdate + ?
|
|
|
1329 |
WHERE assignid = ? AND allowsubmissionsfromdate <> 0",
|
1254 |
ariadna |
1330 |
array($data->timeshift, $this->get_instance()->id)
|
|
|
1331 |
);
|
|
|
1332 |
$DB->execute(
|
|
|
1333 |
"UPDATE {assign_overrides}
|
1 |
efrain |
1334 |
SET duedate = duedate + ?
|
|
|
1335 |
WHERE assignid = ? AND duedate <> 0",
|
1254 |
ariadna |
1336 |
array($data->timeshift, $this->get_instance()->id)
|
|
|
1337 |
);
|
|
|
1338 |
$DB->execute(
|
|
|
1339 |
"UPDATE {assign_overrides}
|
1 |
efrain |
1340 |
SET cutoffdate = cutoffdate + ?
|
|
|
1341 |
WHERE assignid =? AND cutoffdate <> 0",
|
1254 |
ariadna |
1342 |
array($data->timeshift, $this->get_instance()->id)
|
|
|
1343 |
);
|
1 |
efrain |
1344 |
|
|
|
1345 |
$purgeoverrides = true;
|
|
|
1346 |
|
|
|
1347 |
// Any changes to the list of dates that needs to be rolled should be same during course restore and course reset.
|
|
|
1348 |
// See MDL-9367.
|
1254 |
ariadna |
1349 |
shift_course_mod_dates(
|
|
|
1350 |
'assign',
|
|
|
1351 |
array('duedate', 'allowsubmissionsfromdate', 'cutoffdate'),
|
|
|
1352 |
$data->timeshift,
|
|
|
1353 |
$data->courseid,
|
|
|
1354 |
$this->get_instance()->id
|
|
|
1355 |
);
|
|
|
1356 |
$status[] = array(
|
|
|
1357 |
'component' => $componentstr,
|
|
|
1358 |
'item' => get_string('datechanged'),
|
|
|
1359 |
'error' => false
|
|
|
1360 |
);
|
1 |
efrain |
1361 |
}
|
|
|
1362 |
|
|
|
1363 |
if ($purgeoverrides) {
|
|
|
1364 |
cache::make('mod_assign', 'overrides')->purge();
|
|
|
1365 |
}
|
|
|
1366 |
|
|
|
1367 |
return $status;
|
|
|
1368 |
}
|
|
|
1369 |
|
|
|
1370 |
/**
|
|
|
1371 |
* Update the settings for a single plugin.
|
|
|
1372 |
*
|
|
|
1373 |
* @param assign_plugin $plugin The plugin to update
|
|
|
1374 |
* @param stdClass $formdata The form data
|
|
|
1375 |
* @return bool false if an error occurs
|
|
|
1376 |
*/
|
1254 |
ariadna |
1377 |
protected function update_plugin_instance(assign_plugin $plugin, stdClass $formdata)
|
|
|
1378 |
{
|
1 |
efrain |
1379 |
if ($plugin->is_visible()) {
|
|
|
1380 |
$enabledname = $plugin->get_subtype() . '_' . $plugin->get_type() . '_enabled';
|
|
|
1381 |
if (!empty($formdata->$enabledname)) {
|
|
|
1382 |
$plugin->enable();
|
|
|
1383 |
if (!$plugin->save_settings($formdata)) {
|
|
|
1384 |
throw new \moodle_exception($plugin->get_error());
|
|
|
1385 |
return false;
|
|
|
1386 |
}
|
|
|
1387 |
} else {
|
|
|
1388 |
$plugin->disable();
|
|
|
1389 |
}
|
|
|
1390 |
}
|
|
|
1391 |
return true;
|
|
|
1392 |
}
|
|
|
1393 |
|
|
|
1394 |
/**
|
|
|
1395 |
* Update the gradebook information for this assignment.
|
|
|
1396 |
*
|
|
|
1397 |
* @param bool $reset If true, will reset all grades in the gradbook for this assignment
|
|
|
1398 |
* @param int $coursemoduleid This is required because it might not exist in the database yet
|
|
|
1399 |
* @return bool
|
|
|
1400 |
*/
|
1254 |
ariadna |
1401 |
public function update_gradebook($reset, $coursemoduleid)
|
|
|
1402 |
{
|
1 |
efrain |
1403 |
global $CFG;
|
|
|
1404 |
|
1254 |
ariadna |
1405 |
require_once($CFG->dirroot . '/mod/assign/lib.php');
|
1 |
efrain |
1406 |
$assign = clone $this->get_instance();
|
|
|
1407 |
$assign->cmidnumber = $coursemoduleid;
|
|
|
1408 |
|
|
|
1409 |
// Set assign gradebook feedback plugin status (enabled and visible).
|
|
|
1410 |
$assign->gradefeedbackenabled = $this->is_gradebook_feedback_enabled();
|
|
|
1411 |
|
|
|
1412 |
$param = null;
|
|
|
1413 |
if ($reset) {
|
|
|
1414 |
$param = 'reset';
|
|
|
1415 |
}
|
|
|
1416 |
|
|
|
1417 |
return assign_grade_item_update($assign, $param);
|
|
|
1418 |
}
|
|
|
1419 |
|
|
|
1420 |
/**
|
|
|
1421 |
* Get the marking table page size
|
|
|
1422 |
*
|
|
|
1423 |
* @return integer
|
|
|
1424 |
*/
|
1254 |
ariadna |
1425 |
public function get_assign_perpage()
|
|
|
1426 |
{
|
1 |
efrain |
1427 |
$perpage = (int) get_user_preferences('assign_perpage', 10);
|
|
|
1428 |
$adminconfig = $this->get_admin_config();
|
|
|
1429 |
$maxperpage = -1;
|
|
|
1430 |
if (isset($adminconfig->maxperpage)) {
|
|
|
1431 |
$maxperpage = $adminconfig->maxperpage;
|
|
|
1432 |
}
|
1254 |
ariadna |
1433 |
if (
|
|
|
1434 |
isset($maxperpage) &&
|
1 |
efrain |
1435 |
$maxperpage != -1 &&
|
1254 |
ariadna |
1436 |
($perpage == -1 || $perpage > $maxperpage)
|
|
|
1437 |
) {
|
1 |
efrain |
1438 |
$perpage = $maxperpage;
|
|
|
1439 |
}
|
|
|
1440 |
return $perpage;
|
|
|
1441 |
}
|
|
|
1442 |
|
|
|
1443 |
/**
|
|
|
1444 |
* Load and cache the admin config for this module.
|
|
|
1445 |
*
|
|
|
1446 |
* @return stdClass the plugin config
|
|
|
1447 |
*/
|
1254 |
ariadna |
1448 |
public function get_admin_config()
|
|
|
1449 |
{
|
1 |
efrain |
1450 |
if ($this->adminconfig) {
|
|
|
1451 |
return $this->adminconfig;
|
|
|
1452 |
}
|
|
|
1453 |
$this->adminconfig = get_config('assign');
|
|
|
1454 |
return $this->adminconfig;
|
|
|
1455 |
}
|
|
|
1456 |
|
|
|
1457 |
/**
|
|
|
1458 |
* Update the calendar entries for this assignment.
|
|
|
1459 |
*
|
|
|
1460 |
* @param int $coursemoduleid - Required to pass this in because it might
|
|
|
1461 |
* not exist in the database yet.
|
|
|
1462 |
* @return bool
|
|
|
1463 |
*/
|
1254 |
ariadna |
1464 |
public function update_calendar($coursemoduleid)
|
|
|
1465 |
{
|
1 |
efrain |
1466 |
global $DB, $CFG;
|
1254 |
ariadna |
1467 |
require_once($CFG->dirroot . '/calendar/lib.php');
|
1 |
efrain |
1468 |
|
|
|
1469 |
// Special case for add_instance as the coursemodule has not been set yet.
|
|
|
1470 |
$instance = $this->get_instance();
|
|
|
1471 |
|
|
|
1472 |
// Start with creating the event.
|
|
|
1473 |
$event = new stdClass();
|
|
|
1474 |
$event->modulename = 'assign';
|
|
|
1475 |
$event->courseid = $instance->course;
|
|
|
1476 |
$event->groupid = 0;
|
|
|
1477 |
$event->userid = 0;
|
|
|
1478 |
$event->instance = $instance->id;
|
|
|
1479 |
$event->type = CALENDAR_EVENT_TYPE_ACTION;
|
|
|
1480 |
|
|
|
1481 |
// Convert the links to pluginfile. It is a bit hacky but at this stage the files
|
|
|
1482 |
// might not have been saved in the module area yet.
|
|
|
1483 |
$intro = $instance->intro;
|
|
|
1484 |
if ($draftid = file_get_submitted_draft_itemid('introeditor')) {
|
|
|
1485 |
$intro = file_rewrite_urls_to_pluginfile($intro, $draftid);
|
|
|
1486 |
}
|
|
|
1487 |
|
|
|
1488 |
// We need to remove the links to files as the calendar is not ready
|
|
|
1489 |
// to support module events with file areas.
|
|
|
1490 |
$intro = strip_pluginfile_content($intro);
|
|
|
1491 |
if ($this->show_intro()) {
|
|
|
1492 |
$event->description = array(
|
|
|
1493 |
'text' => $intro,
|
|
|
1494 |
'format' => $instance->introformat
|
|
|
1495 |
);
|
|
|
1496 |
} else {
|
|
|
1497 |
$event->description = array(
|
|
|
1498 |
'text' => '',
|
|
|
1499 |
'format' => $instance->introformat
|
|
|
1500 |
);
|
|
|
1501 |
}
|
|
|
1502 |
|
|
|
1503 |
$eventtype = ASSIGN_EVENT_TYPE_DUE;
|
|
|
1504 |
if ($instance->duedate) {
|
|
|
1505 |
$event->name = get_string('calendardue', 'assign', $instance->name);
|
|
|
1506 |
$event->eventtype = $eventtype;
|
|
|
1507 |
$event->timestart = $instance->duedate;
|
|
|
1508 |
$event->timesort = $instance->duedate;
|
|
|
1509 |
$select = "modulename = :modulename
|
|
|
1510 |
AND instance = :instance
|
|
|
1511 |
AND eventtype = :eventtype
|
|
|
1512 |
AND groupid = 0
|
|
|
1513 |
AND courseid <> 0";
|
|
|
1514 |
$params = array('modulename' => 'assign', 'instance' => $instance->id, 'eventtype' => $eventtype);
|
|
|
1515 |
$event->id = $DB->get_field_select('event', 'id', $select, $params);
|
|
|
1516 |
|
|
|
1517 |
// Now process the event.
|
|
|
1518 |
if ($event->id) {
|
|
|
1519 |
$calendarevent = calendar_event::load($event->id);
|
|
|
1520 |
$calendarevent->update($event, false);
|
|
|
1521 |
} else {
|
|
|
1522 |
calendar_event::create($event, false);
|
|
|
1523 |
}
|
|
|
1524 |
} else {
|
1254 |
ariadna |
1525 |
$DB->delete_records('event', array(
|
|
|
1526 |
'modulename' => 'assign',
|
|
|
1527 |
'instance' => $instance->id,
|
|
|
1528 |
'eventtype' => $eventtype
|
|
|
1529 |
));
|
1 |
efrain |
1530 |
}
|
|
|
1531 |
|
|
|
1532 |
$eventtype = ASSIGN_EVENT_TYPE_GRADINGDUE;
|
|
|
1533 |
if ($instance->gradingduedate) {
|
|
|
1534 |
$event->name = get_string('calendargradingdue', 'assign', $instance->name);
|
|
|
1535 |
$event->eventtype = $eventtype;
|
|
|
1536 |
$event->timestart = $instance->gradingduedate;
|
|
|
1537 |
$event->timesort = $instance->gradingduedate;
|
1254 |
ariadna |
1538 |
$event->id = $DB->get_field('event', 'id', array(
|
|
|
1539 |
'modulename' => 'assign',
|
|
|
1540 |
'instance' => $instance->id,
|
|
|
1541 |
'eventtype' => $event->eventtype
|
|
|
1542 |
));
|
1 |
efrain |
1543 |
|
|
|
1544 |
// Now process the event.
|
|
|
1545 |
if ($event->id) {
|
|
|
1546 |
$calendarevent = calendar_event::load($event->id);
|
|
|
1547 |
$calendarevent->update($event, false);
|
|
|
1548 |
} else {
|
|
|
1549 |
calendar_event::create($event, false);
|
|
|
1550 |
}
|
|
|
1551 |
} else {
|
1254 |
ariadna |
1552 |
$DB->delete_records('event', array(
|
|
|
1553 |
'modulename' => 'assign',
|
|
|
1554 |
'instance' => $instance->id,
|
|
|
1555 |
'eventtype' => $eventtype
|
|
|
1556 |
));
|
1 |
efrain |
1557 |
}
|
|
|
1558 |
|
|
|
1559 |
return true;
|
|
|
1560 |
}
|
|
|
1561 |
|
|
|
1562 |
/**
|
|
|
1563 |
* Update this instance in the database.
|
|
|
1564 |
*
|
|
|
1565 |
* @param stdClass $formdata - the data submitted from the form
|
|
|
1566 |
* @return bool false if an error occurs
|
|
|
1567 |
*/
|
1254 |
ariadna |
1568 |
public function update_instance($formdata)
|
|
|
1569 |
{
|
1 |
efrain |
1570 |
global $DB;
|
|
|
1571 |
$adminconfig = $this->get_admin_config();
|
|
|
1572 |
|
|
|
1573 |
$update = new stdClass();
|
|
|
1574 |
$update->id = $formdata->instance;
|
|
|
1575 |
$update->name = $formdata->name;
|
|
|
1576 |
$update->timemodified = time();
|
|
|
1577 |
$update->course = $formdata->course;
|
|
|
1578 |
$update->intro = $formdata->intro;
|
|
|
1579 |
$update->introformat = $formdata->introformat;
|
|
|
1580 |
$update->alwaysshowdescription = !empty($formdata->alwaysshowdescription);
|
|
|
1581 |
if (isset($formdata->activityeditor)) {
|
|
|
1582 |
$update->activity = $this->save_editor_draft_files($formdata);
|
|
|
1583 |
$update->activityformat = $formdata->activityeditor['format'];
|
|
|
1584 |
}
|
|
|
1585 |
if (isset($formdata->submissionattachments)) {
|
|
|
1586 |
$update->submissionattachments = $formdata->submissionattachments;
|
|
|
1587 |
}
|
|
|
1588 |
$update->submissiondrafts = $formdata->submissiondrafts;
|
|
|
1589 |
$update->requiresubmissionstatement = $formdata->requiresubmissionstatement;
|
|
|
1590 |
$update->sendnotifications = $formdata->sendnotifications;
|
|
|
1591 |
$update->sendlatenotifications = $formdata->sendlatenotifications;
|
|
|
1592 |
$update->sendstudentnotifications = $adminconfig->sendstudentnotifications;
|
|
|
1593 |
if (isset($formdata->sendstudentnotifications)) {
|
|
|
1594 |
$update->sendstudentnotifications = $formdata->sendstudentnotifications;
|
|
|
1595 |
}
|
|
|
1596 |
$update->duedate = $formdata->duedate;
|
|
|
1597 |
$update->cutoffdate = $formdata->cutoffdate;
|
|
|
1598 |
if (isset($formdata->timelimit)) {
|
|
|
1599 |
$update->timelimit = $formdata->timelimit;
|
|
|
1600 |
}
|
|
|
1601 |
$update->gradingduedate = $formdata->gradingduedate;
|
|
|
1602 |
$update->allowsubmissionsfromdate = $formdata->allowsubmissionsfromdate;
|
|
|
1603 |
$update->grade = $formdata->grade;
|
|
|
1604 |
if (!empty($formdata->completionunlocked)) {
|
|
|
1605 |
$update->completionsubmit = !empty($formdata->completionsubmit);
|
|
|
1606 |
}
|
|
|
1607 |
$update->teamsubmission = $formdata->teamsubmission;
|
|
|
1608 |
$update->requireallteammemberssubmit = $formdata->requireallteammemberssubmit;
|
|
|
1609 |
if (isset($formdata->teamsubmissiongroupingid)) {
|
|
|
1610 |
$update->teamsubmissiongroupingid = $formdata->teamsubmissiongroupingid;
|
|
|
1611 |
}
|
|
|
1612 |
if (isset($formdata->hidegrader)) {
|
|
|
1613 |
$update->hidegrader = $formdata->hidegrader;
|
|
|
1614 |
}
|
|
|
1615 |
$update->blindmarking = $formdata->blindmarking;
|
|
|
1616 |
$update->attemptreopenmethod = ASSIGN_ATTEMPT_REOPEN_METHOD_NONE;
|
|
|
1617 |
if (!empty($formdata->attemptreopenmethod)) {
|
|
|
1618 |
$update->attemptreopenmethod = $formdata->attemptreopenmethod;
|
|
|
1619 |
}
|
|
|
1620 |
if (!empty($formdata->maxattempts)) {
|
|
|
1621 |
$update->maxattempts = $formdata->maxattempts;
|
|
|
1622 |
}
|
|
|
1623 |
if (isset($formdata->preventsubmissionnotingroup)) {
|
|
|
1624 |
$update->preventsubmissionnotingroup = $formdata->preventsubmissionnotingroup;
|
|
|
1625 |
}
|
|
|
1626 |
$update->markingworkflow = $formdata->markingworkflow;
|
|
|
1627 |
$update->markingallocation = $formdata->markingallocation;
|
|
|
1628 |
if (empty($update->markingworkflow)) { // If marking workflow is disabled, make sure allocation is disabled.
|
|
|
1629 |
$update->markingallocation = 0;
|
|
|
1630 |
}
|
|
|
1631 |
$update->markinganonymous = $formdata->markinganonymous;
|
|
|
1632 |
// If marking workflow is disabled, or blindmarking is disabled then make sure marking anonymous is disabled.
|
|
|
1633 |
if (empty($update->markingworkflow) || empty($update->blindmarking)) {
|
|
|
1634 |
$update->markinganonymous = 0;
|
|
|
1635 |
}
|
|
|
1636 |
|
|
|
1637 |
$result = $DB->update_record('assign', $update);
|
1254 |
ariadna |
1638 |
$this->instance = $DB->get_record('assign', array('id' => $update->id), '*', MUST_EXIST);
|
1 |
efrain |
1639 |
|
|
|
1640 |
$this->save_intro_draft_files($formdata);
|
|
|
1641 |
|
|
|
1642 |
// Load the assignment so the plugins have access to it.
|
|
|
1643 |
|
|
|
1644 |
// Call save_settings hook for submission plugins.
|
|
|
1645 |
foreach ($this->submissionplugins as $plugin) {
|
|
|
1646 |
if (!$this->update_plugin_instance($plugin, $formdata)) {
|
|
|
1647 |
throw new \moodle_exception($plugin->get_error());
|
|
|
1648 |
return false;
|
|
|
1649 |
}
|
|
|
1650 |
}
|
|
|
1651 |
foreach ($this->feedbackplugins as $plugin) {
|
|
|
1652 |
if (!$this->update_plugin_instance($plugin, $formdata)) {
|
|
|
1653 |
throw new \moodle_exception($plugin->get_error());
|
|
|
1654 |
return false;
|
|
|
1655 |
}
|
|
|
1656 |
}
|
|
|
1657 |
|
|
|
1658 |
$this->update_calendar($this->get_course_module()->id);
|
|
|
1659 |
$completionexpected = (!empty($formdata->completionexpected)) ? $formdata->completionexpected : null;
|
1254 |
ariadna |
1660 |
\core_completion\api::update_completion_date_event(
|
|
|
1661 |
$this->get_course_module()->id,
|
|
|
1662 |
'assign',
|
|
|
1663 |
$this->instance,
|
|
|
1664 |
$completionexpected
|
|
|
1665 |
);
|
1 |
efrain |
1666 |
$this->update_gradebook(false, $this->get_course_module()->id);
|
|
|
1667 |
|
|
|
1668 |
$update = new stdClass();
|
|
|
1669 |
$update->id = $this->get_instance()->id;
|
1254 |
ariadna |
1670 |
$update->nosubmissions = (!$this->is_any_submission_plugin_enabled()) ? 1 : 0;
|
1 |
efrain |
1671 |
$DB->update_record('assign', $update);
|
|
|
1672 |
|
|
|
1673 |
return $result;
|
|
|
1674 |
}
|
|
|
1675 |
|
|
|
1676 |
/**
|
|
|
1677 |
* Save the attachments in the intro description.
|
|
|
1678 |
*
|
|
|
1679 |
* @param stdClass $formdata
|
|
|
1680 |
*/
|
1254 |
ariadna |
1681 |
protected function save_intro_draft_files($formdata)
|
|
|
1682 |
{
|
1 |
efrain |
1683 |
if (isset($formdata->introattachments)) {
|
1254 |
ariadna |
1684 |
file_save_draft_area_files(
|
|
|
1685 |
$formdata->introattachments,
|
|
|
1686 |
$this->get_context()->id,
|
|
|
1687 |
'mod_assign',
|
|
|
1688 |
ASSIGN_INTROATTACHMENT_FILEAREA,
|
|
|
1689 |
|
|
|
1690 |
);
|
1 |
efrain |
1691 |
}
|
|
|
1692 |
}
|
|
|
1693 |
|
|
|
1694 |
/**
|
|
|
1695 |
* Save the attachments in the editor description.
|
|
|
1696 |
*
|
|
|
1697 |
* @param stdClass $formdata
|
|
|
1698 |
*/
|
1254 |
ariadna |
1699 |
protected function save_editor_draft_files($formdata): string
|
|
|
1700 |
{
|
1 |
efrain |
1701 |
$text = '';
|
|
|
1702 |
if (isset($formdata->activityeditor)) {
|
|
|
1703 |
$text = $formdata->activityeditor['text'];
|
|
|
1704 |
if (isset($formdata->activityeditor['itemid'])) {
|
1254 |
ariadna |
1705 |
$text = file_save_draft_area_files(
|
|
|
1706 |
$formdata->activityeditor['itemid'],
|
|
|
1707 |
$this->get_context()->id,
|
|
|
1708 |
'mod_assign',
|
|
|
1709 |
ASSIGN_ACTIVITYATTACHMENT_FILEAREA,
|
|
|
1710 |
0,
|
|
|
1711 |
array('subdirs' => true),
|
|
|
1712 |
$formdata->activityeditor['text']
|
|
|
1713 |
);
|
1 |
efrain |
1714 |
}
|
|
|
1715 |
}
|
|
|
1716 |
return $text;
|
|
|
1717 |
}
|
|
|
1718 |
|
|
|
1719 |
|
|
|
1720 |
/**
|
|
|
1721 |
* Add elements in grading plugin form.
|
|
|
1722 |
*
|
|
|
1723 |
* @param mixed $grade stdClass|null
|
|
|
1724 |
* @param MoodleQuickForm $mform
|
|
|
1725 |
* @param stdClass $data
|
|
|
1726 |
* @param int $userid - The userid we are grading
|
|
|
1727 |
* @return void
|
|
|
1728 |
*/
|
1254 |
ariadna |
1729 |
protected function add_plugin_grade_elements($grade, MoodleQuickForm $mform, stdClass $data, $userid)
|
|
|
1730 |
{
|
1 |
efrain |
1731 |
foreach ($this->feedbackplugins as $plugin) {
|
|
|
1732 |
if ($plugin->is_enabled() && $plugin->is_visible()) {
|
|
|
1733 |
$plugin->get_form_elements_for_user($grade, $mform, $data, $userid);
|
|
|
1734 |
}
|
|
|
1735 |
}
|
|
|
1736 |
}
|
|
|
1737 |
|
|
|
1738 |
|
|
|
1739 |
|
|
|
1740 |
/**
|
|
|
1741 |
* Add one plugins settings to edit plugin form.
|
|
|
1742 |
*
|
|
|
1743 |
* @param assign_plugin $plugin The plugin to add the settings from
|
|
|
1744 |
* @param MoodleQuickForm $mform The form to add the configuration settings to.
|
|
|
1745 |
* This form is modified directly (not returned).
|
|
|
1746 |
* @param array $pluginsenabled A list of form elements to be added to a group.
|
|
|
1747 |
* The new element is added to this array by this function.
|
|
|
1748 |
* @return void
|
|
|
1749 |
*/
|
1254 |
ariadna |
1750 |
protected function add_plugin_settings(assign_plugin $plugin, MoodleQuickForm $mform, &$pluginsenabled)
|
|
|
1751 |
{
|
1 |
efrain |
1752 |
global $CFG;
|
|
|
1753 |
if ($plugin->is_visible() && !$plugin->is_configurable() && $plugin->is_enabled()) {
|
|
|
1754 |
$name = $plugin->get_subtype() . '_' . $plugin->get_type() . '_enabled';
|
|
|
1755 |
$pluginsenabled[] = $mform->createElement('hidden', $name, 1);
|
|
|
1756 |
$mform->setType($name, PARAM_BOOL);
|
|
|
1757 |
$plugin->get_settings($mform);
|
|
|
1758 |
} else if ($plugin->is_visible() && $plugin->is_configurable()) {
|
|
|
1759 |
$name = $plugin->get_subtype() . '_' . $plugin->get_type() . '_enabled';
|
|
|
1760 |
$label = $plugin->get_name();
|
|
|
1761 |
$pluginsenabled[] = $mform->createElement('checkbox', $name, '', $label);
|
|
|
1762 |
$helpicon = $this->get_renderer()->help_icon('enabled', $plugin->get_subtype() . '_' . $plugin->get_type());
|
|
|
1763 |
$pluginsenabled[] = $mform->createElement('static', '', '', $helpicon);
|
|
|
1764 |
|
|
|
1765 |
$default = get_config($plugin->get_subtype() . '_' . $plugin->get_type(), 'default');
|
|
|
1766 |
if ($plugin->get_config('enabled') !== false) {
|
|
|
1767 |
$default = $plugin->is_enabled();
|
|
|
1768 |
}
|
|
|
1769 |
$mform->setDefault($plugin->get_subtype() . '_' . $plugin->get_type() . '_enabled', $default);
|
|
|
1770 |
|
|
|
1771 |
$plugin->get_settings($mform);
|
|
|
1772 |
}
|
|
|
1773 |
}
|
|
|
1774 |
|
|
|
1775 |
/**
|
|
|
1776 |
* Add settings to edit plugin form.
|
|
|
1777 |
*
|
|
|
1778 |
* @param MoodleQuickForm $mform The form to add the configuration settings to.
|
|
|
1779 |
* This form is modified directly (not returned).
|
|
|
1780 |
* @return void
|
|
|
1781 |
*/
|
1254 |
ariadna |
1782 |
public function add_all_plugin_settings(MoodleQuickForm $mform)
|
|
|
1783 |
{
|
1 |
efrain |
1784 |
$mform->addElement('header', 'submissiontypes', get_string('submissiontypes', 'assign'));
|
|
|
1785 |
|
|
|
1786 |
$submissionpluginsenabled = array();
|
|
|
1787 |
$group = $mform->addGroup(array(), 'submissionplugins', get_string('submissiontypes', 'assign'), array(' '), false);
|
|
|
1788 |
foreach ($this->submissionplugins as $plugin) {
|
|
|
1789 |
$this->add_plugin_settings($plugin, $mform, $submissionpluginsenabled);
|
|
|
1790 |
}
|
|
|
1791 |
$group->setElements($submissionpluginsenabled);
|
|
|
1792 |
|
|
|
1793 |
$mform->addElement('header', 'feedbacktypes', get_string('feedbacktypes', 'assign'));
|
|
|
1794 |
$feedbackpluginsenabled = array();
|
|
|
1795 |
$group = $mform->addGroup(array(), 'feedbackplugins', get_string('feedbacktypes', 'assign'), array(' '), false);
|
|
|
1796 |
foreach ($this->feedbackplugins as $plugin) {
|
|
|
1797 |
$this->add_plugin_settings($plugin, $mform, $feedbackpluginsenabled);
|
|
|
1798 |
}
|
|
|
1799 |
$group->setElements($feedbackpluginsenabled);
|
|
|
1800 |
$mform->setExpanded('submissiontypes');
|
|
|
1801 |
}
|
|
|
1802 |
|
|
|
1803 |
/**
|
|
|
1804 |
* Allow each plugin an opportunity to update the defaultvalues
|
|
|
1805 |
* passed in to the settings form (needed to set up draft areas for
|
|
|
1806 |
* editor and filemanager elements)
|
|
|
1807 |
*
|
|
|
1808 |
* @param array $defaultvalues
|
|
|
1809 |
*/
|
1254 |
ariadna |
1810 |
public function plugin_data_preprocessing(&$defaultvalues)
|
|
|
1811 |
{
|
1 |
efrain |
1812 |
foreach ($this->submissionplugins as $plugin) {
|
|
|
1813 |
if ($plugin->is_visible()) {
|
|
|
1814 |
$plugin->data_preprocessing($defaultvalues);
|
|
|
1815 |
}
|
|
|
1816 |
}
|
|
|
1817 |
foreach ($this->feedbackplugins as $plugin) {
|
|
|
1818 |
if ($plugin->is_visible()) {
|
|
|
1819 |
$plugin->data_preprocessing($defaultvalues);
|
|
|
1820 |
}
|
|
|
1821 |
}
|
|
|
1822 |
}
|
|
|
1823 |
|
|
|
1824 |
/**
|
|
|
1825 |
* Get the name of the current module.
|
|
|
1826 |
*
|
|
|
1827 |
* @return string the module name (Assignment)
|
|
|
1828 |
*/
|
1254 |
ariadna |
1829 |
protected function get_module_name()
|
|
|
1830 |
{
|
1 |
efrain |
1831 |
if (isset(self::$modulename)) {
|
|
|
1832 |
return self::$modulename;
|
|
|
1833 |
}
|
|
|
1834 |
self::$modulename = get_string('modulename', 'assign');
|
|
|
1835 |
return self::$modulename;
|
|
|
1836 |
}
|
|
|
1837 |
|
|
|
1838 |
/**
|
|
|
1839 |
* Get the plural name of the current module.
|
|
|
1840 |
*
|
|
|
1841 |
* @return string the module name plural (Assignments)
|
|
|
1842 |
*/
|
1254 |
ariadna |
1843 |
protected function get_module_name_plural()
|
|
|
1844 |
{
|
1 |
efrain |
1845 |
if (isset(self::$modulenameplural)) {
|
|
|
1846 |
return self::$modulenameplural;
|
|
|
1847 |
}
|
|
|
1848 |
self::$modulenameplural = get_string('modulenameplural', 'assign');
|
|
|
1849 |
return self::$modulenameplural;
|
|
|
1850 |
}
|
|
|
1851 |
|
|
|
1852 |
/**
|
|
|
1853 |
* Has this assignment been constructed from an instance?
|
|
|
1854 |
*
|
|
|
1855 |
* @return bool
|
|
|
1856 |
*/
|
1254 |
ariadna |
1857 |
public function has_instance()
|
|
|
1858 |
{
|
1 |
efrain |
1859 |
return $this->instance || $this->get_course_module();
|
|
|
1860 |
}
|
|
|
1861 |
|
|
|
1862 |
/**
|
|
|
1863 |
* Get the settings for the current instance of this assignment.
|
|
|
1864 |
*
|
|
|
1865 |
* @return stdClass The settings
|
|
|
1866 |
* @throws dml_exception
|
|
|
1867 |
*/
|
1254 |
ariadna |
1868 |
public function get_default_instance()
|
|
|
1869 |
{
|
1 |
efrain |
1870 |
global $DB;
|
|
|
1871 |
if (!$this->instance && $this->get_course_module()) {
|
|
|
1872 |
$params = array('id' => $this->get_course_module()->instance);
|
|
|
1873 |
$this->instance = $DB->get_record('assign', $params, '*', MUST_EXIST);
|
|
|
1874 |
|
|
|
1875 |
$this->userinstances = [];
|
|
|
1876 |
}
|
|
|
1877 |
return $this->instance;
|
|
|
1878 |
}
|
|
|
1879 |
|
|
|
1880 |
/**
|
|
|
1881 |
* Get the settings for the current instance of this assignment
|
|
|
1882 |
* @param int|null $userid the id of the user to load the assign instance for.
|
|
|
1883 |
* @return stdClass The settings
|
|
|
1884 |
*/
|
1254 |
ariadna |
1885 |
public function get_instance(int $userid = null): stdClass
|
|
|
1886 |
{
|
1 |
efrain |
1887 |
global $USER;
|
|
|
1888 |
$userid = $userid ?? $USER->id;
|
|
|
1889 |
|
|
|
1890 |
$this->instance = $this->get_default_instance();
|
|
|
1891 |
|
|
|
1892 |
// If we have the user instance already, just return it.
|
|
|
1893 |
if (isset($this->userinstances[$userid])) {
|
|
|
1894 |
return $this->userinstances[$userid];
|
|
|
1895 |
}
|
|
|
1896 |
|
|
|
1897 |
// Calculate properties which vary per user.
|
|
|
1898 |
$this->userinstances[$userid] = $this->calculate_properties($this->instance, $userid);
|
|
|
1899 |
return $this->userinstances[$userid];
|
|
|
1900 |
}
|
|
|
1901 |
|
|
|
1902 |
/**
|
|
|
1903 |
* Calculates and updates various properties based on the specified user.
|
|
|
1904 |
*
|
|
|
1905 |
* @param stdClass $record the raw assign record.
|
|
|
1906 |
* @param int $userid the id of the user to calculate the properties for.
|
|
|
1907 |
* @return stdClass a new record having calculated properties.
|
|
|
1908 |
*/
|
1254 |
ariadna |
1909 |
private function calculate_properties(\stdClass $record, int $userid): \stdClass
|
|
|
1910 |
{
|
1 |
efrain |
1911 |
$record = clone ($record);
|
|
|
1912 |
|
|
|
1913 |
// Relative dates.
|
|
|
1914 |
if (!empty($record->duedate)) {
|
|
|
1915 |
$course = $this->get_course();
|
|
|
1916 |
$usercoursedates = course_get_course_dates_for_user_id($course, $userid);
|
|
|
1917 |
if ($usercoursedates['start']) {
|
|
|
1918 |
$userprops = ['duedate' => $record->duedate + $usercoursedates['startoffset']];
|
|
|
1919 |
$record = (object) array_merge((array) $record, (array) $userprops);
|
|
|
1920 |
}
|
|
|
1921 |
}
|
|
|
1922 |
return $record;
|
|
|
1923 |
}
|
|
|
1924 |
|
|
|
1925 |
/**
|
|
|
1926 |
* Get the primary grade item for this assign instance.
|
|
|
1927 |
*
|
|
|
1928 |
* @return grade_item The grade_item record
|
|
|
1929 |
*/
|
1254 |
ariadna |
1930 |
public function get_grade_item()
|
|
|
1931 |
{
|
1 |
efrain |
1932 |
if ($this->gradeitem) {
|
|
|
1933 |
return $this->gradeitem;
|
|
|
1934 |
}
|
|
|
1935 |
$instance = $this->get_instance();
|
1254 |
ariadna |
1936 |
$params = array(
|
|
|
1937 |
'itemtype' => 'mod',
|
|
|
1938 |
'itemmodule' => 'assign',
|
|
|
1939 |
'iteminstance' => $instance->id,
|
|
|
1940 |
'courseid' => $instance->course,
|
|
|
1941 |
'itemnumber' => 0
|
|
|
1942 |
);
|
1 |
efrain |
1943 |
$this->gradeitem = grade_item::fetch($params);
|
|
|
1944 |
if (!$this->gradeitem) {
|
|
|
1945 |
throw new coding_exception('Improper use of the assignment class. ' .
|
1254 |
ariadna |
1946 |
'Cannot load the grade item.');
|
1 |
efrain |
1947 |
}
|
|
|
1948 |
return $this->gradeitem;
|
|
|
1949 |
}
|
|
|
1950 |
|
|
|
1951 |
/**
|
|
|
1952 |
* Get the context of the current course.
|
|
|
1953 |
*
|
|
|
1954 |
* @return mixed context|null The course context
|
|
|
1955 |
*/
|
1254 |
ariadna |
1956 |
public function get_course_context()
|
|
|
1957 |
{
|
1 |
efrain |
1958 |
if (!$this->context && !$this->course) {
|
|
|
1959 |
throw new coding_exception('Improper use of the assignment class. ' .
|
1254 |
ariadna |
1960 |
'Cannot load the course context.');
|
1 |
efrain |
1961 |
}
|
|
|
1962 |
if ($this->context) {
|
|
|
1963 |
return $this->context->get_course_context();
|
|
|
1964 |
} else {
|
|
|
1965 |
return context_course::instance($this->course->id);
|
|
|
1966 |
}
|
|
|
1967 |
}
|
|
|
1968 |
|
|
|
1969 |
|
|
|
1970 |
/**
|
|
|
1971 |
* Get the current course module.
|
|
|
1972 |
*
|
|
|
1973 |
* @return cm_info|null The course module or null if not known
|
|
|
1974 |
*/
|
1254 |
ariadna |
1975 |
public function get_course_module()
|
|
|
1976 |
{
|
1 |
efrain |
1977 |
if ($this->coursemodule) {
|
|
|
1978 |
return $this->coursemodule;
|
|
|
1979 |
}
|
|
|
1980 |
if (!$this->context) {
|
|
|
1981 |
return null;
|
|
|
1982 |
}
|
|
|
1983 |
|
|
|
1984 |
if ($this->context->contextlevel == CONTEXT_MODULE) {
|
|
|
1985 |
$modinfo = get_fast_modinfo($this->get_course());
|
|
|
1986 |
$this->coursemodule = $modinfo->get_cm($this->context->instanceid);
|
|
|
1987 |
return $this->coursemodule;
|
|
|
1988 |
}
|
|
|
1989 |
return null;
|
|
|
1990 |
}
|
|
|
1991 |
|
|
|
1992 |
/**
|
|
|
1993 |
* Get context module.
|
|
|
1994 |
*
|
|
|
1995 |
* @return context
|
|
|
1996 |
*/
|
1254 |
ariadna |
1997 |
public function get_context()
|
|
|
1998 |
{
|
1 |
efrain |
1999 |
return $this->context;
|
|
|
2000 |
}
|
|
|
2001 |
|
|
|
2002 |
/**
|
|
|
2003 |
* Get the current course.
|
|
|
2004 |
*
|
|
|
2005 |
* @return mixed stdClass|null The course
|
|
|
2006 |
*/
|
1254 |
ariadna |
2007 |
public function get_course()
|
|
|
2008 |
{
|
1 |
efrain |
2009 |
global $DB;
|
|
|
2010 |
|
|
|
2011 |
if ($this->course && is_object($this->course)) {
|
|
|
2012 |
return $this->course;
|
|
|
2013 |
}
|
|
|
2014 |
|
|
|
2015 |
if (!$this->context) {
|
|
|
2016 |
return null;
|
|
|
2017 |
}
|
|
|
2018 |
$params = array('id' => $this->get_course_context()->instanceid);
|
|
|
2019 |
$this->course = $DB->get_record('course', $params, '*', MUST_EXIST);
|
|
|
2020 |
|
|
|
2021 |
return $this->course;
|
|
|
2022 |
}
|
|
|
2023 |
|
|
|
2024 |
/**
|
|
|
2025 |
* Count the number of intro attachments.
|
|
|
2026 |
*
|
|
|
2027 |
* @return int
|
|
|
2028 |
*/
|
1254 |
ariadna |
2029 |
protected function count_attachments()
|
|
|
2030 |
{
|
1 |
efrain |
2031 |
|
|
|
2032 |
$fs = get_file_storage();
|
1254 |
ariadna |
2033 |
$files = $fs->get_area_files(
|
|
|
2034 |
$this->get_context()->id,
|
|
|
2035 |
'mod_assign',
|
|
|
2036 |
ASSIGN_INTROATTACHMENT_FILEAREA,
|
|
|
2037 |
0,
|
|
|
2038 |
'id',
|
|
|
2039 |
false
|
|
|
2040 |
);
|
1 |
efrain |
2041 |
|
|
|
2042 |
return count($files);
|
|
|
2043 |
}
|
|
|
2044 |
|
|
|
2045 |
/**
|
|
|
2046 |
* Are there any intro attachments to display?
|
|
|
2047 |
*
|
|
|
2048 |
* @return boolean
|
|
|
2049 |
*/
|
1254 |
ariadna |
2050 |
protected function has_visible_attachments()
|
|
|
2051 |
{
|
1 |
efrain |
2052 |
return ($this->count_attachments() > 0);
|
|
|
2053 |
}
|
|
|
2054 |
|
|
|
2055 |
/**
|
|
|
2056 |
* Check if the intro attachments should be provided to the user.
|
|
|
2057 |
*
|
|
|
2058 |
* @param int $userid User id.
|
|
|
2059 |
* @return bool
|
|
|
2060 |
*/
|
1254 |
ariadna |
2061 |
public function should_provide_intro_attachments(int $userid): bool
|
|
|
2062 |
{
|
1 |
efrain |
2063 |
$instance = $this->get_instance($userid);
|
|
|
2064 |
|
|
|
2065 |
// Check if user has permission to view attachments regardless of assignment settings.
|
|
|
2066 |
if (has_capability('moodle/course:manageactivities', $this->get_context())) {
|
|
|
2067 |
return true;
|
|
|
2068 |
}
|
|
|
2069 |
|
|
|
2070 |
// If assignment does not show intro, we never provide intro attachments.
|
|
|
2071 |
if (!$this->show_intro()) {
|
|
|
2072 |
return false;
|
|
|
2073 |
}
|
|
|
2074 |
|
|
|
2075 |
// If intro attachments should only be shown when submission is started, check if there is an open submission.
|
|
|
2076 |
if (!empty($instance->submissionattachments) && !$this->submissions_open($userid, true)) {
|
|
|
2077 |
return false;
|
|
|
2078 |
}
|
|
|
2079 |
|
|
|
2080 |
return true;
|
|
|
2081 |
}
|
|
|
2082 |
|
|
|
2083 |
/**
|
|
|
2084 |
* Return a grade in user-friendly form, whether it's a scale or not.
|
|
|
2085 |
*
|
|
|
2086 |
* @param mixed $grade int|null
|
|
|
2087 |
* @param boolean $editing Are we allowing changes to this grade?
|
|
|
2088 |
* @param int $userid The user id the grade belongs to
|
|
|
2089 |
* @param int $modified Timestamp from when the grade was last modified
|
|
|
2090 |
* @return string User-friendly representation of grade
|
|
|
2091 |
*/
|
1254 |
ariadna |
2092 |
public function display_grade($grade, $editing, $userid = 0, $modified = 0)
|
|
|
2093 |
{
|
1 |
efrain |
2094 |
global $DB;
|
|
|
2095 |
|
|
|
2096 |
static $scalegrades = array();
|
|
|
2097 |
|
|
|
2098 |
$o = '';
|
|
|
2099 |
|
|
|
2100 |
if ($this->get_instance()->grade >= 0) {
|
|
|
2101 |
// Normal number.
|
|
|
2102 |
if ($editing && $this->get_instance()->grade > 0) {
|
|
|
2103 |
if ($grade < 0) {
|
|
|
2104 |
$displaygrade = '';
|
|
|
2105 |
} else {
|
|
|
2106 |
$displaygrade = format_float($grade, $this->get_grade_item()->get_decimals());
|
|
|
2107 |
}
|
|
|
2108 |
$o .= '<label class="accesshide" for="quickgrade_' . $userid . '">' .
|
1254 |
ariadna |
2109 |
get_string('usergrade', 'assign') .
|
|
|
2110 |
'</label>';
|
1 |
efrain |
2111 |
$o .= '<input type="text"
|
|
|
2112 |
id="quickgrade_' . $userid . '"
|
|
|
2113 |
name="quickgrade_' . $userid . '"
|
|
|
2114 |
value="' . $displaygrade . '"
|
|
|
2115 |
size="6"
|
|
|
2116 |
maxlength="10"
|
|
|
2117 |
class="quickgrade"/>';
|
|
|
2118 |
$o .= ' / ' . format_float($this->get_instance()->grade, $this->get_grade_item()->get_decimals());
|
|
|
2119 |
return $o;
|
|
|
2120 |
} else {
|
|
|
2121 |
if ($grade == -1 || $grade === null) {
|
|
|
2122 |
$o .= '-';
|
|
|
2123 |
} else {
|
|
|
2124 |
$item = $this->get_grade_item();
|
|
|
2125 |
$o .= grade_format_gradevalue($grade, $item);
|
|
|
2126 |
if ($item->get_displaytype() == GRADE_DISPLAY_TYPE_REAL) {
|
|
|
2127 |
// If displaying the raw grade, also display the total value.
|
|
|
2128 |
$o .= ' / ' . format_float($this->get_instance()->grade, $item->get_decimals());
|
|
|
2129 |
}
|
|
|
2130 |
}
|
|
|
2131 |
return $o;
|
|
|
2132 |
}
|
|
|
2133 |
} else {
|
|
|
2134 |
// Scale.
|
|
|
2135 |
if (empty($this->cache['scale'])) {
|
1254 |
ariadna |
2136 |
if ($scale = $DB->get_record('scale', array('id' => - ($this->get_instance()->grade)))) {
|
1 |
efrain |
2137 |
$this->cache['scale'] = make_menu_from_list($scale->scale);
|
|
|
2138 |
} else {
|
|
|
2139 |
$o .= '-';
|
|
|
2140 |
return $o;
|
|
|
2141 |
}
|
|
|
2142 |
}
|
|
|
2143 |
if ($editing) {
|
|
|
2144 |
$o .= '<label class="accesshide"
|
|
|
2145 |
for="quickgrade_' . $userid . '">' .
|
1254 |
ariadna |
2146 |
get_string('usergrade', 'assign') .
|
|
|
2147 |
'</label>';
|
1 |
efrain |
2148 |
$o .= '<select name="quickgrade_' . $userid . '" class="quickgrade">';
|
|
|
2149 |
$o .= '<option value="-1">' . get_string('nograde') . '</option>';
|
|
|
2150 |
foreach ($this->cache['scale'] as $optionid => $option) {
|
|
|
2151 |
$selected = '';
|
|
|
2152 |
if ($grade == $optionid) {
|
|
|
2153 |
$selected = 'selected="selected"';
|
|
|
2154 |
}
|
|
|
2155 |
$o .= '<option value="' . $optionid . '" ' . $selected . '>' . $option . '</option>';
|
|
|
2156 |
}
|
|
|
2157 |
$o .= '</select>';
|
|
|
2158 |
return $o;
|
|
|
2159 |
} else {
|
|
|
2160 |
$scaleid = (int)$grade;
|
|
|
2161 |
if (isset($this->cache['scale'][$scaleid])) {
|
|
|
2162 |
$o .= $this->cache['scale'][$scaleid];
|
|
|
2163 |
return $o;
|
|
|
2164 |
}
|
|
|
2165 |
$o .= '-';
|
|
|
2166 |
return $o;
|
|
|
2167 |
}
|
|
|
2168 |
}
|
|
|
2169 |
}
|
|
|
2170 |
|
|
|
2171 |
/**
|
|
|
2172 |
* Get the submission status/grading status for all submissions in this assignment for the
|
|
|
2173 |
* given paticipants.
|
|
|
2174 |
*
|
|
|
2175 |
* These statuses match the available filters (requiregrading, submitted, notsubmitted, grantedextension).
|
|
|
2176 |
* If this is a group assignment, group info is also returned.
|
|
|
2177 |
*
|
|
|
2178 |
* @param array $participants an associative array where the key is the participant id and
|
|
|
2179 |
* the value is the participant record.
|
|
|
2180 |
* @return array an associative array where the key is the participant id and the value is
|
|
|
2181 |
* the participant record.
|
|
|
2182 |
*/
|
1254 |
ariadna |
2183 |
private function get_submission_info_for_participants($participants)
|
|
|
2184 |
{
|
1 |
efrain |
2185 |
global $DB;
|
|
|
2186 |
|
|
|
2187 |
if (empty($participants)) {
|
|
|
2188 |
return $participants;
|
|
|
2189 |
}
|
|
|
2190 |
|
|
|
2191 |
list($insql, $params) = $DB->get_in_or_equal(array_keys($participants), SQL_PARAMS_NAMED);
|
|
|
2192 |
|
|
|
2193 |
$assignid = $this->get_instance()->id;
|
|
|
2194 |
$params['assignmentid1'] = $assignid;
|
|
|
2195 |
$params['assignmentid2'] = $assignid;
|
|
|
2196 |
$params['assignmentid3'] = $assignid;
|
|
|
2197 |
|
|
|
2198 |
$fields = 'SELECT u.id, s.status, s.timemodified AS stime, g.timemodified AS gtime, g.grade, uf.extensionduedate';
|
|
|
2199 |
$from = ' FROM {user} u
|
|
|
2200 |
LEFT JOIN {assign_submission} s
|
|
|
2201 |
ON u.id = s.userid
|
|
|
2202 |
AND s.assignment = :assignmentid1
|
|
|
2203 |
AND s.latest = 1
|
|
|
2204 |
LEFT JOIN {assign_grades} g
|
|
|
2205 |
ON u.id = g.userid
|
|
|
2206 |
AND g.assignment = :assignmentid2
|
|
|
2207 |
AND g.attemptnumber = s.attemptnumber
|
|
|
2208 |
LEFT JOIN {assign_user_flags} uf
|
|
|
2209 |
ON u.id = uf.userid
|
|
|
2210 |
AND uf.assignment = :assignmentid3
|
|
|
2211 |
';
|
|
|
2212 |
$where = ' WHERE u.id ' . $insql;
|
|
|
2213 |
|
|
|
2214 |
if (!empty($this->get_instance()->blindmarking)) {
|
|
|
2215 |
$from .= 'LEFT JOIN {assign_user_mapping} um
|
|
|
2216 |
ON u.id = um.userid
|
|
|
2217 |
AND um.assignment = :assignmentid4 ';
|
|
|
2218 |
$params['assignmentid4'] = $assignid;
|
|
|
2219 |
$fields .= ', um.id as recordid ';
|
|
|
2220 |
}
|
|
|
2221 |
|
|
|
2222 |
$sql = "$fields $from $where";
|
|
|
2223 |
|
|
|
2224 |
$records = $DB->get_records_sql($sql, $params);
|
|
|
2225 |
|
|
|
2226 |
if ($this->get_instance()->teamsubmission) {
|
|
|
2227 |
// Get all groups.
|
1254 |
ariadna |
2228 |
$allgroups = groups_get_all_groups(
|
|
|
2229 |
$this->get_course()->id,
|
|
|
2230 |
array_keys($participants),
|
|
|
2231 |
$this->get_instance()->teamsubmissiongroupingid,
|
|
|
2232 |
'DISTINCT g.id, g.name'
|
|
|
2233 |
);
|
1 |
efrain |
2234 |
}
|
|
|
2235 |
foreach ($participants as $userid => $participant) {
|
|
|
2236 |
$participants[$userid]->fullname = $this->fullname($participant);
|
|
|
2237 |
$participants[$userid]->submitted = false;
|
|
|
2238 |
$participants[$userid]->requiregrading = false;
|
|
|
2239 |
$participants[$userid]->grantedextension = false;
|
|
|
2240 |
$participants[$userid]->submissionstatus = '';
|
|
|
2241 |
}
|
|
|
2242 |
|
|
|
2243 |
foreach ($records as $userid => $submissioninfo) {
|
|
|
2244 |
// These filters are 100% the same as the ones in the grading table SQL.
|
|
|
2245 |
$submitted = false;
|
|
|
2246 |
$requiregrading = false;
|
|
|
2247 |
$grantedextension = false;
|
|
|
2248 |
$submissionstatus = !empty($submissioninfo->status) ? $submissioninfo->status : '';
|
|
|
2249 |
|
|
|
2250 |
if (!empty($submissioninfo->stime) && $submissioninfo->status == ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
|
|
|
2251 |
$submitted = true;
|
|
|
2252 |
}
|
|
|
2253 |
|
|
|
2254 |
if ($submitted && ($submissioninfo->stime >= $submissioninfo->gtime ||
|
1254 |
ariadna |
2255 |
empty($submissioninfo->gtime) ||
|
|
|
2256 |
$submissioninfo->grade === null)) {
|
1 |
efrain |
2257 |
$requiregrading = true;
|
|
|
2258 |
}
|
|
|
2259 |
|
|
|
2260 |
if (!empty($submissioninfo->extensionduedate)) {
|
|
|
2261 |
$grantedextension = true;
|
|
|
2262 |
}
|
|
|
2263 |
|
|
|
2264 |
$participants[$userid]->submitted = $submitted;
|
|
|
2265 |
$participants[$userid]->requiregrading = $requiregrading;
|
|
|
2266 |
$participants[$userid]->grantedextension = $grantedextension;
|
|
|
2267 |
$participants[$userid]->submissionstatus = $submissionstatus;
|
|
|
2268 |
if ($this->get_instance()->teamsubmission) {
|
|
|
2269 |
$group = $this->get_submission_group($userid);
|
|
|
2270 |
if ($group) {
|
|
|
2271 |
$participants[$userid]->groupid = $group->id;
|
|
|
2272 |
$participants[$userid]->groupname = $group->name;
|
|
|
2273 |
}
|
|
|
2274 |
}
|
|
|
2275 |
}
|
|
|
2276 |
return $participants;
|
|
|
2277 |
}
|
|
|
2278 |
|
|
|
2279 |
/**
|
|
|
2280 |
* Get the submission status/grading status for all submissions in this assignment.
|
|
|
2281 |
* These statuses match the available filters (requiregrading, submitted, notsubmitted, grantedextension).
|
|
|
2282 |
* If this is a group assignment, group info is also returned.
|
|
|
2283 |
*
|
|
|
2284 |
* @param int $currentgroup
|
|
|
2285 |
* @param boolean $tablesort Apply current user table sorting preferences.
|
|
|
2286 |
* @return array List of user records with extra fields 'submitted', 'notsubmitted', 'requiregrading', 'grantedextension',
|
|
|
2287 |
* 'groupid', 'groupname'
|
|
|
2288 |
*/
|
1254 |
ariadna |
2289 |
public function list_participants_with_filter_status_and_group($currentgroup, $tablesort = false)
|
|
|
2290 |
{
|
1 |
efrain |
2291 |
$participants = $this->list_participants($currentgroup, false, $tablesort);
|
|
|
2292 |
|
|
|
2293 |
if (empty($participants)) {
|
|
|
2294 |
return $participants;
|
|
|
2295 |
} else {
|
|
|
2296 |
return $this->get_submission_info_for_participants($participants);
|
|
|
2297 |
}
|
|
|
2298 |
}
|
|
|
2299 |
|
|
|
2300 |
/**
|
|
|
2301 |
* Return a valid order by segment for list_participants that matches
|
|
|
2302 |
* the sorting of the current grading table. Not every field is supported,
|
|
|
2303 |
* we are only concerned with a list of users so we can't search on anything
|
|
|
2304 |
* that is not part of the user information (like grading statud or last modified stuff).
|
|
|
2305 |
*
|
|
|
2306 |
* @return string Order by clause for list_participants
|
|
|
2307 |
*/
|
1254 |
ariadna |
2308 |
private function get_grading_sort_sql()
|
|
|
2309 |
{
|
1 |
efrain |
2310 |
$usersort = flexible_table::get_sort_for_table('mod_assign_grading');
|
|
|
2311 |
// TODO Does not support custom user profile fields (MDL-70456).
|
|
|
2312 |
$userfieldsapi = \core_user\fields::for_identity($this->context, false)->with_userpic();
|
|
|
2313 |
$userfields = $userfieldsapi->get_required_fields();
|
|
|
2314 |
$orderfields = explode(',', $usersort);
|
|
|
2315 |
$validlist = [];
|
|
|
2316 |
|
|
|
2317 |
foreach ($orderfields as $orderfield) {
|
|
|
2318 |
$orderfield = trim($orderfield);
|
|
|
2319 |
foreach ($userfields as $field) {
|
|
|
2320 |
$parts = explode(' ', $orderfield);
|
|
|
2321 |
if ($parts[0] == $field) {
|
|
|
2322 |
// Prepend the user table prefix and count this as a valid order field.
|
|
|
2323 |
array_push($validlist, 'u.' . $orderfield);
|
|
|
2324 |
}
|
|
|
2325 |
}
|
|
|
2326 |
}
|
|
|
2327 |
// Produce a final list.
|
|
|
2328 |
$result = implode(',', $validlist);
|
|
|
2329 |
if (empty($result)) {
|
|
|
2330 |
// Fall back ordering when none has been set.
|
|
|
2331 |
$result = 'u.lastname, u.firstname, u.id';
|
|
|
2332 |
}
|
|
|
2333 |
|
|
|
2334 |
return $result;
|
|
|
2335 |
}
|
|
|
2336 |
|
|
|
2337 |
/**
|
|
|
2338 |
* Returns array with sql code and parameters returning all ids of users who have submitted an assignment.
|
|
|
2339 |
*
|
|
|
2340 |
* @param int $group The group that the query is for.
|
|
|
2341 |
* @return array list($sql, $params)
|
|
|
2342 |
*/
|
1254 |
ariadna |
2343 |
protected function get_submitted_sql($group = 0)
|
|
|
2344 |
{
|
1 |
efrain |
2345 |
// We need to guarentee unique table names.
|
|
|
2346 |
static $i = 0;
|
|
|
2347 |
$i++;
|
|
|
2348 |
$prefix = 'sa' . $i . '_';
|
|
|
2349 |
$params = [
|
|
|
2350 |
"{$prefix}assignment" => (int) $this->get_instance()->id,
|
|
|
2351 |
"{$prefix}status" => ASSIGN_SUBMISSION_STATUS_NEW,
|
|
|
2352 |
];
|
|
|
2353 |
$capjoin = get_enrolled_with_capabilities_join($this->context, $prefix, '', $group, $this->show_only_active_users());
|
|
|
2354 |
$params += $capjoin->params;
|
|
|
2355 |
$sql = "SELECT {$prefix}s.userid
|
|
|
2356 |
FROM {assign_submission} {$prefix}s
|
|
|
2357 |
JOIN {user} {$prefix}u ON {$prefix}u.id = {$prefix}s.userid
|
|
|
2358 |
$capjoin->joins
|
|
|
2359 |
WHERE {$prefix}s.assignment = :{$prefix}assignment
|
|
|
2360 |
AND {$prefix}s.status <> :{$prefix}status
|
|
|
2361 |
AND $capjoin->wheres";
|
|
|
2362 |
return array($sql, $params);
|
|
|
2363 |
}
|
|
|
2364 |
|
|
|
2365 |
/**
|
|
|
2366 |
* Load a list of users enrolled in the current course with the specified permission and group.
|
|
|
2367 |
* 0 for no group.
|
|
|
2368 |
* Apply any current sort filters from the grading table.
|
|
|
2369 |
*
|
|
|
2370 |
* @param int $currentgroup
|
|
|
2371 |
* @param bool $idsonly
|
|
|
2372 |
* @param bool $tablesort
|
|
|
2373 |
* @return array List of user records
|
|
|
2374 |
*/
|
1254 |
ariadna |
2375 |
public function list_participants($currentgroup, $idsonly, $tablesort = false)
|
|
|
2376 |
{
|
1 |
efrain |
2377 |
global $DB, $USER;
|
|
|
2378 |
|
|
|
2379 |
// Get the last known sort order for the grading table.
|
|
|
2380 |
|
|
|
2381 |
if (empty($currentgroup)) {
|
|
|
2382 |
$currentgroup = 0;
|
|
|
2383 |
}
|
|
|
2384 |
|
|
|
2385 |
$key = $this->context->id . '-' . $currentgroup . '-' . $this->show_only_active_users();
|
|
|
2386 |
if (!isset($this->participants[$key])) {
|
1254 |
ariadna |
2387 |
list($esql, $params) = get_enrolled_sql(
|
|
|
2388 |
$this->context,
|
|
|
2389 |
'mod/assign:submit',
|
|
|
2390 |
$currentgroup,
|
|
|
2391 |
$this->show_only_active_users()
|
|
|
2392 |
);
|
1 |
efrain |
2393 |
list($ssql, $sparams) = $this->get_submitted_sql($currentgroup);
|
|
|
2394 |
$params += $sparams;
|
|
|
2395 |
|
|
|
2396 |
$fields = 'u.*';
|
|
|
2397 |
$orderby = 'u.lastname, u.firstname, u.id';
|
|
|
2398 |
|
|
|
2399 |
$additionaljoins = '';
|
|
|
2400 |
$additionalfilters = '';
|
|
|
2401 |
$instance = $this->get_instance();
|
|
|
2402 |
if (!empty($instance->blindmarking)) {
|
|
|
2403 |
$additionaljoins .= " LEFT JOIN {assign_user_mapping} um
|
|
|
2404 |
ON u.id = um.userid
|
|
|
2405 |
AND um.assignment = :assignmentid1
|
|
|
2406 |
LEFT JOIN {assign_submission} s
|
|
|
2407 |
ON u.id = s.userid
|
|
|
2408 |
AND s.assignment = :assignmentid2
|
|
|
2409 |
AND s.latest = 1
|
|
|
2410 |
";
|
|
|
2411 |
$params['assignmentid1'] = (int) $instance->id;
|
|
|
2412 |
$params['assignmentid2'] = (int) $instance->id;
|
|
|
2413 |
$fields .= ', um.id as recordid ';
|
|
|
2414 |
|
|
|
2415 |
// Sort by submission time first, then by um.id to sort reliably by the blind marking id.
|
|
|
2416 |
// Note, different DBs have different ordering of NULL values.
|
|
|
2417 |
// Therefore we coalesce the current time into the timecreated field, and the max possible integer into
|
|
|
2418 |
// the ID field.
|
|
|
2419 |
if (empty($tablesort)) {
|
|
|
2420 |
$orderby = "COALESCE(s.timecreated, " . time() . ") ASC, COALESCE(s.id, " . PHP_INT_MAX . ") ASC, um.id ASC";
|
|
|
2421 |
}
|
|
|
2422 |
}
|
|
|
2423 |
|
1254 |
ariadna |
2424 |
if (
|
|
|
2425 |
$instance->markingworkflow &&
|
|
|
2426 |
$instance->markingallocation &&
|
|
|
2427 |
!has_capability('mod/assign:manageallocations', $this->get_context()) &&
|
|
|
2428 |
has_capability('mod/assign:grade', $this->get_context())
|
|
|
2429 |
) {
|
1 |
efrain |
2430 |
|
|
|
2431 |
$additionaljoins .= ' LEFT JOIN {assign_user_flags} uf
|
|
|
2432 |
ON u.id = uf.userid
|
|
|
2433 |
AND uf.assignment = :assignmentid3';
|
|
|
2434 |
|
|
|
2435 |
$params['assignmentid3'] = (int) $instance->id;
|
|
|
2436 |
|
|
|
2437 |
$additionalfilters .= ' AND uf.allocatedmarker = :markerid';
|
|
|
2438 |
$params['markerid'] = $USER->id;
|
|
|
2439 |
}
|
|
|
2440 |
|
|
|
2441 |
$sql = "SELECT $fields
|
|
|
2442 |
FROM {user} u
|
|
|
2443 |
JOIN ($esql UNION $ssql) je ON je.id = u.id
|
|
|
2444 |
$additionaljoins
|
|
|
2445 |
WHERE u.deleted = 0
|
|
|
2446 |
$additionalfilters
|
|
|
2447 |
ORDER BY $orderby";
|
|
|
2448 |
|
|
|
2449 |
$users = $DB->get_records_sql($sql, $params);
|
|
|
2450 |
|
|
|
2451 |
$cm = $this->get_course_module();
|
|
|
2452 |
$info = new \core_availability\info_module($cm);
|
|
|
2453 |
$users = $info->filter_user_list($users);
|
|
|
2454 |
|
|
|
2455 |
$this->participants[$key] = $users;
|
|
|
2456 |
}
|
|
|
2457 |
|
|
|
2458 |
if ($tablesort) {
|
|
|
2459 |
// Resort the user list according to the grading table sort and filter settings.
|
|
|
2460 |
$sortedfiltereduserids = $this->get_grading_userid_list(true, '');
|
|
|
2461 |
$sortedfilteredusers = [];
|
|
|
2462 |
foreach ($sortedfiltereduserids as $nextid) {
|
|
|
2463 |
$nextid = intval($nextid);
|
|
|
2464 |
if (isset($this->participants[$key][$nextid])) {
|
|
|
2465 |
$sortedfilteredusers[$nextid] = $this->participants[$key][$nextid];
|
|
|
2466 |
}
|
|
|
2467 |
}
|
|
|
2468 |
$this->participants[$key] = $sortedfilteredusers;
|
|
|
2469 |
}
|
|
|
2470 |
|
|
|
2471 |
if ($idsonly) {
|
|
|
2472 |
$idslist = array();
|
|
|
2473 |
foreach ($this->participants[$key] as $id => $user) {
|
|
|
2474 |
$idslist[$id] = new stdClass();
|
|
|
2475 |
$idslist[$id]->id = $id;
|
|
|
2476 |
}
|
|
|
2477 |
return $idslist;
|
|
|
2478 |
}
|
|
|
2479 |
return $this->participants[$key];
|
|
|
2480 |
}
|
|
|
2481 |
|
|
|
2482 |
/**
|
|
|
2483 |
* Load a user if they are enrolled in the current course. Populated with submission
|
|
|
2484 |
* status for this assignment.
|
|
|
2485 |
*
|
|
|
2486 |
* @param int $userid
|
|
|
2487 |
* @return null|stdClass user record
|
|
|
2488 |
*/
|
1254 |
ariadna |
2489 |
public function get_participant($userid)
|
|
|
2490 |
{
|
1 |
efrain |
2491 |
global $DB, $USER;
|
|
|
2492 |
|
|
|
2493 |
if ($userid == $USER->id) {
|
|
|
2494 |
$participant = clone ($USER);
|
|
|
2495 |
} else {
|
|
|
2496 |
$participant = $DB->get_record('user', array('id' => $userid));
|
|
|
2497 |
}
|
|
|
2498 |
if (!$participant) {
|
|
|
2499 |
return null;
|
|
|
2500 |
}
|
|
|
2501 |
|
|
|
2502 |
if (!is_enrolled($this->context, $participant, '', $this->show_only_active_users())) {
|
|
|
2503 |
return null;
|
|
|
2504 |
}
|
|
|
2505 |
|
|
|
2506 |
$result = $this->get_submission_info_for_participants(array($participant->id => $participant));
|
|
|
2507 |
|
|
|
2508 |
$submissioninfo = $result[$participant->id];
|
|
|
2509 |
if (!$submissioninfo->submitted && !has_capability('mod/assign:submit', $this->context, $userid)) {
|
|
|
2510 |
return null;
|
|
|
2511 |
}
|
|
|
2512 |
|
|
|
2513 |
return $submissioninfo;
|
|
|
2514 |
}
|
|
|
2515 |
|
|
|
2516 |
/**
|
|
|
2517 |
* Load a count of valid teams for this assignment.
|
|
|
2518 |
*
|
|
|
2519 |
* @param int $activitygroup Activity active group
|
|
|
2520 |
* @return int number of valid teams
|
|
|
2521 |
*/
|
1254 |
ariadna |
2522 |
public function count_teams($activitygroup = 0)
|
|
|
2523 |
{
|
1 |
efrain |
2524 |
|
|
|
2525 |
$count = 0;
|
|
|
2526 |
|
|
|
2527 |
$participants = $this->list_participants($activitygroup, true);
|
|
|
2528 |
|
|
|
2529 |
// If a team submission grouping id is provided all good as all returned groups
|
|
|
2530 |
// are the submission teams, but if no team submission grouping was specified
|
|
|
2531 |
// $groups will contain all participants groups.
|
|
|
2532 |
if ($this->get_instance()->teamsubmissiongroupingid) {
|
|
|
2533 |
|
|
|
2534 |
// We restrict the users to the selected group ones.
|
1254 |
ariadna |
2535 |
$groups = groups_get_all_groups(
|
|
|
2536 |
$this->get_course()->id,
|
|
|
2537 |
array_keys($participants),
|
|
|
2538 |
$this->get_instance()->teamsubmissiongroupingid,
|
|
|
2539 |
'DISTINCT g.id, g.name'
|
|
|
2540 |
);
|
1 |
efrain |
2541 |
|
|
|
2542 |
$count = count($groups);
|
|
|
2543 |
|
|
|
2544 |
// When a specific group is selected we don't count the default group users.
|
|
|
2545 |
if ($activitygroup == 0) {
|
|
|
2546 |
if (empty($this->get_instance()->preventsubmissionnotingroup)) {
|
|
|
2547 |
// See if there are any users in the default group.
|
|
|
2548 |
$defaultusers = $this->get_submission_group_members(0, true);
|
|
|
2549 |
if (count($defaultusers) > 0) {
|
|
|
2550 |
$count += 1;
|
|
|
2551 |
}
|
|
|
2552 |
}
|
|
|
2553 |
} else if ($activitygroup != 0 && empty($groups)) {
|
|
|
2554 |
// Set count to 1 if $groups returns empty.
|
|
|
2555 |
// It means the group is not part of $this->get_instance()->teamsubmissiongroupingid.
|
|
|
2556 |
$count = 1;
|
|
|
2557 |
}
|
|
|
2558 |
} else {
|
|
|
2559 |
// It is faster to loop around participants if no grouping was specified.
|
|
|
2560 |
$groups = array();
|
|
|
2561 |
foreach ($participants as $participant) {
|
|
|
2562 |
if ($group = $this->get_submission_group($participant->id)) {
|
|
|
2563 |
$groups[$group->id] = true;
|
|
|
2564 |
} else if (empty($this->get_instance()->preventsubmissionnotingroup)) {
|
|
|
2565 |
$groups[0] = true;
|
|
|
2566 |
}
|
|
|
2567 |
}
|
|
|
2568 |
|
|
|
2569 |
$count = count($groups);
|
|
|
2570 |
}
|
|
|
2571 |
|
|
|
2572 |
return $count;
|
|
|
2573 |
}
|
|
|
2574 |
|
|
|
2575 |
/**
|
|
|
2576 |
* Load a count of active users enrolled in the current course with the specified permission and group.
|
|
|
2577 |
* 0 for no group.
|
|
|
2578 |
*
|
|
|
2579 |
* @param int $currentgroup
|
|
|
2580 |
* @return int number of matching users
|
|
|
2581 |
*/
|
1254 |
ariadna |
2582 |
public function count_participants($currentgroup)
|
|
|
2583 |
{
|
1 |
efrain |
2584 |
return count($this->list_participants($currentgroup, true));
|
|
|
2585 |
}
|
|
|
2586 |
|
|
|
2587 |
/**
|
|
|
2588 |
* Load a count of active users submissions in the current module that require grading
|
|
|
2589 |
* This means the submission modification time is more recent than the
|
|
|
2590 |
* grading modification time and the status is SUBMITTED.
|
|
|
2591 |
*
|
|
|
2592 |
* @param mixed $currentgroup int|null the group for counting (if null the function will determine it)
|
|
|
2593 |
* @return int number of matching submissions
|
|
|
2594 |
*/
|
1254 |
ariadna |
2595 |
public function count_submissions_need_grading($currentgroup = null)
|
|
|
2596 |
{
|
1 |
efrain |
2597 |
global $DB;
|
|
|
2598 |
|
|
|
2599 |
if ($this->get_instance()->teamsubmission) {
|
|
|
2600 |
// This does not make sense for group assignment because the submission is shared.
|
|
|
2601 |
return 0;
|
|
|
2602 |
}
|
|
|
2603 |
|
|
|
2604 |
if ($currentgroup === null) {
|
|
|
2605 |
$currentgroup = groups_get_activity_group($this->get_course_module(), true);
|
|
|
2606 |
}
|
|
|
2607 |
list($esql, $params) = get_enrolled_sql($this->get_context(), '', $currentgroup, true);
|
|
|
2608 |
|
|
|
2609 |
$params['assignid'] = $this->get_instance()->id;
|
|
|
2610 |
$params['submitted'] = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
|
|
|
2611 |
$sqlscalegrade = $this->get_instance()->grade < 0 ? ' OR g.grade = -1' : '';
|
|
|
2612 |
|
|
|
2613 |
$sql = 'SELECT COUNT(s.userid)
|
|
|
2614 |
FROM {assign_submission} s
|
|
|
2615 |
LEFT JOIN {assign_grades} g ON
|
|
|
2616 |
s.assignment = g.assignment AND
|
|
|
2617 |
s.userid = g.userid AND
|
|
|
2618 |
g.attemptnumber = s.attemptnumber
|
|
|
2619 |
JOIN(' . $esql . ') e ON e.id = s.userid
|
|
|
2620 |
WHERE
|
|
|
2621 |
s.latest = 1 AND
|
|
|
2622 |
s.assignment = :assignid AND
|
|
|
2623 |
s.timemodified IS NOT NULL AND
|
|
|
2624 |
s.status = :submitted AND
|
|
|
2625 |
(s.timemodified >= g.timemodified OR g.timemodified IS NULL OR g.grade IS NULL '
|
1254 |
ariadna |
2626 |
. $sqlscalegrade . ')';
|
1 |
efrain |
2627 |
|
|
|
2628 |
return $DB->count_records_sql($sql, $params);
|
|
|
2629 |
}
|
|
|
2630 |
|
|
|
2631 |
/**
|
|
|
2632 |
* Load a count of grades.
|
|
|
2633 |
*
|
|
|
2634 |
* @return int number of grades
|
|
|
2635 |
*/
|
1254 |
ariadna |
2636 |
public function count_grades()
|
|
|
2637 |
{
|
1 |
efrain |
2638 |
global $DB;
|
|
|
2639 |
|
|
|
2640 |
if (!$this->has_instance()) {
|
|
|
2641 |
return 0;
|
|
|
2642 |
}
|
|
|
2643 |
|
|
|
2644 |
$currentgroup = groups_get_activity_group($this->get_course_module(), true);
|
|
|
2645 |
list($esql, $params) = get_enrolled_sql($this->get_context(), 'mod/assign:submit', $currentgroup, true);
|
|
|
2646 |
|
|
|
2647 |
$params['assignid'] = $this->get_instance()->id;
|
|
|
2648 |
|
|
|
2649 |
$sql = 'SELECT COUNT(g.userid)
|
|
|
2650 |
FROM {assign_grades} g
|
|
|
2651 |
JOIN(' . $esql . ') e ON e.id = g.userid
|
|
|
2652 |
WHERE g.assignment = :assignid';
|
|
|
2653 |
|
|
|
2654 |
return $DB->count_records_sql($sql, $params);
|
|
|
2655 |
}
|
|
|
2656 |
|
|
|
2657 |
/**
|
|
|
2658 |
* Load a count of submissions.
|
|
|
2659 |
*
|
|
|
2660 |
* @param bool $includenew When true, also counts the submissions with status 'new'.
|
|
|
2661 |
* @return int number of submissions
|
|
|
2662 |
*/
|
1254 |
ariadna |
2663 |
public function count_submissions($includenew = false)
|
|
|
2664 |
{
|
1 |
efrain |
2665 |
global $DB;
|
|
|
2666 |
|
|
|
2667 |
if (!$this->has_instance()) {
|
|
|
2668 |
return 0;
|
|
|
2669 |
}
|
|
|
2670 |
|
|
|
2671 |
$params = array();
|
|
|
2672 |
$sqlnew = '';
|
|
|
2673 |
|
|
|
2674 |
if (!$includenew) {
|
|
|
2675 |
$sqlnew = ' AND s.status <> :status ';
|
|
|
2676 |
$params['status'] = ASSIGN_SUBMISSION_STATUS_NEW;
|
|
|
2677 |
}
|
|
|
2678 |
|
|
|
2679 |
if ($this->get_instance()->teamsubmission) {
|
|
|
2680 |
// We cannot join on the enrolment tables for group submissions (no userid).
|
|
|
2681 |
$sql = 'SELECT COUNT(DISTINCT s.groupid)
|
|
|
2682 |
FROM {assign_submission} s
|
|
|
2683 |
WHERE
|
|
|
2684 |
s.assignment = :assignid AND
|
|
|
2685 |
s.timemodified IS NOT NULL AND
|
|
|
2686 |
s.userid = :groupuserid' .
|
1254 |
ariadna |
2687 |
$sqlnew;
|
1 |
efrain |
2688 |
|
|
|
2689 |
$params['assignid'] = $this->get_instance()->id;
|
|
|
2690 |
$params['groupuserid'] = 0;
|
|
|
2691 |
} else {
|
|
|
2692 |
$currentgroup = groups_get_activity_group($this->get_course_module(), true);
|
|
|
2693 |
list($esql, $enrolparams) = get_enrolled_sql($this->get_context(), 'mod/assign:submit', $currentgroup, true);
|
|
|
2694 |
|
|
|
2695 |
$params = array_merge($params, $enrolparams);
|
|
|
2696 |
$params['assignid'] = $this->get_instance()->id;
|
|
|
2697 |
|
|
|
2698 |
$sql = 'SELECT COUNT(DISTINCT s.userid)
|
|
|
2699 |
FROM {assign_submission} s
|
|
|
2700 |
JOIN(' . $esql . ') e ON e.id = s.userid
|
|
|
2701 |
WHERE
|
|
|
2702 |
s.assignment = :assignid AND
|
|
|
2703 |
s.timemodified IS NOT NULL ' .
|
1254 |
ariadna |
2704 |
$sqlnew;
|
1 |
efrain |
2705 |
}
|
|
|
2706 |
|
|
|
2707 |
return $DB->count_records_sql($sql, $params);
|
|
|
2708 |
}
|
|
|
2709 |
|
|
|
2710 |
/**
|
|
|
2711 |
* Load a count of submissions with a specified status.
|
|
|
2712 |
*
|
|
|
2713 |
* @param string $status The submission status - should match one of the constants
|
|
|
2714 |
* @param mixed $currentgroup int|null the group for counting (if null the function will determine it)
|
|
|
2715 |
* @return int number of matching submissions
|
|
|
2716 |
*/
|
1254 |
ariadna |
2717 |
public function count_submissions_with_status($status, $currentgroup = null)
|
|
|
2718 |
{
|
1 |
efrain |
2719 |
global $DB;
|
|
|
2720 |
|
|
|
2721 |
if ($currentgroup === null) {
|
|
|
2722 |
$currentgroup = groups_get_activity_group($this->get_course_module(), true);
|
|
|
2723 |
}
|
|
|
2724 |
list($esql, $params) = get_enrolled_sql($this->get_context(), '', $currentgroup, true);
|
|
|
2725 |
|
|
|
2726 |
$params['assignid'] = $this->get_instance()->id;
|
|
|
2727 |
$params['assignid2'] = $this->get_instance()->id;
|
|
|
2728 |
$params['submissionstatus'] = $status;
|
|
|
2729 |
|
|
|
2730 |
if ($this->get_instance()->teamsubmission) {
|
|
|
2731 |
|
|
|
2732 |
$groupsstr = '';
|
|
|
2733 |
if ($currentgroup != 0) {
|
|
|
2734 |
// If there is an active group we should only display the current group users groups.
|
|
|
2735 |
$participants = $this->list_participants($currentgroup, true);
|
1254 |
ariadna |
2736 |
$groups = groups_get_all_groups(
|
|
|
2737 |
$this->get_course()->id,
|
|
|
2738 |
array_keys($participants),
|
|
|
2739 |
$this->get_instance()->teamsubmissiongroupingid,
|
|
|
2740 |
'DISTINCT g.id, g.name'
|
|
|
2741 |
);
|
1 |
efrain |
2742 |
if (empty($groups)) {
|
|
|
2743 |
// If $groups is empty it means it is not part of $this->get_instance()->teamsubmissiongroupingid.
|
|
|
2744 |
// All submissions from students that do not belong to any of teamsubmissiongroupingid groups
|
|
|
2745 |
// count towards groupid = 0. Setting to true as only '0' key matters.
|
|
|
2746 |
$groups = [true];
|
|
|
2747 |
}
|
|
|
2748 |
list($groupssql, $groupsparams) = $DB->get_in_or_equal(array_keys($groups), SQL_PARAMS_NAMED);
|
|
|
2749 |
$groupsstr = 's.groupid ' . $groupssql . ' AND';
|
|
|
2750 |
$params = $params + $groupsparams;
|
|
|
2751 |
}
|
|
|
2752 |
$sql = 'SELECT COUNT(s.groupid)
|
|
|
2753 |
FROM {assign_submission} s
|
|
|
2754 |
WHERE
|
|
|
2755 |
s.latest = 1 AND
|
|
|
2756 |
s.assignment = :assignid AND
|
|
|
2757 |
s.timemodified IS NOT NULL AND
|
|
|
2758 |
s.userid = :groupuserid AND '
|
1254 |
ariadna |
2759 |
. $groupsstr . '
|
1 |
efrain |
2760 |
s.status = :submissionstatus';
|
|
|
2761 |
$params['groupuserid'] = 0;
|
|
|
2762 |
} else {
|
|
|
2763 |
$sql = 'SELECT COUNT(s.userid)
|
|
|
2764 |
FROM {assign_submission} s
|
|
|
2765 |
JOIN(' . $esql . ') e ON e.id = s.userid
|
|
|
2766 |
WHERE
|
|
|
2767 |
s.latest = 1 AND
|
|
|
2768 |
s.assignment = :assignid AND
|
|
|
2769 |
s.timemodified IS NOT NULL AND
|
|
|
2770 |
s.status = :submissionstatus';
|
|
|
2771 |
}
|
|
|
2772 |
|
|
|
2773 |
return $DB->count_records_sql($sql, $params);
|
|
|
2774 |
}
|
|
|
2775 |
|
|
|
2776 |
/**
|
|
|
2777 |
* Utility function to get the userid for every row in the grading table
|
|
|
2778 |
* so the order can be frozen while we iterate it.
|
|
|
2779 |
*
|
|
|
2780 |
* @param boolean $cached If true, the cached list from the session could be returned.
|
|
|
2781 |
* @param string $useridlistid String value used for caching the participant list.
|
|
|
2782 |
* @return array An array of userids
|
|
|
2783 |
*/
|
1254 |
ariadna |
2784 |
protected function get_grading_userid_list($cached = false, $useridlistid = '')
|
|
|
2785 |
{
|
1 |
efrain |
2786 |
global $SESSION;
|
|
|
2787 |
|
|
|
2788 |
if ($cached) {
|
|
|
2789 |
if (empty($useridlistid)) {
|
|
|
2790 |
$useridlistid = $this->get_useridlist_key_id();
|
|
|
2791 |
}
|
|
|
2792 |
$useridlistkey = $this->get_useridlist_key($useridlistid);
|
|
|
2793 |
if (empty($SESSION->mod_assign_useridlist[$useridlistkey])) {
|
|
|
2794 |
$SESSION->mod_assign_useridlist[$useridlistkey] = $this->get_grading_userid_list(false, '');
|
|
|
2795 |
}
|
|
|
2796 |
return $SESSION->mod_assign_useridlist[$useridlistkey];
|
|
|
2797 |
}
|
|
|
2798 |
$filter = get_user_preferences('assign_filter', '');
|
|
|
2799 |
$table = new assign_grading_table($this, 0, $filter, 0, false);
|
|
|
2800 |
|
|
|
2801 |
$useridlist = $table->get_column_data('userid');
|
|
|
2802 |
|
|
|
2803 |
return $useridlist;
|
|
|
2804 |
}
|
|
|
2805 |
|
|
|
2806 |
/**
|
|
|
2807 |
* Is user id filtered by user filters and table preferences.
|
|
|
2808 |
*
|
|
|
2809 |
* @param int $userid User id that needs to be checked.
|
|
|
2810 |
* @return bool
|
|
|
2811 |
*/
|
1254 |
ariadna |
2812 |
public function is_userid_filtered($userid)
|
|
|
2813 |
{
|
1 |
efrain |
2814 |
$users = $this->get_grading_userid_list();
|
|
|
2815 |
return in_array($userid, $users);
|
|
|
2816 |
}
|
|
|
2817 |
|
|
|
2818 |
/**
|
|
|
2819 |
* Finds all assignment notifications that have yet to be mailed out, and mails them.
|
|
|
2820 |
*
|
|
|
2821 |
* Cron function to be run periodically according to the moodle cron.
|
|
|
2822 |
*
|
|
|
2823 |
* @return bool
|
|
|
2824 |
*/
|
1254 |
ariadna |
2825 |
public static function cron()
|
|
|
2826 |
{
|
1 |
efrain |
2827 |
global $DB;
|
|
|
2828 |
|
|
|
2829 |
// Only ever send a max of one days worth of updates.
|
|
|
2830 |
$yesterday = time() - (24 * 3600);
|
|
|
2831 |
$timenow = time();
|
|
|
2832 |
$task = \core\task\manager::get_scheduled_task(mod_assign\task\cron_task::class);
|
|
|
2833 |
$lastruntime = $task->get_last_run_time();
|
|
|
2834 |
|
|
|
2835 |
// Collect all submissions that require mailing.
|
|
|
2836 |
// Submissions are included if all are true:
|
|
|
2837 |
// - The assignment is visible in the gradebook.
|
|
|
2838 |
// - No previous notification has been sent.
|
|
|
2839 |
// - The grader was a real user, not an automated process.
|
|
|
2840 |
// - The grade was updated in the past 24 hours.
|
|
|
2841 |
// - If marking workflow is enabled, the workflow state is at 'released'.
|
|
|
2842 |
$sql = "SELECT g.id as gradeid, a.course, a.name, a.blindmarking, a.revealidentities, a.hidegrader,
|
|
|
2843 |
g.*, g.timemodified as lastmodified, cm.id as cmid, um.id as recordid
|
|
|
2844 |
FROM {assign} a
|
|
|
2845 |
JOIN {assign_grades} g ON g.assignment = a.id
|
|
|
2846 |
LEFT JOIN {assign_user_flags} uf ON uf.assignment = a.id AND uf.userid = g.userid
|
|
|
2847 |
JOIN {course_modules} cm ON cm.course = a.course AND cm.instance = a.id
|
|
|
2848 |
JOIN {modules} md ON md.id = cm.module AND md.name = 'assign'
|
|
|
2849 |
JOIN {grade_items} gri ON gri.iteminstance = a.id AND gri.courseid = a.course AND gri.itemmodule = md.name
|
|
|
2850 |
LEFT JOIN {assign_user_mapping} um ON g.id = um.userid AND um.assignment = a.id
|
|
|
2851 |
WHERE (a.markingworkflow = 0 OR (a.markingworkflow = 1 AND uf.workflowstate = :wfreleased)) AND
|
|
|
2852 |
g.grader > 0 AND uf.mailed = 0 AND gri.hidden = 0 AND
|
|
|
2853 |
g.timemodified >= :yesterday AND g.timemodified <= :today
|
|
|
2854 |
ORDER BY a.course, cm.id";
|
|
|
2855 |
|
|
|
2856 |
$params = array(
|
|
|
2857 |
'yesterday' => $yesterday,
|
|
|
2858 |
'today' => $timenow,
|
|
|
2859 |
'wfreleased' => ASSIGN_MARKING_WORKFLOW_STATE_RELEASED,
|
|
|
2860 |
);
|
|
|
2861 |
$submissions = $DB->get_records_sql($sql, $params);
|
|
|
2862 |
|
|
|
2863 |
if (!empty($submissions)) {
|
|
|
2864 |
|
|
|
2865 |
mtrace('Processing ' . count($submissions) . ' assignment submissions ...');
|
|
|
2866 |
|
|
|
2867 |
// Preload courses we are going to need those.
|
|
|
2868 |
$courseids = array();
|
|
|
2869 |
foreach ($submissions as $submission) {
|
|
|
2870 |
$courseids[] = $submission->course;
|
|
|
2871 |
}
|
|
|
2872 |
|
|
|
2873 |
// Filter out duplicates.
|
|
|
2874 |
$courseids = array_unique($courseids);
|
|
|
2875 |
$ctxselect = context_helper::get_preload_record_columns_sql('ctx');
|
|
|
2876 |
list($courseidsql, $params) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED);
|
|
|
2877 |
$sql = 'SELECT c.*, ' . $ctxselect .
|
1254 |
ariadna |
2878 |
' FROM {course} c
|
1 |
efrain |
2879 |
LEFT JOIN {context} ctx ON ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel
|
|
|
2880 |
WHERE c.id ' . $courseidsql;
|
|
|
2881 |
|
|
|
2882 |
$params['contextlevel'] = CONTEXT_COURSE;
|
|
|
2883 |
$courses = $DB->get_records_sql($sql, $params);
|
|
|
2884 |
|
|
|
2885 |
// Clean up... this could go on for a while.
|
|
|
2886 |
unset($courseids);
|
|
|
2887 |
unset($ctxselect);
|
|
|
2888 |
unset($courseidsql);
|
|
|
2889 |
unset($params);
|
|
|
2890 |
|
|
|
2891 |
// Message students about new feedback.
|
|
|
2892 |
foreach ($submissions as $submission) {
|
|
|
2893 |
|
|
|
2894 |
mtrace("Processing assignment submission $submission->id ...");
|
|
|
2895 |
|
|
|
2896 |
// Do not cache user lookups - could be too many.
|
1254 |
ariadna |
2897 |
if (!$user = $DB->get_record('user', array('id' => $submission->userid))) {
|
1 |
efrain |
2898 |
mtrace('Could not find user ' . $submission->userid);
|
|
|
2899 |
continue;
|
|
|
2900 |
}
|
|
|
2901 |
|
|
|
2902 |
// Use a cache to prevent the same DB queries happening over and over.
|
|
|
2903 |
if (!array_key_exists($submission->course, $courses)) {
|
|
|
2904 |
mtrace('Could not find course ' . $submission->course);
|
|
|
2905 |
continue;
|
|
|
2906 |
}
|
|
|
2907 |
$course = $courses[$submission->course];
|
|
|
2908 |
if (isset($course->ctxid)) {
|
|
|
2909 |
// Context has not yet been preloaded. Do so now.
|
|
|
2910 |
context_helper::preload_from_record($course);
|
|
|
2911 |
}
|
|
|
2912 |
|
|
|
2913 |
// Override the language and timezone of the "current" user, so that
|
|
|
2914 |
// mail is customised for the receiver.
|
|
|
2915 |
\core\cron::setup_user($user, $course);
|
|
|
2916 |
|
|
|
2917 |
// Context lookups are already cached.
|
|
|
2918 |
$coursecontext = context_course::instance($course->id);
|
|
|
2919 |
if (!is_enrolled($coursecontext, $user->id)) {
|
1254 |
ariadna |
2920 |
$courseshortname = format_string(
|
|
|
2921 |
$course->shortname,
|
|
|
2922 |
true,
|
|
|
2923 |
array('context' => $coursecontext)
|
|
|
2924 |
);
|
1 |
efrain |
2925 |
mtrace(fullname($user) . ' not an active participant in ' . $courseshortname);
|
|
|
2926 |
continue;
|
|
|
2927 |
}
|
|
|
2928 |
|
1254 |
ariadna |
2929 |
if (!$grader = $DB->get_record('user', array('id' => $submission->grader))) {
|
1 |
efrain |
2930 |
mtrace('Could not find grader ' . $submission->grader);
|
|
|
2931 |
continue;
|
|
|
2932 |
}
|
|
|
2933 |
|
|
|
2934 |
$modinfo = get_fast_modinfo($course, $user->id);
|
|
|
2935 |
$cm = $modinfo->get_cm($submission->cmid);
|
|
|
2936 |
// Context lookups are already cached.
|
|
|
2937 |
$contextmodule = context_module::instance($cm->id);
|
|
|
2938 |
|
|
|
2939 |
if (!$cm->uservisible) {
|
|
|
2940 |
// Hold mail notification for assignments the user cannot access until later.
|
|
|
2941 |
continue;
|
|
|
2942 |
}
|
|
|
2943 |
|
|
|
2944 |
// Notify the student. Default to the non-anon version.
|
|
|
2945 |
$messagetype = 'feedbackavailable';
|
|
|
2946 |
// Message type needs 'anon' if "hidden grading" is enabled and the student
|
|
|
2947 |
// doesn't have permission to see the grader.
|
|
|
2948 |
if ($submission->hidegrader && !has_capability('mod/assign:showhiddengrader', $contextmodule, $user)) {
|
|
|
2949 |
$messagetype = 'feedbackavailableanon';
|
|
|
2950 |
// There's no point in having an "anonymous grader" if the notification email
|
|
|
2951 |
// comes from them. Send the email from the noreply user instead.
|
|
|
2952 |
$grader = core_user::get_noreply_user();
|
|
|
2953 |
}
|
|
|
2954 |
|
|
|
2955 |
$eventtype = 'assign_notification';
|
|
|
2956 |
$updatetime = $submission->lastmodified;
|
|
|
2957 |
$modulename = get_string('modulename', 'assign');
|
|
|
2958 |
|
|
|
2959 |
$uniqueid = 0;
|
|
|
2960 |
if ($submission->blindmarking && !$submission->revealidentities) {
|
|
|
2961 |
if (empty($submission->recordid)) {
|
|
|
2962 |
$uniqueid = self::get_uniqueid_for_user_static($submission->assignment, $grader->id);
|
|
|
2963 |
} else {
|
|
|
2964 |
$uniqueid = $submission->recordid;
|
|
|
2965 |
}
|
|
|
2966 |
}
|
|
|
2967 |
$showusers = $submission->blindmarking && !$submission->revealidentities;
|
1254 |
ariadna |
2968 |
self::send_assignment_notification(
|
|
|
2969 |
$grader,
|
|
|
2970 |
$user,
|
|
|
2971 |
$messagetype,
|
|
|
2972 |
$eventtype,
|
|
|
2973 |
$updatetime,
|
|
|
2974 |
$cm,
|
|
|
2975 |
$contextmodule,
|
|
|
2976 |
$course,
|
|
|
2977 |
$modulename,
|
|
|
2978 |
$submission->name,
|
|
|
2979 |
$showusers,
|
|
|
2980 |
$uniqueid
|
|
|
2981 |
);
|
1 |
efrain |
2982 |
|
1254 |
ariadna |
2983 |
$flags = $DB->get_record('assign_user_flags', array('userid' => $user->id, 'assignment' => $submission->assignment));
|
1 |
efrain |
2984 |
if ($flags) {
|
|
|
2985 |
$flags->mailed = 1;
|
|
|
2986 |
$DB->update_record('assign_user_flags', $flags);
|
|
|
2987 |
} else {
|
|
|
2988 |
$flags = new stdClass();
|
|
|
2989 |
$flags->userid = $user->id;
|
|
|
2990 |
$flags->assignment = $submission->assignment;
|
|
|
2991 |
$flags->mailed = 1;
|
|
|
2992 |
$DB->insert_record('assign_user_flags', $flags);
|
|
|
2993 |
}
|
|
|
2994 |
|
|
|
2995 |
mtrace('Done');
|
|
|
2996 |
}
|
|
|
2997 |
mtrace('Done processing ' . count($submissions) . ' assignment submissions');
|
|
|
2998 |
|
|
|
2999 |
\core\cron::setup_user();
|
|
|
3000 |
|
|
|
3001 |
// Free up memory just to be sure.
|
|
|
3002 |
unset($courses);
|
|
|
3003 |
}
|
|
|
3004 |
|
|
|
3005 |
// Update calendar events to provide a description.
|
|
|
3006 |
$sql = 'SELECT id
|
|
|
3007 |
FROM {assign}
|
|
|
3008 |
WHERE
|
|
|
3009 |
allowsubmissionsfromdate >= :lastruntime AND
|
|
|
3010 |
allowsubmissionsfromdate <= :timenow AND
|
|
|
3011 |
alwaysshowdescription = 0';
|
|
|
3012 |
$params = array('lastruntime' => $lastruntime, 'timenow' => $timenow);
|
|
|
3013 |
$newlyavailable = $DB->get_records_sql($sql, $params);
|
|
|
3014 |
foreach ($newlyavailable as $record) {
|
|
|
3015 |
$cm = get_coursemodule_from_instance('assign', $record->id, 0, false, MUST_EXIST);
|
|
|
3016 |
$context = context_module::instance($cm->id);
|
|
|
3017 |
|
|
|
3018 |
$assignment = new assign($context, null, null);
|
|
|
3019 |
$assignment->update_calendar($cm->id);
|
|
|
3020 |
}
|
|
|
3021 |
|
|
|
3022 |
return true;
|
|
|
3023 |
}
|
|
|
3024 |
|
|
|
3025 |
/**
|
|
|
3026 |
* Mark in the database that this grade record should have an update notification sent by cron.
|
|
|
3027 |
*
|
|
|
3028 |
* @param stdClass $grade a grade record keyed on id
|
|
|
3029 |
* @param bool $mailedoverride when true, flag notification to be sent again.
|
|
|
3030 |
* @return bool true for success
|
|
|
3031 |
*/
|
1254 |
ariadna |
3032 |
public function notify_grade_modified($grade, $mailedoverride = false)
|
|
|
3033 |
{
|
1 |
efrain |
3034 |
global $DB;
|
|
|
3035 |
|
|
|
3036 |
$flags = $this->get_user_flags($grade->userid, true);
|
|
|
3037 |
if ($flags->mailed != 1 || $mailedoverride) {
|
|
|
3038 |
$flags->mailed = 0;
|
|
|
3039 |
}
|
|
|
3040 |
|
|
|
3041 |
return $this->update_user_flags($flags);
|
|
|
3042 |
}
|
|
|
3043 |
|
|
|
3044 |
/**
|
|
|
3045 |
* Update user flags for this user in this assignment.
|
|
|
3046 |
*
|
|
|
3047 |
* @param stdClass $flags a flags record keyed on id
|
|
|
3048 |
* @return bool true for success
|
|
|
3049 |
*/
|
1254 |
ariadna |
3050 |
public function update_user_flags($flags)
|
|
|
3051 |
{
|
1 |
efrain |
3052 |
global $DB;
|
|
|
3053 |
if ($flags->userid <= 0 || $flags->assignment <= 0 || $flags->id <= 0) {
|
|
|
3054 |
return false;
|
|
|
3055 |
}
|
|
|
3056 |
|
|
|
3057 |
$result = $DB->update_record('assign_user_flags', $flags);
|
|
|
3058 |
return $result;
|
|
|
3059 |
}
|
|
|
3060 |
|
|
|
3061 |
/**
|
|
|
3062 |
* Update a grade in the grade table for the assignment and in the gradebook.
|
|
|
3063 |
*
|
|
|
3064 |
* @param stdClass $grade a grade record keyed on id
|
|
|
3065 |
* @param bool $reopenattempt If the attempt reopen method is manual, allow another attempt at this assignment.
|
|
|
3066 |
* @return bool true for success
|
|
|
3067 |
*/
|
1254 |
ariadna |
3068 |
public function update_grade($grade, $reopenattempt = false)
|
|
|
3069 |
{
|
1 |
efrain |
3070 |
global $DB;
|
|
|
3071 |
|
|
|
3072 |
$grade->timemodified = time();
|
|
|
3073 |
|
|
|
3074 |
if (!empty($grade->workflowstate)) {
|
|
|
3075 |
$validstates = $this->get_marking_workflow_states_for_current_user();
|
|
|
3076 |
if (!array_key_exists($grade->workflowstate, $validstates)) {
|
|
|
3077 |
return false;
|
|
|
3078 |
}
|
|
|
3079 |
}
|
|
|
3080 |
|
|
|
3081 |
if ($grade->grade && $grade->grade != -1) {
|
|
|
3082 |
if ($this->get_instance()->grade > 0) {
|
|
|
3083 |
if (!is_numeric($grade->grade)) {
|
|
|
3084 |
return false;
|
|
|
3085 |
} else if ($grade->grade > $this->get_instance()->grade) {
|
|
|
3086 |
return false;
|
|
|
3087 |
} else if ($grade->grade < 0) {
|
|
|
3088 |
return false;
|
|
|
3089 |
}
|
|
|
3090 |
} else {
|
|
|
3091 |
// This is a scale.
|
1254 |
ariadna |
3092 |
if ($scale = $DB->get_record('scale', array('id' => - ($this->get_instance()->grade)))) {
|
1 |
efrain |
3093 |
$scaleoptions = make_menu_from_list($scale->scale);
|
|
|
3094 |
if (!array_key_exists((int) $grade->grade, $scaleoptions)) {
|
|
|
3095 |
return false;
|
|
|
3096 |
}
|
|
|
3097 |
}
|
|
|
3098 |
}
|
|
|
3099 |
}
|
|
|
3100 |
|
|
|
3101 |
if (empty($grade->attemptnumber)) {
|
|
|
3102 |
// Set it to the default.
|
|
|
3103 |
$grade->attemptnumber = 0;
|
|
|
3104 |
}
|
|
|
3105 |
$DB->update_record('assign_grades', $grade);
|
|
|
3106 |
|
|
|
3107 |
$submission = null;
|
|
|
3108 |
if ($this->get_instance()->teamsubmission) {
|
|
|
3109 |
if (isset($this->mostrecentteamsubmission)) {
|
|
|
3110 |
$submission = $this->mostrecentteamsubmission;
|
|
|
3111 |
} else {
|
|
|
3112 |
$submission = $this->get_group_submission($grade->userid, 0, false);
|
|
|
3113 |
}
|
|
|
3114 |
} else {
|
|
|
3115 |
$submission = $this->get_user_submission($grade->userid, false);
|
|
|
3116 |
}
|
|
|
3117 |
|
|
|
3118 |
// Only push to gradebook if the update is for the most recent attempt.
|
|
|
3119 |
if ($submission && $submission->attemptnumber != $grade->attemptnumber) {
|
|
|
3120 |
return true;
|
|
|
3121 |
}
|
|
|
3122 |
|
|
|
3123 |
if ($this->gradebook_item_update(null, $grade)) {
|
|
|
3124 |
\mod_assign\event\submission_graded::create_from_grade($this, $grade)->trigger();
|
|
|
3125 |
}
|
|
|
3126 |
|
|
|
3127 |
// If the conditions are met, allow another attempt.
|
|
|
3128 |
if ($submission) {
|
1254 |
ariadna |
3129 |
$isreopened = $this->reopen_submission_if_required(
|
|
|
3130 |
$grade->userid,
|
|
|
3131 |
$submission,
|
|
|
3132 |
$reopenattempt
|
|
|
3133 |
);
|
1 |
efrain |
3134 |
if ($isreopened) {
|
|
|
3135 |
$completion = new completion_info($this->get_course());
|
1254 |
ariadna |
3136 |
if (
|
|
|
3137 |
$completion->is_enabled($this->get_course_module()) &&
|
|
|
3138 |
$this->get_instance()->completionsubmit
|
|
|
3139 |
) {
|
1 |
efrain |
3140 |
$completion->update_state($this->get_course_module(), COMPLETION_INCOMPLETE, $grade->userid);
|
|
|
3141 |
}
|
|
|
3142 |
}
|
|
|
3143 |
}
|
|
|
3144 |
|
|
|
3145 |
return true;
|
|
|
3146 |
}
|
|
|
3147 |
|
|
|
3148 |
/**
|
|
|
3149 |
* View the grant extension date page.
|
|
|
3150 |
*
|
|
|
3151 |
* Uses url parameters 'userid'
|
|
|
3152 |
* or from parameter 'selectedusers'
|
|
|
3153 |
*
|
|
|
3154 |
* @param moodleform $mform - Used for validation of the submitted data
|
|
|
3155 |
* @return string
|
|
|
3156 |
*/
|
1254 |
ariadna |
3157 |
protected function view_grant_extension($mform)
|
|
|
3158 |
{
|
1 |
efrain |
3159 |
global $CFG;
|
|
|
3160 |
require_once($CFG->dirroot . '/mod/assign/extensionform.php');
|
|
|
3161 |
|
|
|
3162 |
$o = '';
|
|
|
3163 |
|
|
|
3164 |
$data = new stdClass();
|
|
|
3165 |
$data->id = $this->get_course_module()->id;
|
|
|
3166 |
|
|
|
3167 |
$formparams = array(
|
|
|
3168 |
'instance' => $this->get_instance(),
|
|
|
3169 |
'assign' => $this
|
|
|
3170 |
);
|
|
|
3171 |
|
|
|
3172 |
$users = optional_param('userid', 0, PARAM_INT);
|
|
|
3173 |
if (!$users) {
|
|
|
3174 |
$users = required_param('selectedusers', PARAM_SEQUENCE);
|
|
|
3175 |
}
|
|
|
3176 |
$userlist = explode(',', $users);
|
|
|
3177 |
|
|
|
3178 |
$keys = array('duedate', 'cutoffdate', 'allowsubmissionsfromdate');
|
|
|
3179 |
$maxoverride = array('allowsubmissionsfromdate' => 0, 'duedate' => 0, 'cutoffdate' => 0);
|
|
|
3180 |
foreach ($userlist as $userid) {
|
|
|
3181 |
// To validate extension date with users overrides.
|
|
|
3182 |
$override = $this->override_exists($userid);
|
|
|
3183 |
foreach ($keys as $key) {
|
|
|
3184 |
if ($override->{$key}) {
|
|
|
3185 |
if ($maxoverride[$key] < $override->{$key}) {
|
|
|
3186 |
$maxoverride[$key] = $override->{$key};
|
|
|
3187 |
}
|
|
|
3188 |
} else if ($maxoverride[$key] < $this->get_instance()->{$key}) {
|
|
|
3189 |
$maxoverride[$key] = $this->get_instance()->{$key};
|
|
|
3190 |
}
|
|
|
3191 |
}
|
|
|
3192 |
}
|
|
|
3193 |
foreach ($keys as $key) {
|
|
|
3194 |
if ($maxoverride[$key]) {
|
|
|
3195 |
$this->get_instance()->{$key} = $maxoverride[$key];
|
|
|
3196 |
}
|
|
|
3197 |
}
|
|
|
3198 |
|
|
|
3199 |
$formparams['userlist'] = $userlist;
|
|
|
3200 |
|
|
|
3201 |
$data->selectedusers = $users;
|
|
|
3202 |
$data->userid = 0;
|
|
|
3203 |
|
|
|
3204 |
if (empty($mform)) {
|
|
|
3205 |
$mform = new mod_assign_extension_form(null, $formparams);
|
|
|
3206 |
}
|
|
|
3207 |
$mform->set_data($data);
|
1254 |
ariadna |
3208 |
$header = new assign_header(
|
|
|
3209 |
$this->get_instance(),
|
|
|
3210 |
$this->get_context(),
|
|
|
3211 |
$this->show_intro(),
|
|
|
3212 |
$this->get_course_module()->id,
|
|
|
3213 |
get_string('grantextension', 'assign')
|
|
|
3214 |
);
|
1 |
efrain |
3215 |
$o .= $this->get_renderer()->render($header);
|
|
|
3216 |
$o .= $this->get_renderer()->render(new assign_form('extensionform', $mform));
|
|
|
3217 |
$o .= $this->view_footer();
|
|
|
3218 |
return $o;
|
|
|
3219 |
}
|
|
|
3220 |
|
|
|
3221 |
/**
|
|
|
3222 |
* Get a list of the users in the same group as this user.
|
|
|
3223 |
*
|
|
|
3224 |
* @param int $groupid The id of the group whose members we want or 0 for the default group
|
|
|
3225 |
* @param bool $onlyids Whether to retrieve only the user id's
|
|
|
3226 |
* @param bool $excludesuspended Whether to exclude suspended users
|
|
|
3227 |
* @return array The users (possibly id's only)
|
|
|
3228 |
*/
|
1254 |
ariadna |
3229 |
public function get_submission_group_members($groupid, $onlyids, $excludesuspended = false)
|
|
|
3230 |
{
|
1 |
efrain |
3231 |
$members = array();
|
|
|
3232 |
if ($groupid != 0) {
|
|
|
3233 |
$allusers = $this->list_participants($groupid, $onlyids);
|
|
|
3234 |
foreach ($allusers as $user) {
|
|
|
3235 |
if ($this->get_submission_group($user->id)) {
|
|
|
3236 |
$members[] = $user;
|
|
|
3237 |
}
|
|
|
3238 |
}
|
|
|
3239 |
} else {
|
|
|
3240 |
$allusers = $this->list_participants(null, $onlyids);
|
|
|
3241 |
foreach ($allusers as $user) {
|
|
|
3242 |
if ($this->get_submission_group($user->id) == null) {
|
|
|
3243 |
$members[] = $user;
|
|
|
3244 |
}
|
|
|
3245 |
}
|
|
|
3246 |
}
|
|
|
3247 |
// Exclude suspended users, if user can't see them.
|
|
|
3248 |
if ($excludesuspended || !has_capability('moodle/course:viewsuspendedusers', $this->context)) {
|
|
|
3249 |
foreach ($members as $key => $member) {
|
|
|
3250 |
if (!$this->is_active_user($member->id)) {
|
|
|
3251 |
unset($members[$key]);
|
|
|
3252 |
}
|
|
|
3253 |
}
|
|
|
3254 |
}
|
|
|
3255 |
|
|
|
3256 |
return $members;
|
|
|
3257 |
}
|
|
|
3258 |
|
|
|
3259 |
/**
|
|
|
3260 |
* Get a list of the users in the same group as this user that have not submitted the assignment.
|
|
|
3261 |
*
|
|
|
3262 |
* @param int $groupid The id of the group whose members we want or 0 for the default group
|
|
|
3263 |
* @param bool $onlyids Whether to retrieve only the user id's
|
|
|
3264 |
* @return array The users (possibly id's only)
|
|
|
3265 |
*/
|
1254 |
ariadna |
3266 |
public function get_submission_group_members_who_have_not_submitted($groupid, $onlyids)
|
|
|
3267 |
{
|
1 |
efrain |
3268 |
$instance = $this->get_instance();
|
|
|
3269 |
if (!$instance->teamsubmission || !$instance->requireallteammemberssubmit) {
|
|
|
3270 |
return array();
|
|
|
3271 |
}
|
|
|
3272 |
$members = $this->get_submission_group_members($groupid, $onlyids);
|
|
|
3273 |
|
|
|
3274 |
foreach ($members as $id => $member) {
|
|
|
3275 |
$submission = $this->get_user_submission($member->id, false);
|
|
|
3276 |
if ($submission && $submission->status == ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
|
|
|
3277 |
unset($members[$id]);
|
|
|
3278 |
} else {
|
|
|
3279 |
if ($this->is_blind_marking()) {
|
|
|
3280 |
$members[$id]->alias = get_string('hiddenuser', 'assign') .
|
1254 |
ariadna |
3281 |
$this->get_uniqueid_for_user($id);
|
1 |
efrain |
3282 |
}
|
|
|
3283 |
}
|
|
|
3284 |
}
|
|
|
3285 |
return $members;
|
|
|
3286 |
}
|
|
|
3287 |
|
|
|
3288 |
/**
|
|
|
3289 |
* Load the group submission object for a particular user, optionally creating it if required.
|
|
|
3290 |
*
|
|
|
3291 |
* @param int $userid The id of the user whose submission we want
|
|
|
3292 |
* @param int $groupid The id of the group for this user - may be 0 in which
|
|
|
3293 |
* case it is determined from the userid.
|
|
|
3294 |
* @param bool $create If set to true a new submission object will be created in the database
|
|
|
3295 |
* with the status set to "new".
|
|
|
3296 |
* @param int $attemptnumber - -1 means the latest attempt
|
|
|
3297 |
* @return stdClass|false The submission
|
|
|
3298 |
*/
|
1254 |
ariadna |
3299 |
public function get_group_submission($userid, $groupid, $create, $attemptnumber = -1)
|
|
|
3300 |
{
|
1 |
efrain |
3301 |
global $DB;
|
|
|
3302 |
|
|
|
3303 |
if ($groupid == 0) {
|
|
|
3304 |
$group = $this->get_submission_group($userid);
|
|
|
3305 |
if ($group) {
|
|
|
3306 |
$groupid = $group->id;
|
|
|
3307 |
}
|
|
|
3308 |
}
|
|
|
3309 |
|
|
|
3310 |
// Now get the group submission.
|
1254 |
ariadna |
3311 |
$params = array('assignment' => $this->get_instance()->id, 'groupid' => $groupid, 'userid' => 0);
|
1 |
efrain |
3312 |
if ($attemptnumber >= 0) {
|
|
|
3313 |
$params['attemptnumber'] = $attemptnumber;
|
|
|
3314 |
}
|
|
|
3315 |
|
|
|
3316 |
// Only return the row with the highest attemptnumber.
|
|
|
3317 |
$submission = null;
|
|
|
3318 |
$submissions = $DB->get_records('assign_submission', $params, 'attemptnumber DESC', '*', 0, 1);
|
|
|
3319 |
if ($submissions) {
|
|
|
3320 |
$submission = reset($submissions);
|
|
|
3321 |
}
|
|
|
3322 |
|
|
|
3323 |
if ($submission) {
|
|
|
3324 |
if ($create) {
|
|
|
3325 |
$action = optional_param('action', '', PARAM_TEXT);
|
|
|
3326 |
if ($action == 'editsubmission') {
|
|
|
3327 |
if (empty($submission->timestarted) && $this->get_instance()->timelimit) {
|
|
|
3328 |
$submission->timestarted = time();
|
|
|
3329 |
$DB->update_record('assign_submission', $submission);
|
|
|
3330 |
}
|
|
|
3331 |
}
|
|
|
3332 |
}
|
|
|
3333 |
return $submission;
|
|
|
3334 |
}
|
|
|
3335 |
if ($create) {
|
|
|
3336 |
$submission = new stdClass();
|
|
|
3337 |
$submission->assignment = $this->get_instance()->id;
|
|
|
3338 |
$submission->userid = 0;
|
|
|
3339 |
$submission->groupid = $groupid;
|
|
|
3340 |
$submission->timecreated = time();
|
|
|
3341 |
$submission->timemodified = $submission->timecreated;
|
|
|
3342 |
if ($attemptnumber >= 0) {
|
|
|
3343 |
$submission->attemptnumber = $attemptnumber;
|
|
|
3344 |
} else {
|
|
|
3345 |
$submission->attemptnumber = 0;
|
|
|
3346 |
}
|
|
|
3347 |
// Work out if this is the latest submission.
|
|
|
3348 |
$submission->latest = 0;
|
1254 |
ariadna |
3349 |
$params = array('assignment' => $this->get_instance()->id, 'groupid' => $groupid, 'userid' => 0);
|
1 |
efrain |
3350 |
if ($attemptnumber == -1) {
|
|
|
3351 |
// This is a new submission so it must be the latest.
|
|
|
3352 |
$submission->latest = 1;
|
|
|
3353 |
} else {
|
|
|
3354 |
// We need to work this out.
|
|
|
3355 |
$result = $DB->get_records('assign_submission', $params, 'attemptnumber DESC', 'attemptnumber', 0, 1);
|
|
|
3356 |
if ($result) {
|
|
|
3357 |
$latestsubmission = reset($result);
|
|
|
3358 |
}
|
|
|
3359 |
if (!$latestsubmission || ($attemptnumber == $latestsubmission->attemptnumber)) {
|
|
|
3360 |
$submission->latest = 1;
|
|
|
3361 |
}
|
|
|
3362 |
}
|
|
|
3363 |
$transaction = $DB->start_delegated_transaction();
|
|
|
3364 |
if ($submission->latest) {
|
|
|
3365 |
// This is the case when we need to set latest to 0 for all the other attempts.
|
|
|
3366 |
$DB->set_field('assign_submission', 'latest', 0, $params);
|
|
|
3367 |
}
|
|
|
3368 |
$submission->status = ASSIGN_SUBMISSION_STATUS_NEW;
|
|
|
3369 |
$sid = $DB->insert_record('assign_submission', $submission);
|
|
|
3370 |
$transaction->allow_commit();
|
|
|
3371 |
return $DB->get_record('assign_submission', array('id' => $sid));
|
|
|
3372 |
}
|
|
|
3373 |
return false;
|
|
|
3374 |
}
|
|
|
3375 |
|
|
|
3376 |
/**
|
|
|
3377 |
* View a summary listing of all assignments in the current course.
|
|
|
3378 |
*
|
|
|
3379 |
* @return string
|
|
|
3380 |
*/
|
1254 |
ariadna |
3381 |
private function view_course_index()
|
|
|
3382 |
{
|
1 |
efrain |
3383 |
global $USER;
|
|
|
3384 |
|
|
|
3385 |
$o = '';
|
|
|
3386 |
|
|
|
3387 |
$course = $this->get_course();
|
|
|
3388 |
$strplural = get_string('modulenameplural', 'assign');
|
|
|
3389 |
|
|
|
3390 |
if (!$cms = get_coursemodules_in_course('assign', $course->id, 'm.duedate')) {
|
|
|
3391 |
$o .= $this->get_renderer()->notification(get_string('thereareno', 'moodle', $strplural));
|
|
|
3392 |
$o .= $this->get_renderer()->continue_button(new moodle_url('/course/view.php', array('id' => $course->id)));
|
|
|
3393 |
return $o;
|
|
|
3394 |
}
|
|
|
3395 |
|
|
|
3396 |
$strsectionname = '';
|
|
|
3397 |
$usesections = course_format_uses_sections($course->format);
|
|
|
3398 |
$modinfo = get_fast_modinfo($course);
|
|
|
3399 |
|
|
|
3400 |
if ($usesections) {
|
1254 |
ariadna |
3401 |
$strsectionname = get_string('sectionname', 'format_' . $course->format);
|
1 |
efrain |
3402 |
$sections = $modinfo->get_section_info_all();
|
|
|
3403 |
}
|
|
|
3404 |
$courseindexsummary = new assign_course_index_summary($usesections, $strsectionname);
|
|
|
3405 |
|
|
|
3406 |
$timenow = time();
|
|
|
3407 |
|
|
|
3408 |
$currentsection = '';
|
|
|
3409 |
foreach ($modinfo->instances['assign'] as $cm) {
|
|
|
3410 |
if (!$cm->uservisible) {
|
|
|
3411 |
continue;
|
|
|
3412 |
}
|
|
|
3413 |
|
|
|
3414 |
$timedue = $cms[$cm->id]->duedate;
|
|
|
3415 |
|
|
|
3416 |
$sectionname = '';
|
|
|
3417 |
if ($usesections && $cm->sectionnum) {
|
|
|
3418 |
$sectionname = get_section_name($course, $sections[$cm->sectionnum]);
|
|
|
3419 |
}
|
|
|
3420 |
|
|
|
3421 |
$submitted = '';
|
|
|
3422 |
$context = context_module::instance($cm->id);
|
|
|
3423 |
|
|
|
3424 |
$assignment = new assign($context, $cm, $course);
|
|
|
3425 |
|
|
|
3426 |
// Apply overrides.
|
|
|
3427 |
$assignment->update_effective_access($USER->id);
|
|
|
3428 |
$timedue = $assignment->get_instance()->duedate;
|
|
|
3429 |
|
1254 |
ariadna |
3430 |
if (
|
|
|
3431 |
has_capability('mod/assign:submit', $context) &&
|
|
|
3432 |
!has_capability('moodle/site:config', $context)
|
|
|
3433 |
) {
|
1 |
efrain |
3434 |
$cangrade = false;
|
|
|
3435 |
if ($assignment->get_instance()->teamsubmission) {
|
|
|
3436 |
$usersubmission = $assignment->get_group_submission($USER->id, 0, false);
|
|
|
3437 |
} else {
|
|
|
3438 |
$usersubmission = $assignment->get_user_submission($USER->id, false);
|
|
|
3439 |
}
|
|
|
3440 |
|
|
|
3441 |
if (!empty($usersubmission->status)) {
|
|
|
3442 |
$submitted = get_string('submissionstatus_' . $usersubmission->status, 'assign');
|
|
|
3443 |
} else {
|
|
|
3444 |
$submitted = get_string('submissionstatus_', 'assign');
|
|
|
3445 |
}
|
|
|
3446 |
|
|
|
3447 |
$gradinginfo = grade_get_grades($course->id, 'mod', 'assign', $cm->instance, $USER->id);
|
1254 |
ariadna |
3448 |
if (
|
|
|
3449 |
isset($gradinginfo->items[0]->grades[$USER->id]) &&
|
|
|
3450 |
!$gradinginfo->items[0]->grades[$USER->id]->hidden
|
|
|
3451 |
) {
|
1 |
efrain |
3452 |
$grade = $gradinginfo->items[0]->grades[$USER->id]->str_grade;
|
|
|
3453 |
} else {
|
|
|
3454 |
$grade = '-';
|
|
|
3455 |
}
|
|
|
3456 |
} else if (has_capability('mod/assign:grade', $context)) {
|
|
|
3457 |
$submitted = $assignment->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED);
|
|
|
3458 |
$grade = $assignment->count_submissions_need_grading();
|
|
|
3459 |
$cangrade = true;
|
|
|
3460 |
}
|
|
|
3461 |
|
1254 |
ariadna |
3462 |
$courseindexsummary->add_assign_info(
|
|
|
3463 |
$cm->id,
|
|
|
3464 |
$cm->get_formatted_name(),
|
|
|
3465 |
$sectionname,
|
|
|
3466 |
$timedue,
|
|
|
3467 |
$submitted,
|
|
|
3468 |
$grade,
|
|
|
3469 |
$cangrade
|
|
|
3470 |
);
|
1 |
efrain |
3471 |
}
|
|
|
3472 |
|
|
|
3473 |
$o .= $this->get_renderer()->render($courseindexsummary);
|
|
|
3474 |
$o .= $this->view_footer();
|
|
|
3475 |
|
|
|
3476 |
return $o;
|
|
|
3477 |
}
|
|
|
3478 |
|
|
|
3479 |
/**
|
|
|
3480 |
* View a page rendered by a plugin.
|
|
|
3481 |
*
|
|
|
3482 |
* Uses url parameters 'pluginaction', 'pluginsubtype', 'plugin', and 'id'.
|
|
|
3483 |
*
|
|
|
3484 |
* @return string
|
|
|
3485 |
*/
|
1254 |
ariadna |
3486 |
protected function view_plugin_page()
|
|
|
3487 |
{
|
1 |
efrain |
3488 |
global $USER;
|
|
|
3489 |
|
|
|
3490 |
$o = '';
|
|
|
3491 |
|
|
|
3492 |
$pluginsubtype = required_param('pluginsubtype', PARAM_ALPHA);
|
|
|
3493 |
$plugintype = required_param('plugin', PARAM_PLUGIN);
|
|
|
3494 |
$pluginaction = required_param('pluginaction', PARAM_ALPHA);
|
|
|
3495 |
|
|
|
3496 |
$plugin = $this->get_plugin_by_type($pluginsubtype, $plugintype);
|
|
|
3497 |
if (!$plugin) {
|
|
|
3498 |
throw new \moodle_exception('invalidformdata', '');
|
|
|
3499 |
return;
|
|
|
3500 |
}
|
|
|
3501 |
|
|
|
3502 |
$o .= $plugin->view_page($pluginaction);
|
|
|
3503 |
|
|
|
3504 |
return $o;
|
|
|
3505 |
}
|
|
|
3506 |
|
|
|
3507 |
|
|
|
3508 |
/**
|
|
|
3509 |
* This is used for team assignments to get the group for the specified user.
|
|
|
3510 |
* If the user is a member of multiple or no groups this will return false
|
|
|
3511 |
*
|
|
|
3512 |
* @param int $userid The id of the user whose submission we want
|
|
|
3513 |
* @return mixed The group or false
|
|
|
3514 |
*/
|
1254 |
ariadna |
3515 |
public function get_submission_group($userid)
|
|
|
3516 |
{
|
1 |
efrain |
3517 |
|
|
|
3518 |
if (isset($this->usersubmissiongroups[$userid])) {
|
|
|
3519 |
return $this->usersubmissiongroups[$userid];
|
|
|
3520 |
}
|
|
|
3521 |
|
|
|
3522 |
$groups = $this->get_all_groups($userid);
|
|
|
3523 |
if (count($groups) != 1) {
|
|
|
3524 |
$return = false;
|
|
|
3525 |
} else {
|
|
|
3526 |
$return = array_pop($groups);
|
|
|
3527 |
}
|
|
|
3528 |
|
|
|
3529 |
// Cache the user submission group.
|
|
|
3530 |
$this->usersubmissiongroups[$userid] = $return;
|
|
|
3531 |
|
|
|
3532 |
return $return;
|
|
|
3533 |
}
|
|
|
3534 |
|
|
|
3535 |
/**
|
|
|
3536 |
* Gets all groups the user is a member of.
|
|
|
3537 |
*
|
|
|
3538 |
* @param int $userid Teh id of the user who's groups we are checking
|
|
|
3539 |
* @return array The group objects
|
|
|
3540 |
*/
|
1254 |
ariadna |
3541 |
public function get_all_groups($userid)
|
|
|
3542 |
{
|
1 |
efrain |
3543 |
if (isset($this->usergroups[$userid])) {
|
|
|
3544 |
return $this->usergroups[$userid];
|
|
|
3545 |
}
|
|
|
3546 |
|
|
|
3547 |
$grouping = $this->get_instance()->teamsubmissiongroupingid;
|
|
|
3548 |
$return = groups_get_all_groups($this->get_course()->id, $userid, $grouping, 'g.*', false, true);
|
|
|
3549 |
|
|
|
3550 |
$this->usergroups[$userid] = $return;
|
|
|
3551 |
|
|
|
3552 |
return $return;
|
|
|
3553 |
}
|
|
|
3554 |
|
|
|
3555 |
|
|
|
3556 |
/**
|
|
|
3557 |
* Display the submission that is used by a plugin.
|
|
|
3558 |
*
|
|
|
3559 |
* Uses url parameters 'sid', 'gid' and 'plugin'.
|
|
|
3560 |
*
|
|
|
3561 |
* @param string $pluginsubtype
|
|
|
3562 |
* @return string
|
|
|
3563 |
*/
|
1254 |
ariadna |
3564 |
protected function view_plugin_content($pluginsubtype)
|
|
|
3565 |
{
|
1 |
efrain |
3566 |
$o = '';
|
|
|
3567 |
|
|
|
3568 |
$submissionid = optional_param('sid', 0, PARAM_INT);
|
|
|
3569 |
$gradeid = optional_param('gid', 0, PARAM_INT);
|
|
|
3570 |
$plugintype = required_param('plugin', PARAM_PLUGIN);
|
|
|
3571 |
$item = null;
|
|
|
3572 |
if ($pluginsubtype == 'assignsubmission') {
|
|
|
3573 |
$plugin = $this->get_submission_plugin_by_type($plugintype);
|
|
|
3574 |
if ($submissionid <= 0) {
|
|
|
3575 |
throw new coding_exception('Submission id should not be 0');
|
|
|
3576 |
}
|
|
|
3577 |
$item = $this->get_submission($submissionid);
|
|
|
3578 |
|
|
|
3579 |
// Check permissions.
|
|
|
3580 |
if (empty($item->userid)) {
|
|
|
3581 |
// Group submission.
|
|
|
3582 |
$this->require_view_group_submission($item->groupid);
|
|
|
3583 |
} else {
|
|
|
3584 |
$this->require_view_submission($item->userid);
|
|
|
3585 |
}
|
1254 |
ariadna |
3586 |
$o .= $this->get_renderer()->render(new assign_header(
|
|
|
3587 |
$this->get_instance(),
|
|
|
3588 |
$this->get_context(),
|
|
|
3589 |
$this->show_intro(),
|
|
|
3590 |
$this->get_course_module()->id,
|
|
|
3591 |
$plugin->get_name()
|
|
|
3592 |
));
|
|
|
3593 |
$o .= $this->get_renderer()->render(new assign_submission_plugin_submission(
|
|
|
3594 |
$plugin,
|
|
|
3595 |
$item,
|
|
|
3596 |
assign_submission_plugin_submission::FULL,
|
|
|
3597 |
$this->get_course_module()->id,
|
|
|
3598 |
$this->get_return_action(),
|
|
|
3599 |
$this->get_return_params()
|
|
|
3600 |
));
|
1 |
efrain |
3601 |
|
|
|
3602 |
// Trigger event for viewing a submission.
|
|
|
3603 |
\mod_assign\event\submission_viewed::create_from_submission($this, $item)->trigger();
|
|
|
3604 |
} else {
|
|
|
3605 |
$plugin = $this->get_feedback_plugin_by_type($plugintype);
|
|
|
3606 |
if ($gradeid <= 0) {
|
|
|
3607 |
throw new coding_exception('Grade id should not be 0');
|
|
|
3608 |
}
|
|
|
3609 |
$item = $this->get_grade($gradeid);
|
|
|
3610 |
// Check permissions.
|
|
|
3611 |
$this->require_view_submission($item->userid);
|
1254 |
ariadna |
3612 |
$o .= $this->get_renderer()->render(new assign_header(
|
|
|
3613 |
$this->get_instance(),
|
|
|
3614 |
$this->get_context(),
|
|
|
3615 |
$this->show_intro(),
|
|
|
3616 |
$this->get_course_module()->id,
|
|
|
3617 |
$plugin->get_name()
|
|
|
3618 |
));
|
|
|
3619 |
$o .= $this->get_renderer()->render(new assign_feedback_plugin_feedback(
|
|
|
3620 |
$plugin,
|
|
|
3621 |
$item,
|
|
|
3622 |
assign_feedback_plugin_feedback::FULL,
|
|
|
3623 |
$this->get_course_module()->id,
|
|
|
3624 |
$this->get_return_action(),
|
|
|
3625 |
$this->get_return_params()
|
|
|
3626 |
));
|
1 |
efrain |
3627 |
|
|
|
3628 |
// Trigger event for viewing feedback.
|
|
|
3629 |
\mod_assign\event\feedback_viewed::create_from_grade($this, $item)->trigger();
|
|
|
3630 |
}
|
|
|
3631 |
|
|
|
3632 |
$o .= $this->view_return_links();
|
|
|
3633 |
|
|
|
3634 |
$o .= $this->view_footer();
|
|
|
3635 |
|
|
|
3636 |
return $o;
|
|
|
3637 |
}
|
|
|
3638 |
|
|
|
3639 |
/**
|
|
|
3640 |
* Rewrite plugin file urls so they resolve correctly in an exported zip.
|
|
|
3641 |
*
|
|
|
3642 |
* @param string $text - The replacement text
|
|
|
3643 |
* @param stdClass $user - The user record
|
|
|
3644 |
* @param assign_plugin $plugin - The assignment plugin
|
|
|
3645 |
*/
|
1254 |
ariadna |
3646 |
public function download_rewrite_pluginfile_urls($text, $user, $plugin)
|
|
|
3647 |
{
|
1 |
efrain |
3648 |
// The groupname prefix for the urls doesn't depend on the group mode of the assignment instance.
|
|
|
3649 |
// Rather, it should be determined by checking the group submission settings of the instance,
|
|
|
3650 |
// which is what download_submission() does when generating the file name prefixes.
|
|
|
3651 |
$groupname = '';
|
|
|
3652 |
if ($this->get_instance()->teamsubmission) {
|
|
|
3653 |
$submissiongroup = $this->get_submission_group($user->id);
|
|
|
3654 |
if ($submissiongroup) {
|
|
|
3655 |
$groupname = $submissiongroup->name . '-';
|
|
|
3656 |
} else {
|
|
|
3657 |
$groupname = get_string('defaultteam', 'assign') . '-';
|
|
|
3658 |
}
|
|
|
3659 |
}
|
|
|
3660 |
|
|
|
3661 |
if ($this->is_blind_marking()) {
|
|
|
3662 |
$prefix = $groupname . get_string('participant', 'assign');
|
|
|
3663 |
$prefix = str_replace('_', ' ', $prefix);
|
|
|
3664 |
$prefix = clean_filename($prefix . '_' . $this->get_uniqueid_for_user($user->id) . '_');
|
|
|
3665 |
} else {
|
|
|
3666 |
$prefix = $groupname . fullname($user);
|
|
|
3667 |
$prefix = str_replace('_', ' ', $prefix);
|
|
|
3668 |
$prefix = clean_filename($prefix . '_' . $this->get_uniqueid_for_user($user->id) . '_');
|
|
|
3669 |
}
|
|
|
3670 |
|
|
|
3671 |
// Only prefix files if downloadasfolders user preference is NOT set.
|
|
|
3672 |
if (!get_user_preferences('assign_downloadasfolders', 1)) {
|
|
|
3673 |
$subtype = $plugin->get_subtype();
|
|
|
3674 |
$type = $plugin->get_type();
|
|
|
3675 |
$prefix = $prefix . $subtype . '_' . $type . '_';
|
|
|
3676 |
} else {
|
|
|
3677 |
$prefix = "";
|
|
|
3678 |
}
|
|
|
3679 |
$result = str_replace('@@PLUGINFILE@@/', $prefix, $text);
|
|
|
3680 |
|
|
|
3681 |
return $result;
|
|
|
3682 |
}
|
|
|
3683 |
|
|
|
3684 |
/**
|
|
|
3685 |
* Render the content in editor that is often used by plugin.
|
|
|
3686 |
*
|
|
|
3687 |
* @param string $filearea
|
|
|
3688 |
* @param int $submissionid
|
|
|
3689 |
* @param string $plugintype
|
|
|
3690 |
* @param string $editor
|
|
|
3691 |
* @param string $component
|
|
|
3692 |
* @param bool $shortentext Whether to shorten the text content.
|
|
|
3693 |
* @return string
|
|
|
3694 |
*/
|
1254 |
ariadna |
3695 |
public function render_editor_content($filearea, $submissionid, $plugintype, $editor, $component, $shortentext = false)
|
|
|
3696 |
{
|
1 |
efrain |
3697 |
global $CFG;
|
|
|
3698 |
|
|
|
3699 |
$result = '';
|
|
|
3700 |
|
|
|
3701 |
$plugin = $this->get_submission_plugin_by_type($plugintype);
|
|
|
3702 |
|
|
|
3703 |
$text = $plugin->get_editor_text($editor, $submissionid);
|
|
|
3704 |
if ($shortentext) {
|
|
|
3705 |
$text = shorten_text($text, 140);
|
|
|
3706 |
}
|
|
|
3707 |
$format = $plugin->get_editor_format($editor, $submissionid);
|
|
|
3708 |
|
1254 |
ariadna |
3709 |
$finaltext = file_rewrite_pluginfile_urls(
|
|
|
3710 |
$text,
|
|
|
3711 |
'pluginfile.php',
|
|
|
3712 |
$this->get_context()->id,
|
|
|
3713 |
$component,
|
|
|
3714 |
$filearea,
|
|
|
3715 |
$submissionid
|
|
|
3716 |
);
|
1 |
efrain |
3717 |
$params = array('overflowdiv' => true, 'context' => $this->get_context());
|
|
|
3718 |
$result .= format_text($finaltext, $format, $params);
|
|
|
3719 |
|
|
|
3720 |
if ($CFG->enableportfolios && has_capability('mod/assign:exportownsubmission', $this->context)) {
|
|
|
3721 |
require_once($CFG->libdir . '/portfoliolib.php');
|
|
|
3722 |
|
|
|
3723 |
$button = new portfolio_add_button();
|
1254 |
ariadna |
3724 |
$portfolioparams = array(
|
|
|
3725 |
'cmid' => $this->get_course_module()->id,
|
|
|
3726 |
'sid' => $submissionid,
|
|
|
3727 |
'plugin' => $plugintype,
|
|
|
3728 |
'editor' => $editor,
|
|
|
3729 |
'area' => $filearea
|
|
|
3730 |
);
|
1 |
efrain |
3731 |
$button->set_callback_options('assign_portfolio_caller', $portfolioparams, 'mod_assign');
|
|
|
3732 |
$fs = get_file_storage();
|
|
|
3733 |
|
1254 |
ariadna |
3734 |
if ($files = $fs->get_area_files(
|
|
|
3735 |
$this->context->id,
|
|
|
3736 |
$component,
|
|
|
3737 |
$filearea,
|
|
|
3738 |
$submissionid,
|
|
|
3739 |
'timemodified',
|
|
|
3740 |
false
|
|
|
3741 |
)) {
|
1 |
efrain |
3742 |
$button->set_formats(PORTFOLIO_FORMAT_RICHHTML);
|
|
|
3743 |
} else {
|
|
|
3744 |
$button->set_formats(PORTFOLIO_FORMAT_PLAINHTML);
|
|
|
3745 |
}
|
|
|
3746 |
$result .= $button->to_html(PORTFOLIO_ADD_TEXT_LINK);
|
|
|
3747 |
}
|
|
|
3748 |
return $result;
|
|
|
3749 |
}
|
|
|
3750 |
|
|
|
3751 |
/**
|
|
|
3752 |
* Display a continue page after grading.
|
|
|
3753 |
*
|
|
|
3754 |
* @param string $message - The message to display.
|
|
|
3755 |
* @return string
|
|
|
3756 |
*/
|
1254 |
ariadna |
3757 |
protected function view_savegrading_result($message)
|
|
|
3758 |
{
|
1 |
efrain |
3759 |
$o = '';
|
1254 |
ariadna |
3760 |
$o .= $this->get_renderer()->render(new assign_header(
|
|
|
3761 |
$this->get_instance(),
|
|
|
3762 |
$this->get_context(),
|
|
|
3763 |
$this->show_intro(),
|
|
|
3764 |
$this->get_course_module()->id,
|
|
|
3765 |
get_string('savegradingresult', 'assign')
|
|
|
3766 |
));
|
|
|
3767 |
$gradingresult = new assign_gradingmessage(
|
|
|
3768 |
get_string('savegradingresult', 'assign'),
|
|
|
3769 |
$message,
|
|
|
3770 |
$this->get_course_module()->id
|
|
|
3771 |
);
|
1 |
efrain |
3772 |
$o .= $this->get_renderer()->render($gradingresult);
|
|
|
3773 |
$o .= $this->view_footer();
|
|
|
3774 |
return $o;
|
|
|
3775 |
}
|
|
|
3776 |
/**
|
|
|
3777 |
* Display a continue page after quickgrading.
|
|
|
3778 |
*
|
|
|
3779 |
* @param string $message - The message to display.
|
|
|
3780 |
* @return string
|
|
|
3781 |
*/
|
1254 |
ariadna |
3782 |
protected function view_quickgrading_result($message)
|
|
|
3783 |
{
|
1 |
efrain |
3784 |
$o = '';
|
1254 |
ariadna |
3785 |
$o .= $this->get_renderer()->render(new assign_header(
|
|
|
3786 |
$this->get_instance(),
|
|
|
3787 |
$this->get_context(),
|
|
|
3788 |
$this->show_intro(),
|
|
|
3789 |
$this->get_course_module()->id,
|
|
|
3790 |
get_string('quickgradingresult', 'assign')
|
|
|
3791 |
));
|
1 |
efrain |
3792 |
$gradingerror = in_array($message, $this->get_error_messages());
|
|
|
3793 |
$lastpage = optional_param('lastpage', null, PARAM_INT);
|
1254 |
ariadna |
3794 |
$gradingresult = new assign_gradingmessage(
|
|
|
3795 |
get_string('quickgradingresult', 'assign'),
|
|
|
3796 |
$message,
|
|
|
3797 |
$this->get_course_module()->id,
|
|
|
3798 |
$gradingerror,
|
|
|
3799 |
$lastpage
|
|
|
3800 |
);
|
1 |
efrain |
3801 |
$o .= $this->get_renderer()->render($gradingresult);
|
|
|
3802 |
$o .= $this->view_footer();
|
|
|
3803 |
return $o;
|
|
|
3804 |
}
|
|
|
3805 |
|
|
|
3806 |
/**
|
|
|
3807 |
* Display the page footer.
|
|
|
3808 |
*
|
|
|
3809 |
* @return string
|
|
|
3810 |
*/
|
1254 |
ariadna |
3811 |
protected function view_footer()
|
|
|
3812 |
{
|
1 |
efrain |
3813 |
// When viewing the footer during PHPUNIT tests a set_state error is thrown.
|
|
|
3814 |
if (!PHPUNIT_TEST) {
|
|
|
3815 |
return $this->get_renderer()->render_footer();
|
|
|
3816 |
}
|
|
|
3817 |
|
|
|
3818 |
return '';
|
|
|
3819 |
}
|
|
|
3820 |
|
|
|
3821 |
/**
|
|
|
3822 |
* Throw an error if the permissions to view this users' group submission are missing.
|
|
|
3823 |
*
|
|
|
3824 |
* @param int $groupid Group id.
|
|
|
3825 |
* @throws required_capability_exception
|
|
|
3826 |
*/
|
1254 |
ariadna |
3827 |
public function require_view_group_submission($groupid)
|
|
|
3828 |
{
|
1 |
efrain |
3829 |
if (!$this->can_view_group_submission($groupid)) {
|
|
|
3830 |
throw new required_capability_exception($this->context, 'mod/assign:viewgrades', 'nopermission', '');
|
|
|
3831 |
}
|
|
|
3832 |
}
|
|
|
3833 |
|
|
|
3834 |
/**
|
|
|
3835 |
* Throw an error if the permissions to view this users submission are missing.
|
|
|
3836 |
*
|
|
|
3837 |
* @throws required_capability_exception
|
|
|
3838 |
* @return none
|
|
|
3839 |
*/
|
1254 |
ariadna |
3840 |
public function require_view_submission($userid)
|
|
|
3841 |
{
|
1 |
efrain |
3842 |
if (!$this->can_view_submission($userid)) {
|
|
|
3843 |
throw new required_capability_exception($this->context, 'mod/assign:viewgrades', 'nopermission', '');
|
|
|
3844 |
}
|
|
|
3845 |
}
|
|
|
3846 |
|
|
|
3847 |
/**
|
|
|
3848 |
* Throw an error if the permissions to view grades in this assignment are missing.
|
|
|
3849 |
*
|
|
|
3850 |
* @throws required_capability_exception
|
|
|
3851 |
* @return none
|
|
|
3852 |
*/
|
1254 |
ariadna |
3853 |
public function require_view_grades()
|
|
|
3854 |
{
|
1 |
efrain |
3855 |
if (!$this->can_view_grades()) {
|
|
|
3856 |
throw new required_capability_exception($this->context, 'mod/assign:viewgrades', 'nopermission', '');
|
|
|
3857 |
}
|
|
|
3858 |
}
|
|
|
3859 |
|
|
|
3860 |
/**
|
|
|
3861 |
* Does this user have view grade or grade permission for this assignment?
|
|
|
3862 |
*
|
|
|
3863 |
* @param mixed $groupid int|null when is set to a value, use this group instead calculating it
|
|
|
3864 |
* @return bool
|
|
|
3865 |
*/
|
1254 |
ariadna |
3866 |
public function can_view_grades($groupid = null)
|
|
|
3867 |
{
|
1 |
efrain |
3868 |
// Permissions check.
|
|
|
3869 |
if (!has_any_capability(array('mod/assign:viewgrades', 'mod/assign:grade'), $this->context)) {
|
|
|
3870 |
return false;
|
|
|
3871 |
}
|
|
|
3872 |
// Checks for the edge case when user belongs to no groups and groupmode is sep.
|
|
|
3873 |
if ($this->get_course_module()->effectivegroupmode == SEPARATEGROUPS) {
|
|
|
3874 |
if ($groupid === null) {
|
|
|
3875 |
$groupid = groups_get_activity_allowed_groups($this->get_course_module());
|
|
|
3876 |
}
|
|
|
3877 |
$groupflag = has_capability('moodle/site:accessallgroups', $this->get_context());
|
|
|
3878 |
$groupflag = $groupflag || !empty($groupid);
|
|
|
3879 |
return (bool)$groupflag;
|
|
|
3880 |
}
|
|
|
3881 |
return true;
|
|
|
3882 |
}
|
|
|
3883 |
|
|
|
3884 |
/**
|
|
|
3885 |
* Does this user have grade permission for this assignment?
|
|
|
3886 |
*
|
|
|
3887 |
* @param int|stdClass $user The object or id of the user who will do the editing (default to current user).
|
|
|
3888 |
* @return bool
|
|
|
3889 |
*/
|
1254 |
ariadna |
3890 |
public function can_grade($user = null)
|
|
|
3891 |
{
|
1 |
efrain |
3892 |
// Permissions check.
|
|
|
3893 |
if (!has_capability('mod/assign:grade', $this->context, $user)) {
|
|
|
3894 |
return false;
|
|
|
3895 |
}
|
|
|
3896 |
|
|
|
3897 |
return true;
|
|
|
3898 |
}
|
|
|
3899 |
|
|
|
3900 |
/**
|
|
|
3901 |
* Download a zip file of all assignment submissions.
|
|
|
3902 |
*
|
|
|
3903 |
* @param array|null $userids Array of user ids to download assignment submissions in a zip file
|
|
|
3904 |
* @return string - If an error occurs, this will contain the error page.
|
|
|
3905 |
*/
|
1254 |
ariadna |
3906 |
protected function download_submissions($userids = null)
|
|
|
3907 |
{
|
1 |
efrain |
3908 |
$downloader = new downloader($this, $userids ?: null);
|
|
|
3909 |
if ($downloader->load_filelist()) {
|
|
|
3910 |
$downloader->download_zip();
|
|
|
3911 |
}
|
|
|
3912 |
// Show some notification if we have nothing to download.
|
|
|
3913 |
$cm = $this->get_course_module();
|
|
|
3914 |
$renderer = $this->get_renderer();
|
|
|
3915 |
$header = new assign_header(
|
|
|
3916 |
$this->get_instance(),
|
|
|
3917 |
$this->get_context(),
|
|
|
3918 |
'',
|
|
|
3919 |
$cm->id,
|
|
|
3920 |
get_string('downloadall', 'mod_assign')
|
|
|
3921 |
);
|
|
|
3922 |
$result = $renderer->render($header);
|
|
|
3923 |
$result .= $renderer->notification(get_string('nosubmission', 'mod_assign'));
|
|
|
3924 |
$url = new moodle_url('/mod/assign/view.php', ['id' => $cm->id, 'action' => 'grading']);
|
|
|
3925 |
$result .= $renderer->continue_button($url);
|
|
|
3926 |
$result .= $this->view_footer();
|
|
|
3927 |
return $result;
|
|
|
3928 |
}
|
|
|
3929 |
|
|
|
3930 |
/**
|
|
|
3931 |
* @deprecated since 2.7 - Use new events system instead.
|
|
|
3932 |
*/
|
1254 |
ariadna |
3933 |
public function add_to_log()
|
|
|
3934 |
{
|
1 |
efrain |
3935 |
throw new coding_exception(__FUNCTION__ . ' has been deprecated, please do not use it any more');
|
|
|
3936 |
}
|
|
|
3937 |
|
|
|
3938 |
/**
|
|
|
3939 |
* Lazy load the page renderer and expose the renderer to plugins.
|
|
|
3940 |
*
|
|
|
3941 |
* @return assign_renderer
|
|
|
3942 |
*/
|
1254 |
ariadna |
3943 |
public function get_renderer()
|
|
|
3944 |
{
|
1 |
efrain |
3945 |
global $PAGE;
|
|
|
3946 |
if ($this->output) {
|
|
|
3947 |
return $this->output;
|
|
|
3948 |
}
|
|
|
3949 |
$this->output = $PAGE->get_renderer('mod_assign', null, RENDERER_TARGET_GENERAL);
|
|
|
3950 |
return $this->output;
|
|
|
3951 |
}
|
|
|
3952 |
|
|
|
3953 |
/**
|
|
|
3954 |
* Load the submission object for a particular user, optionally creating it if required.
|
|
|
3955 |
*
|
|
|
3956 |
* For team assignments there are 2 submissions - the student submission and the team submission
|
|
|
3957 |
* All files are associated with the team submission but the status of the students contribution is
|
|
|
3958 |
* recorded separately.
|
|
|
3959 |
*
|
|
|
3960 |
* @param int $userid The id of the user whose submission we want or 0 in which case USER->id is used
|
|
|
3961 |
* @param bool $create If set to true a new submission object will be created in the database with the status set to "new".
|
|
|
3962 |
* @param int $attemptnumber - -1 means the latest attempt
|
|
|
3963 |
* @return stdClass|false The submission
|
|
|
3964 |
*/
|
1254 |
ariadna |
3965 |
public function get_user_submission($userid, $create, $attemptnumber = -1)
|
|
|
3966 |
{
|
1 |
efrain |
3967 |
global $DB, $USER;
|
|
|
3968 |
|
|
|
3969 |
if (!$userid) {
|
|
|
3970 |
$userid = $USER->id;
|
|
|
3971 |
}
|
|
|
3972 |
// If the userid is not null then use userid.
|
1254 |
ariadna |
3973 |
$params = array('assignment' => $this->get_instance()->id, 'userid' => $userid, 'groupid' => 0);
|
1 |
efrain |
3974 |
if ($attemptnumber >= 0) {
|
|
|
3975 |
$params['attemptnumber'] = $attemptnumber;
|
|
|
3976 |
}
|
|
|
3977 |
|
|
|
3978 |
// Only return the row with the highest attemptnumber.
|
|
|
3979 |
$submission = null;
|
|
|
3980 |
$submissions = $DB->get_records('assign_submission', $params, 'attemptnumber DESC', '*', 0, 1);
|
|
|
3981 |
if ($submissions) {
|
|
|
3982 |
$submission = reset($submissions);
|
|
|
3983 |
}
|
|
|
3984 |
|
|
|
3985 |
if ($submission) {
|
|
|
3986 |
if ($create) {
|
|
|
3987 |
$action = optional_param('action', '', PARAM_TEXT);
|
|
|
3988 |
if ($action == 'editsubmission') {
|
|
|
3989 |
if (empty($submission->timestarted) && $this->get_instance()->timelimit) {
|
|
|
3990 |
$submission->timestarted = time();
|
|
|
3991 |
$DB->update_record('assign_submission', $submission);
|
|
|
3992 |
}
|
|
|
3993 |
}
|
|
|
3994 |
}
|
|
|
3995 |
return $submission;
|
|
|
3996 |
}
|
|
|
3997 |
if ($create) {
|
|
|
3998 |
$submission = new stdClass();
|
|
|
3999 |
$submission->assignment = $this->get_instance()->id;
|
|
|
4000 |
$submission->userid = $userid;
|
|
|
4001 |
$submission->timecreated = time();
|
|
|
4002 |
$submission->timemodified = $submission->timecreated;
|
|
|
4003 |
$submission->status = ASSIGN_SUBMISSION_STATUS_NEW;
|
|
|
4004 |
if ($attemptnumber >= 0) {
|
|
|
4005 |
$submission->attemptnumber = $attemptnumber;
|
|
|
4006 |
} else {
|
|
|
4007 |
$submission->attemptnumber = 0;
|
|
|
4008 |
}
|
|
|
4009 |
// Work out if this is the latest submission.
|
|
|
4010 |
$submission->latest = 0;
|
1254 |
ariadna |
4011 |
$params = array('assignment' => $this->get_instance()->id, 'userid' => $userid, 'groupid' => 0);
|
1 |
efrain |
4012 |
if ($attemptnumber == -1) {
|
|
|
4013 |
// This is a new submission so it must be the latest.
|
|
|
4014 |
$submission->latest = 1;
|
|
|
4015 |
} else {
|
|
|
4016 |
// We need to work this out.
|
|
|
4017 |
$result = $DB->get_records('assign_submission', $params, 'attemptnumber DESC', 'attemptnumber', 0, 1);
|
|
|
4018 |
$latestsubmission = null;
|
|
|
4019 |
if ($result) {
|
|
|
4020 |
$latestsubmission = reset($result);
|
|
|
4021 |
}
|
|
|
4022 |
if (empty($latestsubmission) || ($attemptnumber > $latestsubmission->attemptnumber)) {
|
|
|
4023 |
$submission->latest = 1;
|
|
|
4024 |
}
|
|
|
4025 |
}
|
|
|
4026 |
$transaction = $DB->start_delegated_transaction();
|
|
|
4027 |
if ($submission->latest) {
|
|
|
4028 |
// This is the case when we need to set latest to 0 for all the other attempts.
|
|
|
4029 |
$DB->set_field('assign_submission', 'latest', 0, $params);
|
|
|
4030 |
}
|
|
|
4031 |
$sid = $DB->insert_record('assign_submission', $submission);
|
|
|
4032 |
$transaction->allow_commit();
|
|
|
4033 |
return $DB->get_record('assign_submission', array('id' => $sid));
|
|
|
4034 |
}
|
|
|
4035 |
return false;
|
|
|
4036 |
}
|
|
|
4037 |
|
|
|
4038 |
/**
|
|
|
4039 |
* Load the submission object from it's id.
|
|
|
4040 |
*
|
|
|
4041 |
* @param int $submissionid The id of the submission we want
|
|
|
4042 |
* @return stdClass The submission
|
|
|
4043 |
*/
|
1254 |
ariadna |
4044 |
protected function get_submission($submissionid)
|
|
|
4045 |
{
|
1 |
efrain |
4046 |
global $DB;
|
|
|
4047 |
|
1254 |
ariadna |
4048 |
$params = array('assignment' => $this->get_instance()->id, 'id' => $submissionid);
|
1 |
efrain |
4049 |
return $DB->get_record('assign_submission', $params, '*', MUST_EXIST);
|
|
|
4050 |
}
|
|
|
4051 |
|
|
|
4052 |
/**
|
|
|
4053 |
* This will retrieve a user flags object from the db optionally creating it if required.
|
|
|
4054 |
* The user flags was split from the user_grades table in 2.5.
|
|
|
4055 |
*
|
|
|
4056 |
* @param int $userid The user we are getting the flags for.
|
|
|
4057 |
* @param bool $create If true the flags record will be created if it does not exist
|
|
|
4058 |
* @return stdClass The flags record
|
|
|
4059 |
*/
|
1254 |
ariadna |
4060 |
public function get_user_flags($userid, $create)
|
|
|
4061 |
{
|
1 |
efrain |
4062 |
global $DB, $USER;
|
|
|
4063 |
|
|
|
4064 |
// If the userid is not null then use userid.
|
|
|
4065 |
if (!$userid) {
|
|
|
4066 |
$userid = $USER->id;
|
|
|
4067 |
}
|
|
|
4068 |
|
1254 |
ariadna |
4069 |
$params = array('assignment' => $this->get_instance()->id, 'userid' => $userid);
|
1 |
efrain |
4070 |
|
|
|
4071 |
$flags = $DB->get_record('assign_user_flags', $params);
|
|
|
4072 |
|
|
|
4073 |
if ($flags) {
|
|
|
4074 |
return $flags;
|
|
|
4075 |
}
|
|
|
4076 |
if ($create) {
|
|
|
4077 |
$flags = new stdClass();
|
|
|
4078 |
$flags->assignment = $this->get_instance()->id;
|
|
|
4079 |
$flags->userid = $userid;
|
|
|
4080 |
$flags->locked = 0;
|
|
|
4081 |
$flags->extensionduedate = 0;
|
|
|
4082 |
$flags->workflowstate = '';
|
|
|
4083 |
$flags->allocatedmarker = 0;
|
|
|
4084 |
|
|
|
4085 |
// The mailed flag can be one of 3 values: 0 is unsent, 1 is sent and 2 is do not send yet.
|
|
|
4086 |
// This is because students only want to be notified about certain types of update (grades and feedback).
|
|
|
4087 |
$flags->mailed = 2;
|
|
|
4088 |
|
|
|
4089 |
$fid = $DB->insert_record('assign_user_flags', $flags);
|
|
|
4090 |
$flags->id = $fid;
|
|
|
4091 |
return $flags;
|
|
|
4092 |
}
|
|
|
4093 |
return false;
|
|
|
4094 |
}
|
|
|
4095 |
|
|
|
4096 |
/**
|
|
|
4097 |
* This will retrieve a grade object from the db, optionally creating it if required.
|
|
|
4098 |
*
|
|
|
4099 |
* @param int $userid The user we are grading
|
|
|
4100 |
* @param bool $create If true the grade will be created if it does not exist
|
|
|
4101 |
* @param int $attemptnumber The attempt number to retrieve the grade for. -1 means the latest submission.
|
|
|
4102 |
* @return stdClass The grade record
|
|
|
4103 |
*/
|
1254 |
ariadna |
4104 |
public function get_user_grade($userid, $create, $attemptnumber = -1)
|
|
|
4105 |
{
|
1 |
efrain |
4106 |
global $DB, $USER;
|
|
|
4107 |
|
|
|
4108 |
// If the userid is not null then use userid.
|
|
|
4109 |
if (!$userid) {
|
|
|
4110 |
$userid = $USER->id;
|
|
|
4111 |
}
|
|
|
4112 |
$submission = null;
|
|
|
4113 |
|
1254 |
ariadna |
4114 |
$params = array('assignment' => $this->get_instance()->id, 'userid' => $userid);
|
1 |
efrain |
4115 |
if ($attemptnumber < 0 || $create) {
|
|
|
4116 |
// Make sure this grade matches the latest submission attempt.
|
|
|
4117 |
if ($this->get_instance()->teamsubmission) {
|
|
|
4118 |
$submission = $this->get_group_submission($userid, 0, true, $attemptnumber);
|
|
|
4119 |
} else {
|
|
|
4120 |
$submission = $this->get_user_submission($userid, true, $attemptnumber);
|
|
|
4121 |
}
|
|
|
4122 |
if ($submission) {
|
|
|
4123 |
$attemptnumber = $submission->attemptnumber;
|
|
|
4124 |
}
|
|
|
4125 |
}
|
|
|
4126 |
|
|
|
4127 |
if ($attemptnumber >= 0) {
|
|
|
4128 |
$params['attemptnumber'] = $attemptnumber;
|
|
|
4129 |
}
|
|
|
4130 |
|
|
|
4131 |
$grades = $DB->get_records('assign_grades', $params, 'attemptnumber DESC', '*', 0, 1);
|
|
|
4132 |
|
|
|
4133 |
if ($grades) {
|
|
|
4134 |
return reset($grades);
|
|
|
4135 |
}
|
|
|
4136 |
if ($create) {
|
|
|
4137 |
$grade = new stdClass();
|
|
|
4138 |
$grade->assignment = $this->get_instance()->id;
|
|
|
4139 |
$grade->userid = $userid;
|
|
|
4140 |
$grade->timecreated = time();
|
|
|
4141 |
// If we are "auto-creating" a grade - and there is a submission
|
|
|
4142 |
// the new grade should not have a more recent timemodified value
|
|
|
4143 |
// than the submission.
|
|
|
4144 |
if ($submission) {
|
|
|
4145 |
$grade->timemodified = $submission->timemodified;
|
|
|
4146 |
} else {
|
|
|
4147 |
$grade->timemodified = $grade->timecreated;
|
|
|
4148 |
}
|
|
|
4149 |
$grade->grade = -1;
|
|
|
4150 |
// Do not set the grader id here as it would be the admin users which is incorrect.
|
|
|
4151 |
$grade->grader = -1;
|
|
|
4152 |
if ($attemptnumber >= 0) {
|
|
|
4153 |
$grade->attemptnumber = $attemptnumber;
|
|
|
4154 |
}
|
|
|
4155 |
|
|
|
4156 |
$gid = $DB->insert_record('assign_grades', $grade);
|
|
|
4157 |
$grade->id = $gid;
|
|
|
4158 |
return $grade;
|
|
|
4159 |
}
|
|
|
4160 |
return false;
|
|
|
4161 |
}
|
|
|
4162 |
|
|
|
4163 |
/**
|
|
|
4164 |
* This will retrieve a grade object from the db.
|
|
|
4165 |
*
|
|
|
4166 |
* @param int $gradeid The id of the grade
|
|
|
4167 |
* @return stdClass The grade record
|
|
|
4168 |
*/
|
1254 |
ariadna |
4169 |
protected function get_grade($gradeid)
|
|
|
4170 |
{
|
1 |
efrain |
4171 |
global $DB;
|
|
|
4172 |
|
1254 |
ariadna |
4173 |
$params = array('assignment' => $this->get_instance()->id, 'id' => $gradeid);
|
1 |
efrain |
4174 |
return $DB->get_record('assign_grades', $params, '*', MUST_EXIST);
|
|
|
4175 |
}
|
|
|
4176 |
|
|
|
4177 |
/**
|
|
|
4178 |
* Print the grading page for a single user submission.
|
|
|
4179 |
*
|
|
|
4180 |
* @param array $args Optional args array (better than pulling args from _GET and _POST)
|
|
|
4181 |
* @return string
|
|
|
4182 |
*/
|
1254 |
ariadna |
4183 |
protected function view_single_grading_panel($args)
|
|
|
4184 |
{
|
1 |
efrain |
4185 |
global $DB, $CFG;
|
|
|
4186 |
|
|
|
4187 |
$o = '';
|
|
|
4188 |
|
|
|
4189 |
require_once($CFG->dirroot . '/mod/assign/gradeform.php');
|
|
|
4190 |
|
|
|
4191 |
// Need submit permission to submit an assignment.
|
|
|
4192 |
require_capability('mod/assign:grade', $this->context);
|
|
|
4193 |
|
|
|
4194 |
// If userid is passed - we are only grading a single student.
|
|
|
4195 |
$userid = $args['userid'];
|
|
|
4196 |
$attemptnumber = $args['attemptnumber'];
|
|
|
4197 |
$instance = $this->get_instance($userid);
|
|
|
4198 |
|
|
|
4199 |
// Apply overrides.
|
|
|
4200 |
$this->update_effective_access($userid);
|
|
|
4201 |
|
|
|
4202 |
$rownum = 0;
|
|
|
4203 |
$useridlist = array($userid);
|
|
|
4204 |
|
|
|
4205 |
$last = true;
|
|
|
4206 |
// This variation on the url will link direct to this student, with no next/previous links.
|
|
|
4207 |
// The benefit is the url will be the same every time for this student, so Atto autosave drafts can match up.
|
|
|
4208 |
$returnparams = array('userid' => $userid, 'rownum' => 0, 'useridlistid' => 0);
|
|
|
4209 |
$this->register_return_link('grade', $returnparams);
|
|
|
4210 |
|
|
|
4211 |
$user = $DB->get_record('user', array('id' => $userid));
|
|
|
4212 |
$submission = $this->get_user_submission($userid, false, $attemptnumber);
|
|
|
4213 |
$submissiongroup = null;
|
|
|
4214 |
$teamsubmission = null;
|
|
|
4215 |
$notsubmitted = array();
|
|
|
4216 |
if ($instance->teamsubmission) {
|
|
|
4217 |
$teamsubmission = $this->get_group_submission($userid, 0, false, $attemptnumber);
|
|
|
4218 |
$submissiongroup = $this->get_submission_group($userid);
|
|
|
4219 |
$groupid = 0;
|
|
|
4220 |
if ($submissiongroup) {
|
|
|
4221 |
$groupid = $submissiongroup->id;
|
|
|
4222 |
}
|
|
|
4223 |
$notsubmitted = $this->get_submission_group_members_who_have_not_submitted($groupid, false);
|
|
|
4224 |
}
|
|
|
4225 |
|
|
|
4226 |
// Get the requested grade.
|
|
|
4227 |
$grade = $this->get_user_grade($userid, false, $attemptnumber);
|
|
|
4228 |
$flags = $this->get_user_flags($userid, false);
|
|
|
4229 |
if ($this->can_view_submission($userid)) {
|
|
|
4230 |
$submissionlocked = ($flags && $flags->locked);
|
|
|
4231 |
$extensionduedate = null;
|
|
|
4232 |
if ($flags) {
|
|
|
4233 |
$extensionduedate = $flags->extensionduedate;
|
|
|
4234 |
}
|
|
|
4235 |
$showedit = $this->submissions_open($userid) && ($this->is_any_submission_plugin_enabled());
|
|
|
4236 |
$viewfullnames = has_capability('moodle/site:viewfullnames', $this->get_context());
|
|
|
4237 |
$usergroups = $this->get_all_groups($user->id);
|
|
|
4238 |
|
1254 |
ariadna |
4239 |
$submissionstatus = new assign_submission_status_compact(
|
|
|
4240 |
$instance->allowsubmissionsfromdate,
|
|
|
4241 |
$instance->alwaysshowdescription,
|
|
|
4242 |
$submission,
|
|
|
4243 |
$instance->teamsubmission,
|
|
|
4244 |
$teamsubmission,
|
|
|
4245 |
$submissiongroup,
|
|
|
4246 |
$notsubmitted,
|
|
|
4247 |
$this->is_any_submission_plugin_enabled(),
|
|
|
4248 |
$submissionlocked,
|
|
|
4249 |
$this->is_graded($userid),
|
|
|
4250 |
$instance->duedate,
|
|
|
4251 |
$instance->cutoffdate,
|
|
|
4252 |
$this->get_submission_plugins(),
|
|
|
4253 |
$this->get_return_action(),
|
|
|
4254 |
$this->get_return_params(),
|
|
|
4255 |
$this->get_course_module()->id,
|
|
|
4256 |
$this->get_course()->id,
|
|
|
4257 |
assign_submission_status::GRADER_VIEW,
|
|
|
4258 |
$showedit,
|
|
|
4259 |
false,
|
|
|
4260 |
$viewfullnames,
|
|
|
4261 |
$extensionduedate,
|
|
|
4262 |
$this->get_context(),
|
|
|
4263 |
$this->is_blind_marking(),
|
|
|
4264 |
'',
|
|
|
4265 |
$instance->attemptreopenmethod,
|
|
|
4266 |
$instance->maxattempts,
|
|
|
4267 |
$this->get_grading_status($userid),
|
|
|
4268 |
$instance->preventsubmissionnotingroup,
|
|
|
4269 |
$usergroups,
|
|
|
4270 |
$instance->timelimit
|
|
|
4271 |
);
|
1 |
efrain |
4272 |
$o .= $this->get_renderer()->render($submissionstatus);
|
|
|
4273 |
}
|
|
|
4274 |
|
|
|
4275 |
if ($grade) {
|
|
|
4276 |
$data = new stdClass();
|
|
|
4277 |
if ($grade->grade !== null && $grade->grade >= 0) {
|
|
|
4278 |
$data->grade = format_float($grade->grade, $this->get_grade_item()->get_decimals());
|
|
|
4279 |
}
|
|
|
4280 |
} else {
|
|
|
4281 |
$data = new stdClass();
|
|
|
4282 |
$data->grade = '';
|
|
|
4283 |
}
|
|
|
4284 |
|
|
|
4285 |
if (!empty($flags->workflowstate)) {
|
|
|
4286 |
$data->workflowstate = $flags->workflowstate;
|
|
|
4287 |
}
|
|
|
4288 |
if (!empty($flags->allocatedmarker)) {
|
|
|
4289 |
$data->allocatedmarker = $flags->allocatedmarker;
|
|
|
4290 |
}
|
|
|
4291 |
|
|
|
4292 |
// Warning if required.
|
|
|
4293 |
$allsubmissions = $this->get_all_submissions($userid);
|
|
|
4294 |
|
|
|
4295 |
if ($attemptnumber != -1 && ($attemptnumber + 1) != count($allsubmissions)) {
|
1254 |
ariadna |
4296 |
$params = array(
|
|
|
4297 |
'attemptnumber' => $attemptnumber + 1,
|
|
|
4298 |
'totalattempts' => count($allsubmissions)
|
|
|
4299 |
);
|
1 |
efrain |
4300 |
$message = get_string('editingpreviousfeedbackwarning', 'assign', $params);
|
|
|
4301 |
$o .= $this->get_renderer()->notification($message);
|
|
|
4302 |
}
|
|
|
4303 |
|
1254 |
ariadna |
4304 |
$pagination = array(
|
|
|
4305 |
'rownum' => $rownum,
|
|
|
4306 |
'useridlistid' => 0,
|
|
|
4307 |
'last' => $last,
|
|
|
4308 |
'userid' => $userid,
|
|
|
4309 |
'attemptnumber' => $attemptnumber,
|
|
|
4310 |
'gradingpanel' => true
|
|
|
4311 |
);
|
1 |
efrain |
4312 |
|
|
|
4313 |
if (!empty($args['formdata'])) {
|
|
|
4314 |
$data = (array) $data;
|
|
|
4315 |
$data = (object) array_merge($data, $args['formdata']);
|
|
|
4316 |
}
|
|
|
4317 |
$formparams = array($this, $data, $pagination);
|
1254 |
ariadna |
4318 |
$mform = new mod_assign_grade_form(
|
|
|
4319 |
null,
|
|
|
4320 |
$formparams,
|
|
|
4321 |
'post',
|
|
|
4322 |
'',
|
|
|
4323 |
array('class' => 'gradeform')
|
|
|
4324 |
);
|
1 |
efrain |
4325 |
|
|
|
4326 |
if (!empty($args['formdata'])) {
|
|
|
4327 |
// If we were passed form data - we want the form to check the data
|
|
|
4328 |
// and show errors.
|
|
|
4329 |
$mform->is_validated();
|
|
|
4330 |
}
|
|
|
4331 |
|
|
|
4332 |
$o .= $this->get_renderer()->render(new assign_form('gradingform', $mform));
|
|
|
4333 |
|
|
|
4334 |
if (count($allsubmissions) > 1) {
|
|
|
4335 |
$allgrades = $this->get_all_grades($userid);
|
1254 |
ariadna |
4336 |
$history = new assign_attempt_history_chooser(
|
|
|
4337 |
$allsubmissions,
|
|
|
4338 |
$allgrades,
|
|
|
4339 |
$this->get_course_module()->id,
|
|
|
4340 |
$userid
|
|
|
4341 |
);
|
1 |
efrain |
4342 |
|
|
|
4343 |
$o .= $this->get_renderer()->render($history);
|
|
|
4344 |
}
|
|
|
4345 |
|
|
|
4346 |
\mod_assign\event\grading_form_viewed::create_from_user($this, $user)->trigger();
|
|
|
4347 |
|
|
|
4348 |
return $o;
|
|
|
4349 |
}
|
|
|
4350 |
|
|
|
4351 |
/**
|
|
|
4352 |
* Print the grading page for a single user submission.
|
|
|
4353 |
*
|
|
|
4354 |
* @param moodleform $mform
|
|
|
4355 |
* @return string
|
|
|
4356 |
*/
|
1254 |
ariadna |
4357 |
protected function view_single_grade_page($mform)
|
|
|
4358 |
{
|
1 |
efrain |
4359 |
global $DB, $CFG, $SESSION;
|
|
|
4360 |
|
|
|
4361 |
$o = '';
|
|
|
4362 |
$instance = $this->get_instance();
|
|
|
4363 |
|
|
|
4364 |
require_once($CFG->dirroot . '/mod/assign/gradeform.php');
|
|
|
4365 |
|
|
|
4366 |
// Need submit permission to submit an assignment.
|
|
|
4367 |
require_capability('mod/assign:grade', $this->context);
|
|
|
4368 |
|
1254 |
ariadna |
4369 |
$header = new assign_header(
|
|
|
4370 |
$instance,
|
|
|
4371 |
$this->get_context(),
|
|
|
4372 |
false,
|
|
|
4373 |
$this->get_course_module()->id,
|
|
|
4374 |
get_string('grading', 'assign')
|
|
|
4375 |
);
|
1 |
efrain |
4376 |
$o .= $this->get_renderer()->render($header);
|
|
|
4377 |
|
|
|
4378 |
// If userid is passed - we are only grading a single student.
|
|
|
4379 |
$rownum = optional_param('rownum', 0, PARAM_INT);
|
|
|
4380 |
$useridlistid = optional_param('useridlistid', $this->get_useridlist_key_id(), PARAM_ALPHANUM);
|
|
|
4381 |
$userid = optional_param('userid', 0, PARAM_INT);
|
|
|
4382 |
$attemptnumber = optional_param('attemptnumber', -1, PARAM_INT);
|
|
|
4383 |
|
|
|
4384 |
if (!$userid) {
|
|
|
4385 |
$useridlist = $this->get_grading_userid_list(true, $useridlistid);
|
|
|
4386 |
} else {
|
|
|
4387 |
$rownum = 0;
|
|
|
4388 |
$useridlistid = 0;
|
|
|
4389 |
$useridlist = array($userid);
|
|
|
4390 |
}
|
|
|
4391 |
|
|
|
4392 |
if ($rownum < 0 || $rownum > count($useridlist)) {
|
|
|
4393 |
throw new coding_exception('Row is out of bounds for the current grading table: ' . $rownum);
|
|
|
4394 |
}
|
|
|
4395 |
|
|
|
4396 |
$last = false;
|
|
|
4397 |
$userid = $useridlist[$rownum];
|
|
|
4398 |
if ($rownum == count($useridlist) - 1) {
|
|
|
4399 |
$last = true;
|
|
|
4400 |
}
|
|
|
4401 |
// This variation on the url will link direct to this student, with no next/previous links.
|
|
|
4402 |
// The benefit is the url will be the same every time for this student, so Atto autosave drafts can match up.
|
|
|
4403 |
$returnparams = array('userid' => $userid, 'rownum' => 0, 'useridlistid' => 0);
|
|
|
4404 |
$this->register_return_link('grade', $returnparams);
|
|
|
4405 |
|
|
|
4406 |
$user = $DB->get_record('user', array('id' => $userid));
|
|
|
4407 |
if ($user) {
|
|
|
4408 |
$this->update_effective_access($userid);
|
|
|
4409 |
$viewfullnames = has_capability('moodle/site:viewfullnames', $this->get_context());
|
1254 |
ariadna |
4410 |
$usersummary = new assign_user_summary(
|
|
|
4411 |
$user,
|
|
|
4412 |
$this->get_course()->id,
|
|
|
4413 |
$viewfullnames,
|
|
|
4414 |
$this->is_blind_marking(),
|
|
|
4415 |
$this->get_uniqueid_for_user($user->id),
|
|
|
4416 |
// TODO Does not support custom user profile fields (MDL-70456).
|
|
|
4417 |
\core_user\fields::get_identity_fields($this->get_context(), false),
|
|
|
4418 |
!$this->is_active_user($userid)
|
|
|
4419 |
);
|
1 |
efrain |
4420 |
$o .= $this->get_renderer()->render($usersummary);
|
|
|
4421 |
}
|
|
|
4422 |
$submission = $this->get_user_submission($userid, false, $attemptnumber);
|
|
|
4423 |
$submissiongroup = null;
|
|
|
4424 |
$teamsubmission = null;
|
|
|
4425 |
$notsubmitted = array();
|
|
|
4426 |
if ($instance->teamsubmission) {
|
|
|
4427 |
$teamsubmission = $this->get_group_submission($userid, 0, false, $attemptnumber);
|
|
|
4428 |
$submissiongroup = $this->get_submission_group($userid);
|
|
|
4429 |
$groupid = 0;
|
|
|
4430 |
if ($submissiongroup) {
|
|
|
4431 |
$groupid = $submissiongroup->id;
|
|
|
4432 |
}
|
|
|
4433 |
$notsubmitted = $this->get_submission_group_members_who_have_not_submitted($groupid, false);
|
|
|
4434 |
}
|
|
|
4435 |
|
|
|
4436 |
// Get the requested grade.
|
|
|
4437 |
$grade = $this->get_user_grade($userid, false, $attemptnumber);
|
|
|
4438 |
$flags = $this->get_user_flags($userid, false);
|
|
|
4439 |
if ($this->can_view_submission($userid)) {
|
|
|
4440 |
$submissionlocked = ($flags && $flags->locked);
|
|
|
4441 |
$extensionduedate = null;
|
|
|
4442 |
if ($flags) {
|
|
|
4443 |
$extensionduedate = $flags->extensionduedate;
|
|
|
4444 |
}
|
|
|
4445 |
$showedit = $this->submissions_open($userid) && ($this->is_any_submission_plugin_enabled());
|
|
|
4446 |
$viewfullnames = has_capability('moodle/site:viewfullnames', $this->get_context());
|
|
|
4447 |
$usergroups = $this->get_all_groups($user->id);
|
1254 |
ariadna |
4448 |
$submissionstatus = new assign_submission_status(
|
|
|
4449 |
$instance->allowsubmissionsfromdate,
|
|
|
4450 |
$instance->alwaysshowdescription,
|
|
|
4451 |
$submission,
|
|
|
4452 |
$instance->teamsubmission,
|
|
|
4453 |
$teamsubmission,
|
|
|
4454 |
$submissiongroup,
|
|
|
4455 |
$notsubmitted,
|
|
|
4456 |
$this->is_any_submission_plugin_enabled(),
|
|
|
4457 |
$submissionlocked,
|
|
|
4458 |
$this->is_graded($userid),
|
|
|
4459 |
$instance->duedate,
|
|
|
4460 |
$instance->cutoffdate,
|
|
|
4461 |
$this->get_submission_plugins(),
|
|
|
4462 |
$this->get_return_action(),
|
|
|
4463 |
$this->get_return_params(),
|
|
|
4464 |
$this->get_course_module()->id,
|
|
|
4465 |
$this->get_course()->id,
|
|
|
4466 |
assign_submission_status::GRADER_VIEW,
|
|
|
4467 |
$showedit,
|
|
|
4468 |
false,
|
|
|
4469 |
$viewfullnames,
|
|
|
4470 |
$extensionduedate,
|
|
|
4471 |
$this->get_context(),
|
|
|
4472 |
$this->is_blind_marking(),
|
|
|
4473 |
'',
|
|
|
4474 |
$instance->attemptreopenmethod,
|
|
|
4475 |
$instance->maxattempts,
|
|
|
4476 |
$this->get_grading_status($userid),
|
|
|
4477 |
$instance->preventsubmissionnotingroup,
|
|
|
4478 |
$usergroups,
|
|
|
4479 |
$instance->timelimit
|
|
|
4480 |
);
|
1 |
efrain |
4481 |
$o .= $this->get_renderer()->render($submissionstatus);
|
|
|
4482 |
}
|
|
|
4483 |
|
|
|
4484 |
if ($grade) {
|
|
|
4485 |
$data = new stdClass();
|
|
|
4486 |
if ($grade->grade !== null && $grade->grade >= 0) {
|
|
|
4487 |
$data->grade = format_float($grade->grade, $this->get_grade_item()->get_decimals());
|
|
|
4488 |
}
|
|
|
4489 |
} else {
|
|
|
4490 |
$data = new stdClass();
|
|
|
4491 |
$data->grade = '';
|
|
|
4492 |
}
|
|
|
4493 |
|
|
|
4494 |
if (!empty($flags->workflowstate)) {
|
|
|
4495 |
$data->workflowstate = $flags->workflowstate;
|
|
|
4496 |
}
|
|
|
4497 |
if (!empty($flags->allocatedmarker)) {
|
|
|
4498 |
$data->allocatedmarker = $flags->allocatedmarker;
|
|
|
4499 |
}
|
|
|
4500 |
|
|
|
4501 |
// Warning if required.
|
|
|
4502 |
$allsubmissions = $this->get_all_submissions($userid);
|
|
|
4503 |
|
|
|
4504 |
if ($attemptnumber != -1 && ($attemptnumber + 1) != count($allsubmissions)) {
|
1254 |
ariadna |
4505 |
$params = array(
|
|
|
4506 |
'attemptnumber' => $attemptnumber + 1,
|
|
|
4507 |
'totalattempts' => count($allsubmissions)
|
|
|
4508 |
);
|
1 |
efrain |
4509 |
$message = get_string('editingpreviousfeedbackwarning', 'assign', $params);
|
|
|
4510 |
$o .= $this->get_renderer()->notification($message);
|
|
|
4511 |
}
|
|
|
4512 |
|
|
|
4513 |
// Now show the grading form.
|
|
|
4514 |
if (!$mform) {
|
1254 |
ariadna |
4515 |
$pagination = array(
|
|
|
4516 |
'rownum' => $rownum,
|
|
|
4517 |
'useridlistid' => $useridlistid,
|
|
|
4518 |
'last' => $last,
|
|
|
4519 |
'userid' => $userid,
|
|
|
4520 |
'attemptnumber' => $attemptnumber
|
|
|
4521 |
);
|
1 |
efrain |
4522 |
$formparams = array($this, $data, $pagination);
|
1254 |
ariadna |
4523 |
$mform = new mod_assign_grade_form(
|
|
|
4524 |
null,
|
|
|
4525 |
$formparams,
|
|
|
4526 |
'post',
|
|
|
4527 |
'',
|
|
|
4528 |
array('class' => 'gradeform')
|
|
|
4529 |
);
|
1 |
efrain |
4530 |
}
|
|
|
4531 |
|
|
|
4532 |
$o .= $this->get_renderer()->render(new assign_form('gradingform', $mform));
|
|
|
4533 |
|
|
|
4534 |
if (count($allsubmissions) > 1 && $attemptnumber == -1) {
|
|
|
4535 |
$allgrades = $this->get_all_grades($userid);
|
1254 |
ariadna |
4536 |
$history = new assign_attempt_history(
|
|
|
4537 |
$allsubmissions,
|
|
|
4538 |
$allgrades,
|
|
|
4539 |
$this->get_submission_plugins(),
|
|
|
4540 |
$this->get_feedback_plugins(),
|
|
|
4541 |
$this->get_course_module()->id,
|
|
|
4542 |
$this->get_return_action(),
|
|
|
4543 |
$this->get_return_params(),
|
|
|
4544 |
true,
|
|
|
4545 |
$useridlistid,
|
|
|
4546 |
$rownum
|
|
|
4547 |
);
|
1 |
efrain |
4548 |
|
|
|
4549 |
$o .= $this->get_renderer()->render($history);
|
|
|
4550 |
}
|
|
|
4551 |
|
|
|
4552 |
\mod_assign\event\grading_form_viewed::create_from_user($this, $user)->trigger();
|
|
|
4553 |
|
|
|
4554 |
$o .= $this->view_footer();
|
|
|
4555 |
return $o;
|
|
|
4556 |
}
|
|
|
4557 |
|
|
|
4558 |
/**
|
|
|
4559 |
* Show a confirmation page to make sure they want to remove submission data.
|
|
|
4560 |
*
|
|
|
4561 |
* @return string
|
|
|
4562 |
*/
|
1254 |
ariadna |
4563 |
protected function view_remove_submission_confirm()
|
|
|
4564 |
{
|
1 |
efrain |
4565 |
global $USER;
|
|
|
4566 |
|
|
|
4567 |
$userid = optional_param('userid', $USER->id, PARAM_INT);
|
|
|
4568 |
|
|
|
4569 |
if (!$this->can_edit_submission($userid, $USER->id)) {
|
|
|
4570 |
throw new \moodle_exception('nopermission');
|
|
|
4571 |
}
|
|
|
4572 |
$user = core_user::get_user($userid, '*', MUST_EXIST);
|
|
|
4573 |
|
|
|
4574 |
$o = '';
|
1254 |
ariadna |
4575 |
$header = new assign_header(
|
|
|
4576 |
$this->get_instance(),
|
|
|
4577 |
$this->get_context(),
|
|
|
4578 |
false,
|
|
|
4579 |
$this->get_course_module()->id
|
|
|
4580 |
);
|
1 |
efrain |
4581 |
$o .= $this->get_renderer()->render($header);
|
|
|
4582 |
|
1254 |
ariadna |
4583 |
$urlparams = array(
|
|
|
4584 |
'id' => $this->get_course_module()->id,
|
|
|
4585 |
'action' => 'removesubmission',
|
|
|
4586 |
'userid' => $userid,
|
|
|
4587 |
'sesskey' => sesskey()
|
|
|
4588 |
);
|
1 |
efrain |
4589 |
$confirmurl = new moodle_url('/mod/assign/view.php', $urlparams);
|
|
|
4590 |
|
1254 |
ariadna |
4591 |
$urlparams = array(
|
|
|
4592 |
'id' => $this->get_course_module()->id,
|
|
|
4593 |
'action' => 'view'
|
|
|
4594 |
);
|
1 |
efrain |
4595 |
$cancelurl = new moodle_url('/mod/assign/view.php', $urlparams);
|
|
|
4596 |
|
|
|
4597 |
if ($userid == $USER->id) {
|
|
|
4598 |
if ($this->is_time_limit_enabled($userid)) {
|
|
|
4599 |
$confirmstr = get_string('removesubmissionconfirmwithtimelimit', 'assign');
|
|
|
4600 |
} else {
|
|
|
4601 |
$confirmstr = get_string('removesubmissionconfirm', 'assign');
|
|
|
4602 |
}
|
|
|
4603 |
} else {
|
|
|
4604 |
if ($this->is_time_limit_enabled($userid)) {
|
|
|
4605 |
$confirmstr = get_string('removesubmissionconfirmforstudentwithtimelimit', 'assign', $this->fullname($user));
|
|
|
4606 |
} else {
|
|
|
4607 |
$confirmstr = get_string('removesubmissionconfirmforstudent', 'assign', $this->fullname($user));
|
|
|
4608 |
}
|
|
|
4609 |
}
|
1254 |
ariadna |
4610 |
$o .= $this->get_renderer()->confirm(
|
|
|
4611 |
$confirmstr,
|
|
|
4612 |
$confirmurl,
|
|
|
4613 |
$cancelurl
|
|
|
4614 |
);
|
1 |
efrain |
4615 |
$o .= $this->view_footer();
|
|
|
4616 |
|
|
|
4617 |
\mod_assign\event\remove_submission_form_viewed::create_from_user($this, $user)->trigger();
|
|
|
4618 |
|
|
|
4619 |
return $o;
|
|
|
4620 |
}
|
|
|
4621 |
|
|
|
4622 |
|
|
|
4623 |
/**
|
|
|
4624 |
* Show a confirmation page to make sure they want to release student identities.
|
|
|
4625 |
*
|
|
|
4626 |
* @return string
|
|
|
4627 |
*/
|
1254 |
ariadna |
4628 |
protected function view_reveal_identities_confirm()
|
|
|
4629 |
{
|
1 |
efrain |
4630 |
require_capability('mod/assign:revealidentities', $this->get_context());
|
|
|
4631 |
|
|
|
4632 |
$o = '';
|
1254 |
ariadna |
4633 |
$header = new assign_header(
|
|
|
4634 |
$this->get_instance(),
|
|
|
4635 |
$this->get_context(),
|
|
|
4636 |
false,
|
|
|
4637 |
$this->get_course_module()->id
|
|
|
4638 |
);
|
1 |
efrain |
4639 |
$o .= $this->get_renderer()->render($header);
|
|
|
4640 |
|
1254 |
ariadna |
4641 |
$urlparams = array(
|
|
|
4642 |
'id' => $this->get_course_module()->id,
|
|
|
4643 |
'action' => 'revealidentitiesconfirm',
|
|
|
4644 |
'sesskey' => sesskey()
|
|
|
4645 |
);
|
1 |
efrain |
4646 |
$confirmurl = new moodle_url('/mod/assign/view.php', $urlparams);
|
|
|
4647 |
|
1254 |
ariadna |
4648 |
$urlparams = array(
|
|
|
4649 |
'id' => $this->get_course_module()->id,
|
|
|
4650 |
'action' => 'grading'
|
|
|
4651 |
);
|
1 |
efrain |
4652 |
$cancelurl = new moodle_url('/mod/assign/view.php', $urlparams);
|
|
|
4653 |
|
1254 |
ariadna |
4654 |
$o .= $this->get_renderer()->confirm(
|
|
|
4655 |
get_string('revealidentitiesconfirm', 'assign'),
|
|
|
4656 |
$confirmurl,
|
|
|
4657 |
$cancelurl
|
|
|
4658 |
);
|
1 |
efrain |
4659 |
$o .= $this->view_footer();
|
|
|
4660 |
|
|
|
4661 |
\mod_assign\event\reveal_identities_confirmation_page_viewed::create_from_assign($this)->trigger();
|
|
|
4662 |
|
|
|
4663 |
return $o;
|
|
|
4664 |
}
|
|
|
4665 |
|
|
|
4666 |
/**
|
|
|
4667 |
* View a link to go back to the previous page. Uses url parameters returnaction and returnparams.
|
|
|
4668 |
*
|
|
|
4669 |
* @return string
|
|
|
4670 |
*/
|
1254 |
ariadna |
4671 |
protected function view_return_links()
|
|
|
4672 |
{
|
1 |
efrain |
4673 |
$returnaction = optional_param('returnaction', '', PARAM_ALPHA);
|
|
|
4674 |
$returnparams = optional_param('returnparams', '', PARAM_TEXT);
|
|
|
4675 |
|
|
|
4676 |
$params = array();
|
|
|
4677 |
$returnparams = str_replace('&', '&', $returnparams);
|
|
|
4678 |
parse_str($returnparams, $params);
|
|
|
4679 |
$newparams = array('id' => $this->get_course_module()->id, 'action' => $returnaction);
|
|
|
4680 |
$params = array_merge($newparams, $params);
|
|
|
4681 |
|
|
|
4682 |
$url = new moodle_url('/mod/assign/view.php', $params);
|
|
|
4683 |
return $this->get_renderer()->single_button($url, get_string('back'), 'get');
|
|
|
4684 |
}
|
|
|
4685 |
|
|
|
4686 |
/**
|
|
|
4687 |
* View the grading table of all submissions for this assignment.
|
|
|
4688 |
*
|
|
|
4689 |
* @return string
|
|
|
4690 |
*/
|
1254 |
ariadna |
4691 |
protected function view_grading_table()
|
|
|
4692 |
{
|
1 |
efrain |
4693 |
global $USER, $CFG, $SESSION, $PAGE;
|
|
|
4694 |
|
|
|
4695 |
// Include grading options form.
|
|
|
4696 |
require_once($CFG->dirroot . '/mod/assign/gradingoptionsform.php');
|
|
|
4697 |
require_once($CFG->dirroot . '/mod/assign/quickgradingform.php');
|
|
|
4698 |
require_once($CFG->dirroot . '/mod/assign/gradingbatchoperationsform.php');
|
|
|
4699 |
$o = '';
|
|
|
4700 |
$cmid = $this->get_course_module()->id;
|
|
|
4701 |
|
|
|
4702 |
$links = array();
|
1254 |
ariadna |
4703 |
if (
|
|
|
4704 |
has_capability('gradereport/grader:view', $this->get_course_context()) &&
|
|
|
4705 |
has_capability('moodle/grade:viewall', $this->get_course_context())
|
|
|
4706 |
) {
|
1 |
efrain |
4707 |
$gradebookurl = '/grade/report/grader/index.php?id=' . $this->get_course()->id;
|
|
|
4708 |
$links[$gradebookurl] = get_string('viewgradebook', 'assign');
|
|
|
4709 |
}
|
1254 |
ariadna |
4710 |
if (
|
|
|
4711 |
$this->is_blind_marking() &&
|
|
|
4712 |
has_capability('mod/assign:revealidentities', $this->get_context())
|
|
|
4713 |
) {
|
1 |
efrain |
4714 |
$revealidentitiesurl = '/mod/assign/view.php?id=' . $cmid . '&action=revealidentities';
|
|
|
4715 |
$links[$revealidentitiesurl] = get_string('revealidentities', 'assign');
|
|
|
4716 |
}
|
|
|
4717 |
foreach ($this->get_feedback_plugins() as $plugin) {
|
|
|
4718 |
if ($plugin->is_enabled() && $plugin->is_visible()) {
|
|
|
4719 |
foreach ($plugin->get_grading_actions() as $action => $description) {
|
|
|
4720 |
$url = '/mod/assign/view.php' .
|
1254 |
ariadna |
4721 |
'?id=' . $cmid .
|
|
|
4722 |
'&plugin=' . $plugin->get_type() .
|
|
|
4723 |
'&pluginsubtype=assignfeedback' .
|
|
|
4724 |
'&action=viewpluginpage&pluginaction=' . $action;
|
1 |
efrain |
4725 |
$links[$url] = $description;
|
|
|
4726 |
}
|
|
|
4727 |
}
|
|
|
4728 |
}
|
|
|
4729 |
|
|
|
4730 |
// Sort links alphabetically based on the link description.
|
|
|
4731 |
core_collator::asort($links);
|
|
|
4732 |
|
|
|
4733 |
$gradingactions = new url_select($links);
|
|
|
4734 |
$gradingactions->set_label(get_string('choosegradingaction', 'assign'));
|
|
|
4735 |
$gradingactions->class .= ' mb-1';
|
|
|
4736 |
|
|
|
4737 |
$gradingmanager = get_grading_manager($this->get_context(), 'mod_assign', 'submissions');
|
|
|
4738 |
|
|
|
4739 |
$perpage = $this->get_assign_perpage();
|
|
|
4740 |
$filter = get_user_preferences('assign_filter', '');
|
|
|
4741 |
$markerfilter = get_user_preferences('assign_markerfilter', '');
|
|
|
4742 |
$workflowfilter = get_user_preferences('assign_workflowfilter', '');
|
|
|
4743 |
$controller = $gradingmanager->get_active_controller();
|
|
|
4744 |
$showquickgrading = empty($controller) && $this->can_grade();
|
|
|
4745 |
$quickgrading = get_user_preferences('assign_quickgrading', false);
|
|
|
4746 |
$showonlyactiveenrolopt = has_capability('moodle/course:viewsuspendedusers', $this->context);
|
|
|
4747 |
$downloadasfolders = get_user_preferences('assign_downloadasfolders', 1);
|
|
|
4748 |
|
|
|
4749 |
$markingallocation = $this->get_instance()->markingworkflow &&
|
|
|
4750 |
$this->get_instance()->markingallocation &&
|
|
|
4751 |
has_capability('mod/assign:manageallocations', $this->context);
|
|
|
4752 |
// Get markers to use in drop lists.
|
|
|
4753 |
$markingallocationoptions = array();
|
|
|
4754 |
if ($markingallocation) {
|
|
|
4755 |
list($sort, $params) = users_order_by_sql('u');
|
|
|
4756 |
// Only enrolled users could be assigned as potential markers.
|
|
|
4757 |
$markers = get_enrolled_users($this->context, 'mod/assign:grade', 0, 'u.*', $sort);
|
|
|
4758 |
$markingallocationoptions[''] = get_string('filternone', 'assign');
|
|
|
4759 |
$markingallocationoptions[ASSIGN_MARKER_FILTER_NO_MARKER] = get_string('markerfilternomarker', 'assign');
|
|
|
4760 |
$viewfullnames = has_capability('moodle/site:viewfullnames', $this->context);
|
|
|
4761 |
foreach ($markers as $marker) {
|
|
|
4762 |
$markingallocationoptions[$marker->id] = fullname($marker, $viewfullnames);
|
|
|
4763 |
}
|
|
|
4764 |
}
|
|
|
4765 |
|
|
|
4766 |
$markingworkflow = $this->get_instance()->markingworkflow;
|
|
|
4767 |
// Get marking states to show in form.
|
|
|
4768 |
$markingworkflowoptions = $this->get_marking_workflow_filters();
|
|
|
4769 |
|
|
|
4770 |
// Print options for changing the filter and changing the number of results per page.
|
1254 |
ariadna |
4771 |
$gradingoptionsformparams = array(
|
|
|
4772 |
'cm' => $cmid,
|
|
|
4773 |
'contextid' => $this->context->id,
|
|
|
4774 |
'userid' => $USER->id,
|
|
|
4775 |
'submissionsenabled' => $this->is_any_submission_plugin_enabled(),
|
|
|
4776 |
'showquickgrading' => $showquickgrading,
|
|
|
4777 |
'quickgrading' => $quickgrading,
|
|
|
4778 |
'markingworkflowopt' => $markingworkflowoptions,
|
|
|
4779 |
'markingallocationopt' => $markingallocationoptions,
|
|
|
4780 |
'showonlyactiveenrolopt' => $showonlyactiveenrolopt,
|
|
|
4781 |
'showonlyactiveenrol' => $this->show_only_active_users(),
|
|
|
4782 |
'downloadasfolders' => $downloadasfolders
|
|
|
4783 |
);
|
1 |
efrain |
4784 |
|
1254 |
ariadna |
4785 |
$classoptions = array('class' => 'gradingoptionsform');
|
|
|
4786 |
$gradingoptionsform = new mod_assign_grading_options_form(
|
|
|
4787 |
null,
|
|
|
4788 |
$gradingoptionsformparams,
|
|
|
4789 |
'post',
|
|
|
4790 |
'',
|
|
|
4791 |
$classoptions
|
|
|
4792 |
);
|
1 |
efrain |
4793 |
|
1254 |
ariadna |
4794 |
$batchformparams = array(
|
|
|
4795 |
'cm' => $cmid,
|
|
|
4796 |
'submissiondrafts' => $this->get_instance()->submissiondrafts,
|
|
|
4797 |
'duedate' => $this->get_instance()->duedate,
|
|
|
4798 |
'attemptreopenmethod' => $this->get_instance()->attemptreopenmethod,
|
|
|
4799 |
'feedbackplugins' => $this->get_feedback_plugins(),
|
|
|
4800 |
'context' => $this->get_context(),
|
|
|
4801 |
'markingworkflow' => $markingworkflow,
|
|
|
4802 |
'markingallocation' => $markingallocation
|
|
|
4803 |
);
|
1 |
efrain |
4804 |
$classoptions = [
|
|
|
4805 |
'class' => 'gradingbatchoperationsform',
|
|
|
4806 |
'data-double-submit-protection' => 'off',
|
|
|
4807 |
];
|
|
|
4808 |
|
1254 |
ariadna |
4809 |
$gradingbatchoperationsform = new mod_assign_grading_batch_operations_form(
|
|
|
4810 |
null,
|
|
|
4811 |
$batchformparams,
|
|
|
4812 |
'post',
|
|
|
4813 |
'',
|
|
|
4814 |
$classoptions
|
|
|
4815 |
);
|
1 |
efrain |
4816 |
|
|
|
4817 |
$gradingoptionsdata = new stdClass();
|
|
|
4818 |
$gradingoptionsdata->perpage = $perpage;
|
|
|
4819 |
$gradingoptionsdata->filter = $filter;
|
|
|
4820 |
$gradingoptionsdata->markerfilter = $markerfilter;
|
|
|
4821 |
$gradingoptionsdata->workflowfilter = $workflowfilter;
|
|
|
4822 |
$gradingoptionsform->set_data($gradingoptionsdata);
|
|
|
4823 |
|
1254 |
ariadna |
4824 |
$buttons = new \mod_assign\output\grading_actionmenu(
|
|
|
4825 |
$this->get_course_module()->id,
|
|
|
4826 |
$this->is_any_submission_plugin_enabled(),
|
|
|
4827 |
$this->count_submissions()
|
|
|
4828 |
);
|
1 |
efrain |
4829 |
$actionformtext = $this->get_renderer()->render($buttons);
|
|
|
4830 |
$PAGE->activityheader->set_attrs(['hidecompletion' => true]);
|
|
|
4831 |
|
|
|
4832 |
$currenturl = new moodle_url('/mod/assign/view.php', ['id' => $this->get_course_module()->id, 'action' => 'grading']);
|
|
|
4833 |
|
1254 |
ariadna |
4834 |
$header = new assign_header(
|
|
|
4835 |
$this->get_instance(),
|
|
|
4836 |
$this->get_context(),
|
|
|
4837 |
false,
|
|
|
4838 |
$this->get_course_module()->id,
|
|
|
4839 |
get_string('grading', 'assign'),
|
|
|
4840 |
'',
|
|
|
4841 |
'',
|
|
|
4842 |
$currenturl
|
|
|
4843 |
);
|
1 |
efrain |
4844 |
$o .= $this->get_renderer()->render($header);
|
|
|
4845 |
|
|
|
4846 |
$o .= $actionformtext;
|
|
|
4847 |
|
|
|
4848 |
$o .= $this->get_renderer()->heading(get_string('gradeitem:submissions', 'mod_assign'), 2);
|
|
|
4849 |
$o .= $this->get_renderer()->render($gradingactions);
|
|
|
4850 |
|
|
|
4851 |
$o .= groups_print_activity_menu($this->get_course_module(), $currenturl, true);
|
|
|
4852 |
|
|
|
4853 |
// Plagiarism update status apearring in the grading book.
|
|
|
4854 |
if (!empty($CFG->enableplagiarism)) {
|
|
|
4855 |
require_once($CFG->libdir . '/plagiarismlib.php');
|
|
|
4856 |
$o .= plagiarism_update_status($this->get_course(), $this->get_course_module());
|
|
|
4857 |
}
|
|
|
4858 |
|
|
|
4859 |
if ($this->is_blind_marking() && has_capability('mod/assign:viewblinddetails', $this->get_context())) {
|
|
|
4860 |
$o .= $this->get_renderer()->notification(get_string('blindmarkingenabledwarning', 'assign'), 'notifymessage');
|
|
|
4861 |
}
|
|
|
4862 |
|
|
|
4863 |
// Load and print the table of submissions.
|
1256 |
ariadna |
4864 |
$assignform = new assign_form(
|
|
|
4865 |
'gradingoptionsform',
|
|
|
4866 |
$gradingoptionsform,
|
|
|
4867 |
'M.mod_assign.init_grading_options'
|
|
|
4868 |
);
|
|
|
4869 |
$o .= $this->get_renderer()->render($assignform);
|
1255 |
ariadna |
4870 |
$currentgroup = groups_get_activity_group($this->get_course_module(), true);
|
|
|
4871 |
$users = array_keys($this->list_participants($currentgroup, true));
|
|
|
4872 |
if (count($users) != 0 && $this->can_grade()) {
|
|
|
4873 |
// If no enrolled user in a course then don't display the batch operations feature.
|
|
|
4874 |
$assignform = new assign_form('gradingbatchoperationsform', $gradingbatchoperationsform);
|
|
|
4875 |
$o .= $this->get_renderer()->render($assignform);
|
|
|
4876 |
}
|
1 |
efrain |
4877 |
if ($showquickgrading && $quickgrading) {
|
1267 |
ariadna |
4878 |
$page = optional_param('page', null, PARAM_INT);
|
|
|
4879 |
$gradingtable = new assign_grading_table($this, $perpage, $filter, 0, true);
|
|
|
4880 |
$quickgradingform = new mod_assign_quick_grading_form(null, $quickformparams);
|
1 |
efrain |
4881 |
$table = $this->get_renderer()->render($gradingtable);
|
1254 |
ariadna |
4882 |
$quickformparams = array(
|
|
|
4883 |
'cm' => $this->get_course_module()->id,
|
|
|
4884 |
'gradingtable' => $table,
|
|
|
4885 |
'sendstudentnotifications' => $this->get_instance()->sendstudentnotifications,
|
|
|
4886 |
'page' => $page
|
|
|
4887 |
);
|
1 |
efrain |
4888 |
|
|
|
4889 |
$o .= $this->get_renderer()->render(new assign_form('quickgradingform', $quickgradingform));
|
|
|
4890 |
} else {
|
|
|
4891 |
$gradingtable = new assign_grading_table($this, $perpage, $filter, 0, false);
|
|
|
4892 |
$o .= $this->get_renderer()->render($gradingtable);
|
|
|
4893 |
}
|
|
|
4894 |
|
|
|
4895 |
if ($this->can_grade()) {
|
|
|
4896 |
// We need to store the order of uses in the table as the person may wish to grade them.
|
|
|
4897 |
// This is done based on the row number of the user.
|
|
|
4898 |
$useridlist = $gradingtable->get_column_data('userid');
|
|
|
4899 |
$SESSION->mod_assign_useridlist[$this->get_useridlist_key()] = $useridlist;
|
|
|
4900 |
}
|
|
|
4901 |
return $o;
|
|
|
4902 |
}
|
|
|
4903 |
|
|
|
4904 |
/**
|
|
|
4905 |
* View entire grader app.
|
|
|
4906 |
*
|
|
|
4907 |
* @return string
|
|
|
4908 |
*/
|
1254 |
ariadna |
4909 |
protected function view_grader()
|
|
|
4910 |
{
|
1 |
efrain |
4911 |
global $USER, $PAGE;
|
|
|
4912 |
|
|
|
4913 |
$o = '';
|
|
|
4914 |
// Need submit permission to submit an assignment.
|
|
|
4915 |
$this->require_view_grades();
|
|
|
4916 |
|
|
|
4917 |
$PAGE->set_pagelayout('embedded');
|
|
|
4918 |
|
|
|
4919 |
$PAGE->activityheader->disable();
|
|
|
4920 |
|
|
|
4921 |
$courseshortname = $this->get_context()->get_course_context()->get_context_name(false, true);
|
|
|
4922 |
$args = [
|
|
|
4923 |
'contextname' => $this->get_context()->get_context_name(false, true),
|
|
|
4924 |
'subpage' => get_string('grading', 'assign')
|
|
|
4925 |
];
|
|
|
4926 |
$title = get_string('subpagetitle', 'assign', $args);
|
|
|
4927 |
$title = $courseshortname . ': ' . $title;
|
|
|
4928 |
$PAGE->set_title($title);
|
|
|
4929 |
|
|
|
4930 |
$o .= $this->get_renderer()->header();
|
|
|
4931 |
|
|
|
4932 |
$userid = optional_param('userid', 0, PARAM_INT);
|
|
|
4933 |
$blindid = optional_param('blindid', 0, PARAM_INT);
|
|
|
4934 |
|
|
|
4935 |
if (!$userid && $blindid) {
|
|
|
4936 |
$userid = $this->get_user_id_for_uniqueid($blindid);
|
|
|
4937 |
}
|
|
|
4938 |
|
|
|
4939 |
// Instantiate table object to apply table preferences.
|
|
|
4940 |
$gradingtable = new assign_grading_table($this, 10, '', 0, false);
|
|
|
4941 |
$gradingtable->setup();
|
|
|
4942 |
|
|
|
4943 |
$currentgroup = groups_get_activity_group($this->get_course_module(), true);
|
|
|
4944 |
$framegrader = new grading_app($userid, $currentgroup, $this);
|
|
|
4945 |
|
|
|
4946 |
$this->update_effective_access($userid);
|
|
|
4947 |
|
|
|
4948 |
$o .= $this->get_renderer()->render($framegrader);
|
|
|
4949 |
|
|
|
4950 |
$o .= $this->view_footer();
|
|
|
4951 |
|
|
|
4952 |
\mod_assign\event\grading_table_viewed::create_from_assign($this)->trigger();
|
|
|
4953 |
|
|
|
4954 |
return $o;
|
|
|
4955 |
}
|
|
|
4956 |
/**
|
|
|
4957 |
* View entire grading page.
|
|
|
4958 |
*
|
|
|
4959 |
* @return string
|
|
|
4960 |
*/
|
1254 |
ariadna |
4961 |
protected function view_grading_page()
|
|
|
4962 |
{
|
1 |
efrain |
4963 |
global $CFG;
|
|
|
4964 |
|
|
|
4965 |
$o = '';
|
|
|
4966 |
// Need submit permission to submit an assignment.
|
|
|
4967 |
$this->require_view_grades();
|
|
|
4968 |
require_once($CFG->dirroot . '/mod/assign/gradeform.php');
|
|
|
4969 |
|
|
|
4970 |
$this->add_grade_notices();
|
|
|
4971 |
|
|
|
4972 |
// Only load this if it is.
|
|
|
4973 |
$o .= $this->view_grading_table();
|
|
|
4974 |
|
|
|
4975 |
$o .= $this->view_footer();
|
|
|
4976 |
|
|
|
4977 |
\mod_assign\event\grading_table_viewed::create_from_assign($this)->trigger();
|
|
|
4978 |
|
|
|
4979 |
return $o;
|
|
|
4980 |
}
|
|
|
4981 |
|
|
|
4982 |
/**
|
|
|
4983 |
* Capture the output of the plagiarism plugins disclosures and return it as a string.
|
|
|
4984 |
*
|
|
|
4985 |
* @return string
|
|
|
4986 |
*/
|
1254 |
ariadna |
4987 |
protected function plagiarism_print_disclosure()
|
|
|
4988 |
{
|
1 |
efrain |
4989 |
global $CFG;
|
|
|
4990 |
$o = '';
|
|
|
4991 |
|
|
|
4992 |
if (!empty($CFG->enableplagiarism)) {
|
|
|
4993 |
require_once($CFG->libdir . '/plagiarismlib.php');
|
|
|
4994 |
|
|
|
4995 |
$o .= plagiarism_print_disclosure($this->get_course_module()->id);
|
|
|
4996 |
}
|
|
|
4997 |
|
|
|
4998 |
return $o;
|
|
|
4999 |
}
|
|
|
5000 |
|
|
|
5001 |
/**
|
|
|
5002 |
* Message for students when assignment submissions have been closed.
|
|
|
5003 |
*
|
|
|
5004 |
* @param string $title The page title
|
|
|
5005 |
* @param array $notices The array of notices to show.
|
|
|
5006 |
* @return string
|
|
|
5007 |
*/
|
1254 |
ariadna |
5008 |
protected function view_notices($title, $notices)
|
|
|
5009 |
{
|
1 |
efrain |
5010 |
global $CFG;
|
|
|
5011 |
|
|
|
5012 |
$o = '';
|
|
|
5013 |
|
1254 |
ariadna |
5014 |
$header = new assign_header(
|
|
|
5015 |
$this->get_instance(),
|
|
|
5016 |
$this->get_context(),
|
|
|
5017 |
$this->show_intro(),
|
|
|
5018 |
$this->get_course_module()->id,
|
|
|
5019 |
$title
|
|
|
5020 |
);
|
1 |
efrain |
5021 |
$o .= $this->get_renderer()->render($header);
|
|
|
5022 |
|
|
|
5023 |
foreach ($notices as $notice) {
|
|
|
5024 |
$o .= $this->get_renderer()->notification($notice);
|
|
|
5025 |
}
|
|
|
5026 |
|
1254 |
ariadna |
5027 |
$url = new moodle_url('/mod/assign/view.php', array('id' => $this->get_course_module()->id, 'action' => 'view'));
|
1 |
efrain |
5028 |
$o .= $this->get_renderer()->continue_button($url);
|
|
|
5029 |
|
|
|
5030 |
$o .= $this->view_footer();
|
|
|
5031 |
|
|
|
5032 |
return $o;
|
|
|
5033 |
}
|
|
|
5034 |
|
|
|
5035 |
/**
|
|
|
5036 |
* Get the name for a user - hiding their real name if blind marking is on.
|
|
|
5037 |
*
|
|
|
5038 |
* @param stdClass $user The user record as required by fullname()
|
|
|
5039 |
* @return string The name.
|
|
|
5040 |
*/
|
1254 |
ariadna |
5041 |
public function fullname($user)
|
|
|
5042 |
{
|
1 |
efrain |
5043 |
if ($this->is_blind_marking()) {
|
|
|
5044 |
$hasviewblind = has_capability('mod/assign:viewblinddetails', $this->get_context());
|
|
|
5045 |
if (empty($user->recordid)) {
|
|
|
5046 |
$uniqueid = $this->get_uniqueid_for_user($user->id);
|
|
|
5047 |
} else {
|
|
|
5048 |
$uniqueid = $user->recordid;
|
|
|
5049 |
}
|
|
|
5050 |
if ($hasviewblind) {
|
|
|
5051 |
return get_string('participant', 'assign') . ' ' . $uniqueid . ' (' .
|
1254 |
ariadna |
5052 |
fullname($user, has_capability('moodle/site:viewfullnames', $this->get_context())) . ')';
|
1 |
efrain |
5053 |
} else {
|
|
|
5054 |
return get_string('participant', 'assign') . ' ' . $uniqueid;
|
|
|
5055 |
}
|
|
|
5056 |
} else {
|
|
|
5057 |
return fullname($user, has_capability('moodle/site:viewfullnames', $this->get_context()));
|
|
|
5058 |
}
|
|
|
5059 |
}
|
|
|
5060 |
|
|
|
5061 |
/**
|
|
|
5062 |
* View edit submissions page.
|
|
|
5063 |
*
|
|
|
5064 |
* @param moodleform $mform
|
|
|
5065 |
* @param array $notices A list of notices to display at the top of the
|
|
|
5066 |
* edit submission form (e.g. from plugins).
|
|
|
5067 |
* @return string The page output.
|
|
|
5068 |
*/
|
1254 |
ariadna |
5069 |
protected function view_edit_submission_page($mform, $notices)
|
|
|
5070 |
{
|
1 |
efrain |
5071 |
global $CFG, $USER, $DB, $PAGE;
|
|
|
5072 |
|
|
|
5073 |
$o = '';
|
|
|
5074 |
require_once($CFG->dirroot . '/mod/assign/submission_form.php');
|
|
|
5075 |
// Need submit permission to submit an assignment.
|
|
|
5076 |
$userid = optional_param('userid', $USER->id, PARAM_INT);
|
1254 |
ariadna |
5077 |
$user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
|
1 |
efrain |
5078 |
$timelimitenabled = get_config('assign', 'enabletimelimit');
|
|
|
5079 |
|
|
|
5080 |
// This variation on the url will link direct to this student.
|
|
|
5081 |
// The benefit is the url will be the same every time for this student, so Atto autosave drafts can match up.
|
|
|
5082 |
$returnparams = array('userid' => $userid, 'rownum' => 0, 'useridlistid' => 0);
|
|
|
5083 |
$this->register_return_link('editsubmission', $returnparams);
|
|
|
5084 |
|
|
|
5085 |
if ($userid == $USER->id) {
|
|
|
5086 |
if (!$this->can_edit_submission($userid, $USER->id)) {
|
|
|
5087 |
throw new \moodle_exception('nopermission');
|
|
|
5088 |
}
|
|
|
5089 |
// User is editing their own submission.
|
|
|
5090 |
require_capability('mod/assign:submit', $this->context);
|
|
|
5091 |
$title = get_string('editsubmission', 'assign');
|
|
|
5092 |
} else {
|
|
|
5093 |
// User is editing another user's submission.
|
|
|
5094 |
if (!$this->can_edit_submission($userid, $USER->id)) {
|
|
|
5095 |
throw new \moodle_exception('nopermission');
|
|
|
5096 |
}
|
|
|
5097 |
|
|
|
5098 |
$name = $this->fullname($user);
|
|
|
5099 |
$title = get_string('editsubmissionother', 'assign', $name);
|
|
|
5100 |
}
|
|
|
5101 |
|
|
|
5102 |
if (!$this->submissions_open($userid)) {
|
|
|
5103 |
$message = array(get_string('submissionsclosed', 'assign'));
|
|
|
5104 |
return $this->view_notices($title, $message);
|
|
|
5105 |
}
|
|
|
5106 |
|
|
|
5107 |
$postfix = '';
|
|
|
5108 |
if ($this->has_visible_attachments()) {
|
|
|
5109 |
$postfix = $this->render_area_files('mod_assign', ASSIGN_INTROATTACHMENT_FILEAREA, 0);
|
|
|
5110 |
}
|
|
|
5111 |
|
|
|
5112 |
$data = new stdClass();
|
|
|
5113 |
$data->userid = $userid;
|
|
|
5114 |
if (!$mform) {
|
|
|
5115 |
$mform = new mod_assign_submission_form(null, array($this, $data));
|
|
|
5116 |
}
|
|
|
5117 |
|
|
|
5118 |
if ($this->get_instance()->teamsubmission) {
|
|
|
5119 |
$submission = $this->get_group_submission($userid, 0, false);
|
|
|
5120 |
} else {
|
|
|
5121 |
$submission = $this->get_user_submission($userid, false);
|
|
|
5122 |
}
|
|
|
5123 |
|
|
|
5124 |
if ($timelimitenabled && !empty($submission->timestarted) && $this->get_instance()->timelimit) {
|
|
|
5125 |
$navbc = $this->get_timelimit_panel($submission);
|
|
|
5126 |
$regions = $PAGE->blocks->get_regions();
|
|
|
5127 |
$bc = new \block_contents();
|
|
|
5128 |
$bc->attributes['id'] = 'mod_assign_timelimit_block';
|
|
|
5129 |
$bc->attributes['role'] = 'navigation';
|
|
|
5130 |
$bc->attributes['aria-labelledby'] = 'mod_assign_timelimit_block_title';
|
|
|
5131 |
$bc->title = get_string('assigntimeleft', 'assign');
|
|
|
5132 |
$bc->content = $navbc;
|
|
|
5133 |
$PAGE->blocks->add_fake_block($bc, reset($regions));
|
|
|
5134 |
}
|
|
|
5135 |
|
|
|
5136 |
$o .= $this->get_renderer()->render(
|
1254 |
ariadna |
5137 |
new assign_header(
|
|
|
5138 |
$this->get_instance(),
|
|
|
5139 |
$this->get_context(),
|
|
|
5140 |
$this->show_intro(),
|
|
|
5141 |
$this->get_course_module()->id,
|
|
|
5142 |
$title,
|
|
|
5143 |
'',
|
|
|
5144 |
$postfix,
|
|
|
5145 |
null,
|
|
|
5146 |
true
|
1 |
efrain |
5147 |
)
|
|
|
5148 |
);
|
|
|
5149 |
|
|
|
5150 |
// Show plagiarism disclosure for any user submitter.
|
|
|
5151 |
$o .= $this->plagiarism_print_disclosure();
|
|
|
5152 |
|
|
|
5153 |
foreach ($notices as $notice) {
|
|
|
5154 |
$o .= $this->get_renderer()->notification($notice);
|
|
|
5155 |
}
|
|
|
5156 |
|
|
|
5157 |
$o .= $this->get_renderer()->render(new assign_form('editsubmissionform', $mform));
|
|
|
5158 |
$o .= $this->view_footer();
|
|
|
5159 |
|
|
|
5160 |
\mod_assign\event\submission_form_viewed::create_from_user($this, $user)->trigger();
|
|
|
5161 |
|
|
|
5162 |
return $o;
|
|
|
5163 |
}
|
|
|
5164 |
|
|
|
5165 |
/**
|
|
|
5166 |
* Get the time limit panel object for this submission attempt.
|
|
|
5167 |
*
|
|
|
5168 |
* @param stdClass $submission assign submission.
|
|
|
5169 |
* @return string the panel output.
|
|
|
5170 |
*/
|
1254 |
ariadna |
5171 |
public function get_timelimit_panel(stdClass $submission): string
|
|
|
5172 |
{
|
1 |
efrain |
5173 |
global $USER;
|
|
|
5174 |
|
|
|
5175 |
// Apply overrides.
|
|
|
5176 |
$this->update_effective_access($USER->id);
|
|
|
5177 |
$panel = new timelimit_panel($submission, $this->get_instance());
|
|
|
5178 |
return $this->get_renderer()->render($panel);
|
|
|
5179 |
}
|
|
|
5180 |
|
|
|
5181 |
/**
|
|
|
5182 |
* See if this assignment has a grade yet.
|
|
|
5183 |
*
|
|
|
5184 |
* @param int $userid
|
|
|
5185 |
* @return bool
|
|
|
5186 |
*/
|
1254 |
ariadna |
5187 |
protected function is_graded($userid)
|
|
|
5188 |
{
|
1 |
efrain |
5189 |
$grade = $this->get_user_grade($userid, false);
|
|
|
5190 |
if ($grade) {
|
|
|
5191 |
return ($grade->grade !== null && $grade->grade >= 0);
|
|
|
5192 |
}
|
|
|
5193 |
return false;
|
|
|
5194 |
}
|
|
|
5195 |
|
|
|
5196 |
/**
|
|
|
5197 |
* Perform an access check to see if the current $USER can edit this group submission.
|
|
|
5198 |
*
|
|
|
5199 |
* @param int $groupid
|
|
|
5200 |
* @return bool
|
|
|
5201 |
*/
|
1254 |
ariadna |
5202 |
public function can_edit_group_submission($groupid)
|
|
|
5203 |
{
|
1 |
efrain |
5204 |
global $USER;
|
|
|
5205 |
|
|
|
5206 |
$members = $this->get_submission_group_members($groupid, true);
|
|
|
5207 |
foreach ($members as $member) {
|
|
|
5208 |
// If we can edit any members submission, we can edit the submission for the group.
|
|
|
5209 |
if ($this->can_edit_submission($member->id)) {
|
|
|
5210 |
return true;
|
|
|
5211 |
}
|
|
|
5212 |
}
|
|
|
5213 |
return false;
|
|
|
5214 |
}
|
|
|
5215 |
|
|
|
5216 |
/**
|
|
|
5217 |
* Perform an access check to see if the current $USER can view this group submission.
|
|
|
5218 |
*
|
|
|
5219 |
* @param int $groupid
|
|
|
5220 |
* @return bool
|
|
|
5221 |
*/
|
1254 |
ariadna |
5222 |
public function can_view_group_submission($groupid)
|
|
|
5223 |
{
|
1 |
efrain |
5224 |
global $USER;
|
|
|
5225 |
|
|
|
5226 |
$members = $this->get_submission_group_members($groupid, true);
|
|
|
5227 |
foreach ($members as $member) {
|
|
|
5228 |
// If we can view any members submission, we can view the submission for the group.
|
|
|
5229 |
if ($this->can_view_submission($member->id)) {
|
|
|
5230 |
return true;
|
|
|
5231 |
}
|
|
|
5232 |
}
|
|
|
5233 |
return false;
|
|
|
5234 |
}
|
|
|
5235 |
|
|
|
5236 |
/**
|
|
|
5237 |
* Perform an access check to see if the current $USER can view this users submission.
|
|
|
5238 |
*
|
|
|
5239 |
* @param int $userid
|
|
|
5240 |
* @return bool
|
|
|
5241 |
*/
|
1254 |
ariadna |
5242 |
public function can_view_submission($userid)
|
|
|
5243 |
{
|
1 |
efrain |
5244 |
global $USER;
|
|
|
5245 |
|
|
|
5246 |
if (!$this->is_active_user($userid) && !has_capability('moodle/course:viewsuspendedusers', $this->context)) {
|
|
|
5247 |
return false;
|
|
|
5248 |
}
|
|
|
5249 |
if (!is_enrolled($this->get_course_context(), $userid)) {
|
|
|
5250 |
return false;
|
|
|
5251 |
}
|
|
|
5252 |
if (has_any_capability(array('mod/assign:viewgrades', 'mod/assign:grade'), $this->context)) {
|
|
|
5253 |
return true;
|
|
|
5254 |
}
|
|
|
5255 |
if ($userid == $USER->id) {
|
|
|
5256 |
return true;
|
|
|
5257 |
}
|
|
|
5258 |
return false;
|
|
|
5259 |
}
|
|
|
5260 |
|
|
|
5261 |
/**
|
|
|
5262 |
* Allows the plugin to show a batch grading operation page.
|
|
|
5263 |
*
|
|
|
5264 |
* @param moodleform $mform
|
|
|
5265 |
* @return none
|
|
|
5266 |
*/
|
1254 |
ariadna |
5267 |
protected function view_plugin_grading_batch_operation($mform)
|
|
|
5268 |
{
|
1 |
efrain |
5269 |
require_capability('mod/assign:grade', $this->context);
|
|
|
5270 |
$prefix = 'plugingradingbatchoperation_';
|
|
|
5271 |
|
|
|
5272 |
if ($data = $mform->get_data()) {
|
|
|
5273 |
$tail = substr($data->operation, strlen($prefix));
|
|
|
5274 |
list($plugintype, $action) = explode('_', $tail, 2);
|
|
|
5275 |
|
|
|
5276 |
$plugin = $this->get_feedback_plugin_by_type($plugintype);
|
|
|
5277 |
if ($plugin) {
|
|
|
5278 |
$users = $data->selectedusers;
|
|
|
5279 |
$userlist = explode(',', $users);
|
|
|
5280 |
echo $plugin->grading_batch_operation($action, $userlist);
|
|
|
5281 |
return;
|
|
|
5282 |
}
|
|
|
5283 |
}
|
|
|
5284 |
throw new \moodle_exception('invalidformdata', '');
|
|
|
5285 |
}
|
|
|
5286 |
|
|
|
5287 |
/**
|
|
|
5288 |
* Ask the user to confirm they want to perform this batch operation
|
|
|
5289 |
*
|
|
|
5290 |
* @param moodleform $mform Set to a grading batch operations form
|
|
|
5291 |
* @return string - the page to view after processing these actions
|
|
|
5292 |
*/
|
1254 |
ariadna |
5293 |
protected function process_grading_batch_operation(&$mform)
|
|
|
5294 |
{
|
1 |
efrain |
5295 |
global $CFG;
|
|
|
5296 |
require_once($CFG->dirroot . '/mod/assign/gradingbatchoperationsform.php');
|
|
|
5297 |
require_sesskey();
|
|
|
5298 |
|
|
|
5299 |
$markingallocation = $this->get_instance()->markingworkflow &&
|
|
|
5300 |
$this->get_instance()->markingallocation &&
|
|
|
5301 |
has_capability('mod/assign:manageallocations', $this->context);
|
|
|
5302 |
|
1254 |
ariadna |
5303 |
$batchformparams = array(
|
|
|
5304 |
'cm' => $this->get_course_module()->id,
|
|
|
5305 |
'submissiondrafts' => $this->get_instance()->submissiondrafts,
|
|
|
5306 |
'duedate' => $this->get_instance()->duedate,
|
|
|
5307 |
'attemptreopenmethod' => $this->get_instance()->attemptreopenmethod,
|
|
|
5308 |
'feedbackplugins' => $this->get_feedback_plugins(),
|
|
|
5309 |
'context' => $this->get_context(),
|
|
|
5310 |
'markingworkflow' => $this->get_instance()->markingworkflow,
|
|
|
5311 |
'markingallocation' => $markingallocation
|
|
|
5312 |
);
|
1 |
efrain |
5313 |
$formclasses = [
|
|
|
5314 |
'class' => 'gradingbatchoperationsform',
|
|
|
5315 |
'data-double-submit-protection' => 'off'
|
|
|
5316 |
];
|
|
|
5317 |
|
1254 |
ariadna |
5318 |
$mform = new mod_assign_grading_batch_operations_form(
|
|
|
5319 |
null,
|
|
|
5320 |
$batchformparams,
|
|
|
5321 |
'post',
|
|
|
5322 |
'',
|
|
|
5323 |
$formclasses
|
|
|
5324 |
);
|
1 |
efrain |
5325 |
|
|
|
5326 |
if ($data = $mform->get_data()) {
|
|
|
5327 |
// Get the list of users.
|
|
|
5328 |
$users = $data->selectedusers;
|
|
|
5329 |
$userlist = explode(',', $users);
|
|
|
5330 |
|
|
|
5331 |
$prefix = 'plugingradingbatchoperation_';
|
|
|
5332 |
|
|
|
5333 |
if ($data->operation == 'grantextension') {
|
|
|
5334 |
// Reset the form so the grant extension page will create the extension form.
|
|
|
5335 |
$mform = null;
|
|
|
5336 |
return 'grantextension';
|
|
|
5337 |
} else if ($data->operation == 'setmarkingworkflowstate') {
|
|
|
5338 |
return 'viewbatchsetmarkingworkflowstate';
|
|
|
5339 |
} else if ($data->operation == 'setmarkingallocation') {
|
|
|
5340 |
return 'viewbatchmarkingallocation';
|
|
|
5341 |
} else if (strpos($data->operation, $prefix) === 0) {
|
|
|
5342 |
$tail = substr($data->operation, strlen($prefix));
|
|
|
5343 |
list($plugintype, $action) = explode('_', $tail, 2);
|
|
|
5344 |
|
|
|
5345 |
$plugin = $this->get_feedback_plugin_by_type($plugintype);
|
|
|
5346 |
if ($plugin) {
|
|
|
5347 |
return 'plugingradingbatchoperation';
|
|
|
5348 |
}
|
|
|
5349 |
}
|
|
|
5350 |
|
|
|
5351 |
if ($data->operation == 'downloadselected') {
|
|
|
5352 |
$this->download_submissions($userlist);
|
|
|
5353 |
} else {
|
|
|
5354 |
foreach ($userlist as $userid) {
|
|
|
5355 |
if ($data->operation == 'lock') {
|
|
|
5356 |
$this->process_lock_submission($userid);
|
|
|
5357 |
} else if ($data->operation == 'unlock') {
|
|
|
5358 |
$this->process_unlock_submission($userid);
|
|
|
5359 |
} else if ($data->operation == 'reverttodraft') {
|
|
|
5360 |
$this->process_revert_to_draft($userid);
|
|
|
5361 |
} else if ($data->operation == 'removesubmission') {
|
|
|
5362 |
$this->process_remove_submission($userid);
|
|
|
5363 |
} else if ($data->operation == 'addattempt') {
|
|
|
5364 |
if (!$this->get_instance()->teamsubmission) {
|
|
|
5365 |
$this->process_add_attempt($userid);
|
|
|
5366 |
}
|
|
|
5367 |
}
|
|
|
5368 |
}
|
|
|
5369 |
}
|
|
|
5370 |
if ($this->get_instance()->teamsubmission && $data->operation == 'addattempt') {
|
|
|
5371 |
// This needs to be handled separately so that each team submission is only re-opened one time.
|
|
|
5372 |
$this->process_add_attempt_group($userlist);
|
|
|
5373 |
}
|
|
|
5374 |
}
|
|
|
5375 |
|
|
|
5376 |
return 'grading';
|
|
|
5377 |
}
|
|
|
5378 |
|
|
|
5379 |
/**
|
|
|
5380 |
* Shows a form that allows the workflow state for selected submissions to be changed.
|
|
|
5381 |
*
|
|
|
5382 |
* @param moodleform $mform Set to a grading batch operations form
|
|
|
5383 |
* @return string - the page to view after processing these actions
|
|
|
5384 |
*/
|
1254 |
ariadna |
5385 |
protected function view_batch_set_workflow_state($mform)
|
|
|
5386 |
{
|
1 |
efrain |
5387 |
global $CFG, $DB;
|
|
|
5388 |
|
|
|
5389 |
require_once($CFG->dirroot . '/mod/assign/batchsetmarkingworkflowstateform.php');
|
|
|
5390 |
|
|
|
5391 |
$o = '';
|
|
|
5392 |
|
|
|
5393 |
$submitteddata = $mform->get_data();
|
|
|
5394 |
$users = $submitteddata->selectedusers;
|
|
|
5395 |
$userlist = explode(',', $users);
|
|
|
5396 |
|
1254 |
ariadna |
5397 |
$formdata = array(
|
|
|
5398 |
'id' => $this->get_course_module()->id,
|
|
|
5399 |
'selectedusers' => $users
|
|
|
5400 |
);
|
1 |
efrain |
5401 |
|
|
|
5402 |
$usershtml = '';
|
|
|
5403 |
|
|
|
5404 |
$usercount = 0;
|
|
|
5405 |
// TODO Does not support custom user profile fields (MDL-70456).
|
|
|
5406 |
$extrauserfields = \core_user\fields::get_identity_fields($this->get_context(), false);
|
|
|
5407 |
$viewfullnames = has_capability('moodle/site:viewfullnames', $this->get_context());
|
|
|
5408 |
foreach ($userlist as $userid) {
|
|
|
5409 |
if ($usercount >= 5) {
|
|
|
5410 |
$usershtml .= get_string('moreusers', 'assign', count($userlist) - 5);
|
|
|
5411 |
break;
|
|
|
5412 |
}
|
1254 |
ariadna |
5413 |
$user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
|
1 |
efrain |
5414 |
|
1254 |
ariadna |
5415 |
$usershtml .= $this->get_renderer()->render(new assign_user_summary(
|
|
|
5416 |
$user,
|
|
|
5417 |
$this->get_course()->id,
|
|
|
5418 |
$viewfullnames,
|
|
|
5419 |
$this->is_blind_marking(),
|
|
|
5420 |
$this->get_uniqueid_for_user($user->id),
|
|
|
5421 |
$extrauserfields,
|
|
|
5422 |
!$this->is_active_user($userid)
|
|
|
5423 |
));
|
1 |
efrain |
5424 |
$usercount += 1;
|
|
|
5425 |
}
|
|
|
5426 |
|
|
|
5427 |
$formparams = array(
|
|
|
5428 |
'userscount' => count($userlist),
|
|
|
5429 |
'usershtml' => $usershtml,
|
|
|
5430 |
'markingworkflowstates' => $this->get_marking_workflow_states_for_current_user()
|
|
|
5431 |
);
|
|
|
5432 |
|
|
|
5433 |
$mform = new mod_assign_batch_set_marking_workflow_state_form(null, $formparams);
|
|
|
5434 |
$mform->set_data($formdata); // Initialises the hidden elements.
|
1254 |
ariadna |
5435 |
$header = new assign_header(
|
|
|
5436 |
$this->get_instance(),
|
1 |
efrain |
5437 |
$this->get_context(),
|
|
|
5438 |
$this->show_intro(),
|
|
|
5439 |
$this->get_course_module()->id,
|
1254 |
ariadna |
5440 |
get_string('setmarkingworkflowstate', 'assign')
|
|
|
5441 |
);
|
1 |
efrain |
5442 |
$o .= $this->get_renderer()->render($header);
|
|
|
5443 |
$o .= $this->get_renderer()->render(new assign_form('setworkflowstate', $mform));
|
|
|
5444 |
$o .= $this->view_footer();
|
|
|
5445 |
|
|
|
5446 |
\mod_assign\event\batch_set_workflow_state_viewed::create_from_assign($this)->trigger();
|
|
|
5447 |
|
|
|
5448 |
return $o;
|
|
|
5449 |
}
|
|
|
5450 |
|
|
|
5451 |
/**
|
|
|
5452 |
* Shows a form that allows the allocated marker for selected submissions to be changed.
|
|
|
5453 |
*
|
|
|
5454 |
* @param moodleform $mform Set to a grading batch operations form
|
|
|
5455 |
* @return string - the page to view after processing these actions
|
|
|
5456 |
*/
|
1254 |
ariadna |
5457 |
public function view_batch_markingallocation($mform)
|
|
|
5458 |
{
|
1 |
efrain |
5459 |
global $CFG, $DB;
|
|
|
5460 |
|
|
|
5461 |
require_once($CFG->dirroot . '/mod/assign/batchsetallocatedmarkerform.php');
|
|
|
5462 |
|
|
|
5463 |
$o = '';
|
|
|
5464 |
|
|
|
5465 |
$submitteddata = $mform->get_data();
|
|
|
5466 |
$users = $submitteddata->selectedusers;
|
|
|
5467 |
$userlist = explode(',', $users);
|
|
|
5468 |
|
1254 |
ariadna |
5469 |
$formdata = array(
|
|
|
5470 |
'id' => $this->get_course_module()->id,
|
|
|
5471 |
'selectedusers' => $users
|
|
|
5472 |
);
|
1 |
efrain |
5473 |
|
|
|
5474 |
$usershtml = '';
|
|
|
5475 |
|
|
|
5476 |
$usercount = 0;
|
|
|
5477 |
// TODO Does not support custom user profile fields (MDL-70456).
|
|
|
5478 |
$extrauserfields = \core_user\fields::get_identity_fields($this->get_context(), false);
|
|
|
5479 |
$viewfullnames = has_capability('moodle/site:viewfullnames', $this->get_context());
|
|
|
5480 |
foreach ($userlist as $userid) {
|
|
|
5481 |
if ($usercount >= 5) {
|
|
|
5482 |
$usershtml .= get_string('moreusers', 'assign', count($userlist) - 5);
|
|
|
5483 |
break;
|
|
|
5484 |
}
|
1254 |
ariadna |
5485 |
$user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
|
1 |
efrain |
5486 |
|
1254 |
ariadna |
5487 |
$usershtml .= $this->get_renderer()->render(new assign_user_summary(
|
|
|
5488 |
$user,
|
1 |
efrain |
5489 |
$this->get_course()->id,
|
|
|
5490 |
$viewfullnames,
|
|
|
5491 |
$this->is_blind_marking(),
|
|
|
5492 |
$this->get_uniqueid_for_user($user->id),
|
|
|
5493 |
$extrauserfields,
|
1254 |
ariadna |
5494 |
!$this->is_active_user($userid)
|
|
|
5495 |
));
|
1 |
efrain |
5496 |
$usercount += 1;
|
|
|
5497 |
}
|
|
|
5498 |
|
|
|
5499 |
$formparams = array(
|
|
|
5500 |
'userscount' => count($userlist),
|
|
|
5501 |
'usershtml' => $usershtml,
|
|
|
5502 |
);
|
|
|
5503 |
|
|
|
5504 |
list($sort, $params) = users_order_by_sql('u');
|
|
|
5505 |
// Only enrolled users could be assigned as potential markers.
|
|
|
5506 |
$markers = get_enrolled_users($this->get_context(), 'mod/assign:grade', 0, 'u.*', $sort);
|
|
|
5507 |
$markerlist = array();
|
|
|
5508 |
foreach ($markers as $marker) {
|
|
|
5509 |
$markerlist[$marker->id] = fullname($marker);
|
|
|
5510 |
}
|
|
|
5511 |
|
|
|
5512 |
$formparams['markers'] = $markerlist;
|
|
|
5513 |
|
|
|
5514 |
$mform = new mod_assign_batch_set_allocatedmarker_form(null, $formparams);
|
|
|
5515 |
$mform->set_data($formdata); // Initialises the hidden elements.
|
1254 |
ariadna |
5516 |
$header = new assign_header(
|
|
|
5517 |
$this->get_instance(),
|
1 |
efrain |
5518 |
$this->get_context(),
|
|
|
5519 |
$this->show_intro(),
|
|
|
5520 |
$this->get_course_module()->id,
|
1254 |
ariadna |
5521 |
get_string('setmarkingallocation', 'assign')
|
|
|
5522 |
);
|
1 |
efrain |
5523 |
$o .= $this->get_renderer()->render($header);
|
|
|
5524 |
$o .= $this->get_renderer()->render(new assign_form('setworkflowstate', $mform));
|
|
|
5525 |
$o .= $this->view_footer();
|
|
|
5526 |
|
|
|
5527 |
\mod_assign\event\batch_set_marker_allocation_viewed::create_from_assign($this)->trigger();
|
|
|
5528 |
|
|
|
5529 |
return $o;
|
|
|
5530 |
}
|
|
|
5531 |
|
|
|
5532 |
/**
|
|
|
5533 |
* Ask the user to confirm they want to submit their work for grading.
|
|
|
5534 |
*
|
|
|
5535 |
* @param moodleform $mform - null unless form validation has failed
|
|
|
5536 |
* @return string
|
|
|
5537 |
*/
|
1254 |
ariadna |
5538 |
protected function check_submit_for_grading($mform)
|
|
|
5539 |
{
|
1 |
efrain |
5540 |
global $USER, $CFG;
|
|
|
5541 |
|
|
|
5542 |
require_once($CFG->dirroot . '/mod/assign/submissionconfirmform.php');
|
|
|
5543 |
|
|
|
5544 |
// Check that all of the submission plugins are ready for this submission.
|
|
|
5545 |
// Also check whether there is something to be submitted as well against atleast one.
|
|
|
5546 |
$notifications = array();
|
|
|
5547 |
$submission = $this->get_user_submission($USER->id, false);
|
|
|
5548 |
if ($this->get_instance()->teamsubmission) {
|
|
|
5549 |
$submission = $this->get_group_submission($USER->id, 0, false);
|
|
|
5550 |
}
|
|
|
5551 |
|
|
|
5552 |
$plugins = $this->get_submission_plugins();
|
|
|
5553 |
$hassubmission = false;
|
|
|
5554 |
foreach ($plugins as $plugin) {
|
|
|
5555 |
if ($plugin->is_enabled() && $plugin->is_visible()) {
|
|
|
5556 |
$check = $plugin->precheck_submission($submission);
|
|
|
5557 |
if ($check !== true) {
|
|
|
5558 |
$notifications[] = $check;
|
|
|
5559 |
}
|
|
|
5560 |
|
|
|
5561 |
if (is_object($submission) && !$plugin->is_empty($submission)) {
|
|
|
5562 |
$hassubmission = true;
|
|
|
5563 |
}
|
|
|
5564 |
}
|
|
|
5565 |
}
|
|
|
5566 |
|
|
|
5567 |
// If there are no submissions and no existing notifications to be displayed the stop.
|
|
|
5568 |
if (!$hassubmission && !$notifications) {
|
|
|
5569 |
$notifications[] = get_string('addsubmission_help', 'assign');
|
|
|
5570 |
}
|
|
|
5571 |
|
|
|
5572 |
$data = new stdClass();
|
|
|
5573 |
$adminconfig = $this->get_admin_config();
|
|
|
5574 |
$requiresubmissionstatement = $this->get_instance()->requiresubmissionstatement;
|
|
|
5575 |
$submissionstatement = '';
|
|
|
5576 |
|
|
|
5577 |
if ($requiresubmissionstatement) {
|
|
|
5578 |
$submissionstatement = $this->get_submissionstatement($adminconfig, $this->get_instance(), $this->get_context());
|
|
|
5579 |
}
|
|
|
5580 |
|
|
|
5581 |
// If we get back an empty submission statement, we have to set $requiredsubmisisonstatement to false to prevent
|
|
|
5582 |
// that the submission statement checkbox will be displayed.
|
|
|
5583 |
if (empty($submissionstatement)) {
|
|
|
5584 |
$requiresubmissionstatement = false;
|
|
|
5585 |
}
|
|
|
5586 |
|
|
|
5587 |
if ($mform == null) {
|
1254 |
ariadna |
5588 |
$mform = new mod_assign_confirm_submission_form(null, array(
|
|
|
5589 |
$requiresubmissionstatement,
|
|
|
5590 |
$submissionstatement,
|
|
|
5591 |
$this->get_course_module()->id,
|
|
|
5592 |
$data
|
|
|
5593 |
));
|
1 |
efrain |
5594 |
}
|
|
|
5595 |
$o = '';
|
1254 |
ariadna |
5596 |
$o .= $this->get_renderer()->render(new assign_header(
|
|
|
5597 |
$this->get_instance(),
|
|
|
5598 |
$this->get_context(),
|
|
|
5599 |
$this->show_intro(),
|
|
|
5600 |
$this->get_course_module()->id,
|
|
|
5601 |
get_string('confirmsubmissionheading', 'assign')
|
|
|
5602 |
));
|
|
|
5603 |
$submitforgradingpage = new assign_submit_for_grading_page(
|
|
|
5604 |
$notifications,
|
|
|
5605 |
$this->get_course_module()->id,
|
|
|
5606 |
$mform
|
|
|
5607 |
);
|
1 |
efrain |
5608 |
$o .= $this->get_renderer()->render($submitforgradingpage);
|
|
|
5609 |
$o .= $this->view_footer();
|
|
|
5610 |
|
|
|
5611 |
\mod_assign\event\submission_confirmation_form_viewed::create_from_assign($this)->trigger();
|
|
|
5612 |
|
|
|
5613 |
return $o;
|
|
|
5614 |
}
|
|
|
5615 |
|
|
|
5616 |
/**
|
|
|
5617 |
* Creates an assign_submission_status renderable.
|
|
|
5618 |
*
|
|
|
5619 |
* @param stdClass $user the user to get the report for
|
|
|
5620 |
* @param bool $showlinks return plain text or links to the profile
|
|
|
5621 |
* @return assign_submission_status renderable object
|
|
|
5622 |
*/
|
1254 |
ariadna |
5623 |
public function get_assign_submission_status_renderable($user, $showlinks)
|
|
|
5624 |
{
|
1 |
efrain |
5625 |
global $PAGE;
|
|
|
5626 |
|
|
|
5627 |
$instance = $this->get_instance();
|
|
|
5628 |
$flags = $this->get_user_flags($user->id, false);
|
|
|
5629 |
$submission = $this->get_user_submission($user->id, false);
|
|
|
5630 |
|
|
|
5631 |
$teamsubmission = null;
|
|
|
5632 |
$submissiongroup = null;
|
|
|
5633 |
$notsubmitted = array();
|
|
|
5634 |
if ($instance->teamsubmission) {
|
|
|
5635 |
$teamsubmission = $this->get_group_submission($user->id, 0, false);
|
|
|
5636 |
$submissiongroup = $this->get_submission_group($user->id);
|
|
|
5637 |
$groupid = 0;
|
|
|
5638 |
if ($submissiongroup) {
|
|
|
5639 |
$groupid = $submissiongroup->id;
|
|
|
5640 |
}
|
|
|
5641 |
$notsubmitted = $this->get_submission_group_members_who_have_not_submitted($groupid, false);
|
|
|
5642 |
}
|
|
|
5643 |
|
|
|
5644 |
$showedit = $showlinks &&
|
1254 |
ariadna |
5645 |
($this->is_any_submission_plugin_enabled()) &&
|
|
|
5646 |
$this->can_edit_submission($user->id);
|
1 |
efrain |
5647 |
|
|
|
5648 |
$submissionlocked = ($flags && $flags->locked);
|
|
|
5649 |
|
|
|
5650 |
// Grading criteria preview.
|
|
|
5651 |
$gradingmanager = get_grading_manager($this->context, 'mod_assign', 'submissions');
|
|
|
5652 |
$gradingcontrollerpreview = '';
|
|
|
5653 |
if ($gradingmethod = $gradingmanager->get_active_method()) {
|
|
|
5654 |
$controller = $gradingmanager->get_controller($gradingmethod);
|
|
|
5655 |
if ($controller->is_form_defined()) {
|
|
|
5656 |
$gradingcontrollerpreview = $controller->render_preview($PAGE);
|
|
|
5657 |
}
|
|
|
5658 |
}
|
|
|
5659 |
|
|
|
5660 |
$showsubmit = ($showlinks && $this->submissions_open($user->id));
|
|
|
5661 |
$showsubmit = ($showsubmit && $this->show_submit_button($submission, $teamsubmission, $user->id));
|
|
|
5662 |
|
|
|
5663 |
$extensionduedate = null;
|
|
|
5664 |
if ($flags) {
|
|
|
5665 |
$extensionduedate = $flags->extensionduedate;
|
|
|
5666 |
}
|
|
|
5667 |
$viewfullnames = has_capability('moodle/site:viewfullnames', $this->get_context());
|
|
|
5668 |
|
|
|
5669 |
$gradingstatus = $this->get_grading_status($user->id);
|
|
|
5670 |
$usergroups = $this->get_all_groups($user->id);
|
1254 |
ariadna |
5671 |
$submissionstatus = new assign_submission_status(
|
|
|
5672 |
$instance->allowsubmissionsfromdate,
|
|
|
5673 |
$instance->alwaysshowdescription,
|
|
|
5674 |
$submission,
|
|
|
5675 |
$instance->teamsubmission,
|
|
|
5676 |
$teamsubmission,
|
|
|
5677 |
$submissiongroup,
|
|
|
5678 |
$notsubmitted,
|
|
|
5679 |
$this->is_any_submission_plugin_enabled(),
|
|
|
5680 |
$submissionlocked,
|
|
|
5681 |
$this->is_graded($user->id),
|
|
|
5682 |
$instance->duedate,
|
|
|
5683 |
$instance->cutoffdate,
|
|
|
5684 |
$this->get_submission_plugins(),
|
|
|
5685 |
$this->get_return_action(),
|
|
|
5686 |
$this->get_return_params(),
|
|
|
5687 |
$this->get_course_module()->id,
|
|
|
5688 |
$this->get_course()->id,
|
|
|
5689 |
assign_submission_status::STUDENT_VIEW,
|
|
|
5690 |
$showedit,
|
|
|
5691 |
$showsubmit,
|
|
|
5692 |
$viewfullnames,
|
|
|
5693 |
$extensionduedate,
|
|
|
5694 |
$this->get_context(),
|
|
|
5695 |
$this->is_blind_marking(),
|
|
|
5696 |
$gradingcontrollerpreview,
|
|
|
5697 |
$instance->attemptreopenmethod,
|
|
|
5698 |
$instance->maxattempts,
|
|
|
5699 |
$gradingstatus,
|
|
|
5700 |
$instance->preventsubmissionnotingroup,
|
|
|
5701 |
$usergroups,
|
|
|
5702 |
$instance->timelimit
|
|
|
5703 |
);
|
1 |
efrain |
5704 |
return $submissionstatus;
|
|
|
5705 |
}
|
|
|
5706 |
|
|
|
5707 |
|
|
|
5708 |
/**
|
|
|
5709 |
* Creates an assign_feedback_status renderable.
|
|
|
5710 |
*
|
|
|
5711 |
* @param stdClass $user the user to get the report for
|
|
|
5712 |
* @return assign_feedback_status renderable object
|
|
|
5713 |
*/
|
1254 |
ariadna |
5714 |
public function get_assign_feedback_status_renderable($user)
|
|
|
5715 |
{
|
1 |
efrain |
5716 |
global $CFG, $DB, $PAGE;
|
|
|
5717 |
|
1254 |
ariadna |
5718 |
require_once($CFG->libdir . '/gradelib.php');
|
|
|
5719 |
require_once($CFG->dirroot . '/grade/grading/lib.php');
|
1 |
efrain |
5720 |
|
|
|
5721 |
$instance = $this->get_instance();
|
|
|
5722 |
$grade = $this->get_user_grade($user->id, false);
|
|
|
5723 |
$gradingstatus = $this->get_grading_status($user->id);
|
|
|
5724 |
|
1254 |
ariadna |
5725 |
$gradinginfo = grade_get_grades(
|
|
|
5726 |
$this->get_course()->id,
|
|
|
5727 |
'mod',
|
|
|
5728 |
'assign',
|
|
|
5729 |
$instance->id,
|
|
|
5730 |
$user->id
|
|
|
5731 |
);
|
1 |
efrain |
5732 |
|
|
|
5733 |
$gradingitem = null;
|
|
|
5734 |
$gradebookgrade = null;
|
|
|
5735 |
if (isset($gradinginfo->items[0])) {
|
|
|
5736 |
$gradingitem = $gradinginfo->items[0];
|
|
|
5737 |
$gradebookgrade = $gradingitem->grades[$user->id];
|
|
|
5738 |
}
|
|
|
5739 |
|
|
|
5740 |
// Check to see if all feedback plugins are empty.
|
|
|
5741 |
$emptyplugins = true;
|
|
|
5742 |
if ($grade) {
|
|
|
5743 |
foreach ($this->get_feedback_plugins() as $plugin) {
|
|
|
5744 |
if ($plugin->is_visible() && $plugin->is_enabled()) {
|
|
|
5745 |
if (!$plugin->is_empty($grade)) {
|
|
|
5746 |
$emptyplugins = false;
|
|
|
5747 |
}
|
|
|
5748 |
}
|
|
|
5749 |
}
|
|
|
5750 |
}
|
|
|
5751 |
|
|
|
5752 |
if ($this->get_instance()->markingworkflow && $gradingstatus != ASSIGN_MARKING_WORKFLOW_STATE_RELEASED) {
|
|
|
5753 |
$emptyplugins = true; // Don't show feedback plugins until released either.
|
|
|
5754 |
}
|
|
|
5755 |
|
|
|
5756 |
$cangrade = has_capability('mod/assign:grade', $this->get_context());
|
|
|
5757 |
$hasgrade = $this->get_instance()->grade != GRADE_TYPE_NONE &&
|
1254 |
ariadna |
5758 |
!is_null($gradebookgrade) && !is_null($gradebookgrade->grade);
|
1 |
efrain |
5759 |
$gradevisible = $cangrade || $this->get_instance()->grade == GRADE_TYPE_NONE ||
|
1254 |
ariadna |
5760 |
(!is_null($gradebookgrade) && !$gradebookgrade->hidden);
|
1 |
efrain |
5761 |
// If there is a visible grade, show the summary.
|
|
|
5762 |
if (($hasgrade || !$emptyplugins) && $gradevisible) {
|
|
|
5763 |
|
|
|
5764 |
$gradefordisplay = null;
|
|
|
5765 |
$gradeddate = null;
|
|
|
5766 |
$grader = null;
|
|
|
5767 |
$gradingmanager = get_grading_manager($this->get_context(), 'mod_assign', 'submissions');
|
|
|
5768 |
|
|
|
5769 |
$gradingcontrollergrade = '';
|
|
|
5770 |
if ($hasgrade) {
|
|
|
5771 |
if ($controller = $gradingmanager->get_active_controller()) {
|
|
|
5772 |
$menu = make_grades_menu($this->get_instance()->grade);
|
|
|
5773 |
$controller->set_grade_range($menu, $this->get_instance()->grade > 0);
|
|
|
5774 |
$gradingcontrollergrade = $controller->render_grade(
|
|
|
5775 |
$PAGE,
|
|
|
5776 |
$grade->id,
|
|
|
5777 |
$gradingitem,
|
|
|
5778 |
'',
|
|
|
5779 |
$cangrade
|
|
|
5780 |
);
|
|
|
5781 |
$gradefordisplay = $gradebookgrade->str_long_grade;
|
|
|
5782 |
} else {
|
|
|
5783 |
$gradefordisplay = $this->display_grade($gradebookgrade->grade, false);
|
|
|
5784 |
}
|
|
|
5785 |
$gradeddate = $gradebookgrade->dategraded;
|
|
|
5786 |
|
|
|
5787 |
// Show the grader's identity if 'Hide Grader' is disabled or has the 'Show Hidden Grader' capability.
|
|
|
5788 |
if (has_capability('mod/assign:showhiddengrader', $this->context) || !$this->is_hidden_grader()) {
|
|
|
5789 |
// Only display the grader if it is in the right state.
|
|
|
5790 |
if (in_array($gradingstatus, [ASSIGN_GRADING_STATUS_GRADED, ASSIGN_MARKING_WORKFLOW_STATE_RELEASED])) {
|
|
|
5791 |
if (isset($grade->grader) && $grade->grader > 0) {
|
|
|
5792 |
$grader = $DB->get_record('user', array('id' => $grade->grader));
|
1254 |
ariadna |
5793 |
} else if (
|
|
|
5794 |
isset($gradebookgrade->usermodified)
|
1 |
efrain |
5795 |
&& $gradebookgrade->usermodified > 0
|
1254 |
ariadna |
5796 |
&& has_capability('mod/assign:grade', $this->get_context(), $gradebookgrade->usermodified)
|
|
|
5797 |
) {
|
1 |
efrain |
5798 |
// Grader not provided. Check that usermodified is a user who can grade.
|
|
|
5799 |
// Case 1: When an assignment is reopened an empty assign_grade is created so the feedback
|
|
|
5800 |
// plugin can know which attempt it's referring to. In this case, usermodifed is a student.
|
|
|
5801 |
// Case 2: When an assignment's grade is overrided via the gradebook, usermodified is a grader.
|
|
|
5802 |
$grader = $DB->get_record('user', array('id' => $gradebookgrade->usermodified));
|
|
|
5803 |
}
|
|
|
5804 |
}
|
|
|
5805 |
}
|
|
|
5806 |
}
|
|
|
5807 |
|
|
|
5808 |
$viewfullnames = has_capability('moodle/site:viewfullnames', $this->get_context());
|
|
|
5809 |
|
|
|
5810 |
if ($grade) {
|
|
|
5811 |
\mod_assign\event\feedback_viewed::create_from_grade($this, $grade)->trigger();
|
|
|
5812 |
}
|
|
|
5813 |
$feedbackstatus = new assign_feedback_status(
|
|
|
5814 |
$gradefordisplay,
|
|
|
5815 |
$gradeddate,
|
|
|
5816 |
$grader,
|
|
|
5817 |
$this->get_feedback_plugins(),
|
|
|
5818 |
$grade,
|
|
|
5819 |
$this->get_course_module()->id,
|
|
|
5820 |
$this->get_return_action(),
|
|
|
5821 |
$this->get_return_params(),
|
|
|
5822 |
$viewfullnames,
|
|
|
5823 |
$gradingcontrollergrade
|
|
|
5824 |
);
|
|
|
5825 |
|
|
|
5826 |
return $feedbackstatus;
|
|
|
5827 |
}
|
|
|
5828 |
return;
|
|
|
5829 |
}
|
|
|
5830 |
|
|
|
5831 |
/**
|
|
|
5832 |
* Creates an assign_attempt_history renderable.
|
|
|
5833 |
*
|
|
|
5834 |
* @param stdClass $user the user to get the report for
|
|
|
5835 |
* @return assign_attempt_history renderable object
|
|
|
5836 |
*/
|
1254 |
ariadna |
5837 |
public function get_assign_attempt_history_renderable($user)
|
|
|
5838 |
{
|
1 |
efrain |
5839 |
|
|
|
5840 |
$allsubmissions = $this->get_all_submissions($user->id);
|
|
|
5841 |
$allgrades = $this->get_all_grades($user->id);
|
|
|
5842 |
|
1254 |
ariadna |
5843 |
$history = new assign_attempt_history(
|
|
|
5844 |
$allsubmissions,
|
|
|
5845 |
$allgrades,
|
|
|
5846 |
$this->get_submission_plugins(),
|
|
|
5847 |
$this->get_feedback_plugins(),
|
|
|
5848 |
$this->get_course_module()->id,
|
|
|
5849 |
$this->get_return_action(),
|
|
|
5850 |
$this->get_return_params(),
|
|
|
5851 |
false,
|
|
|
5852 |
0,
|
|
|
5853 |
|
|
|
5854 |
);
|
1 |
efrain |
5855 |
return $history;
|
|
|
5856 |
}
|
|
|
5857 |
|
|
|
5858 |
/**
|
|
|
5859 |
* Print 2 tables of information with no action links -
|
|
|
5860 |
* the submission summary and the grading summary.
|
|
|
5861 |
*
|
|
|
5862 |
* @param stdClass $user the user to print the report for
|
|
|
5863 |
* @param bool $showlinks - Return plain text or links to the profile
|
|
|
5864 |
* @return string - the html summary
|
|
|
5865 |
*/
|
1254 |
ariadna |
5866 |
public function view_student_summary($user, $showlinks)
|
|
|
5867 |
{
|
1 |
efrain |
5868 |
|
|
|
5869 |
$o = '';
|
|
|
5870 |
|
|
|
5871 |
if ($this->can_view_submission($user->id)) {
|
|
|
5872 |
if (has_capability('mod/assign:viewownsubmissionsummary', $this->get_context(), $user, false)) {
|
|
|
5873 |
// The user can view the submission summary.
|
|
|
5874 |
$submissionstatus = $this->get_assign_submission_status_renderable($user, $showlinks);
|
|
|
5875 |
$o .= $this->get_renderer()->render($submissionstatus);
|
|
|
5876 |
}
|
|
|
5877 |
|
|
|
5878 |
// If there is a visible grade, show the feedback.
|
|
|
5879 |
$feedbackstatus = $this->get_assign_feedback_status_renderable($user);
|
|
|
5880 |
if ($feedbackstatus) {
|
|
|
5881 |
$o .= $this->get_renderer()->render($feedbackstatus);
|
|
|
5882 |
}
|
|
|
5883 |
|
|
|
5884 |
// If there is more than one submission, show the history.
|
|
|
5885 |
$history = $this->get_assign_attempt_history_renderable($user);
|
|
|
5886 |
if (count($history->submissions) > 1) {
|
|
|
5887 |
$o .= $this->get_renderer()->render($history);
|
|
|
5888 |
}
|
|
|
5889 |
}
|
|
|
5890 |
return $o;
|
|
|
5891 |
}
|
|
|
5892 |
|
|
|
5893 |
/**
|
|
|
5894 |
* Returns true if the submit subsission button should be shown to the user.
|
|
|
5895 |
*
|
|
|
5896 |
* @param stdClass $submission The users own submission record.
|
|
|
5897 |
* @param stdClass $teamsubmission The users team submission record if there is one
|
|
|
5898 |
* @param int $userid The user
|
|
|
5899 |
* @return bool
|
|
|
5900 |
*/
|
1254 |
ariadna |
5901 |
protected function show_submit_button($submission = null, $teamsubmission = null, $userid = null)
|
|
|
5902 |
{
|
1 |
efrain |
5903 |
if (!has_capability('mod/assign:submit', $this->get_context(), $userid, false)) {
|
|
|
5904 |
// The user does not have the capability to submit.
|
|
|
5905 |
return false;
|
|
|
5906 |
}
|
|
|
5907 |
if ($teamsubmission) {
|
|
|
5908 |
if ($teamsubmission->status === ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
|
|
|
5909 |
// The assignment submission has been completed.
|
|
|
5910 |
return false;
|
|
|
5911 |
} else if ($this->submission_empty($teamsubmission)) {
|
|
|
5912 |
// There is nothing to submit yet.
|
|
|
5913 |
return false;
|
|
|
5914 |
} else if ($submission && $submission->status === ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
|
|
|
5915 |
// The user has already clicked the submit button on the team submission.
|
|
|
5916 |
return false;
|
|
|
5917 |
} else if (
|
|
|
5918 |
!empty($this->get_instance()->preventsubmissionnotingroup)
|
|
|
5919 |
&& $this->get_submission_group($userid) == false
|
|
|
5920 |
) {
|
|
|
5921 |
return false;
|
|
|
5922 |
}
|
|
|
5923 |
} else if ($submission) {
|
|
|
5924 |
if ($submission->status === ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
|
|
|
5925 |
// The assignment submission has been completed.
|
|
|
5926 |
return false;
|
|
|
5927 |
} else if ($this->submission_empty($submission)) {
|
|
|
5928 |
// There is nothing to submit.
|
|
|
5929 |
return false;
|
|
|
5930 |
}
|
|
|
5931 |
} else {
|
|
|
5932 |
// We've not got a valid submission or team submission.
|
|
|
5933 |
return false;
|
|
|
5934 |
}
|
|
|
5935 |
// Last check is that this instance allows drafts.
|
|
|
5936 |
return $this->get_instance()->submissiondrafts;
|
|
|
5937 |
}
|
|
|
5938 |
|
|
|
5939 |
/**
|
|
|
5940 |
* Get the grades for all previous attempts.
|
|
|
5941 |
* For each grade - the grader is a full user record,
|
|
|
5942 |
* and gradefordisplay is added (rendered from grading manager).
|
|
|
5943 |
*
|
|
|
5944 |
* @param int $userid If not set, $USER->id will be used.
|
|
|
5945 |
* @return array $grades All grade records for this user.
|
|
|
5946 |
*/
|
1254 |
ariadna |
5947 |
protected function get_all_grades($userid)
|
|
|
5948 |
{
|
1 |
efrain |
5949 |
global $DB, $USER, $PAGE;
|
|
|
5950 |
|
|
|
5951 |
// If the userid is not null then use userid.
|
|
|
5952 |
if (!$userid) {
|
|
|
5953 |
$userid = $USER->id;
|
|
|
5954 |
}
|
|
|
5955 |
|
1254 |
ariadna |
5956 |
$params = array('assignment' => $this->get_instance()->id, 'userid' => $userid);
|
1 |
efrain |
5957 |
|
|
|
5958 |
$grades = $DB->get_records('assign_grades', $params, 'attemptnumber ASC');
|
|
|
5959 |
|
|
|
5960 |
$gradercache = array();
|
|
|
5961 |
$cangrade = has_capability('mod/assign:grade', $this->get_context());
|
|
|
5962 |
|
|
|
5963 |
// Show the grader's identity if 'Hide Grader' is disabled or has the 'Show Hidden Grader' capability.
|
|
|
5964 |
$showgradername = (
|
|
|
5965 |
has_capability('mod/assign:showhiddengrader', $this->context, $userid) or
|
|
|
5966 |
!$this->is_hidden_grader()
|
|
|
5967 |
);
|
|
|
5968 |
|
|
|
5969 |
// Need gradingitem and gradingmanager.
|
|
|
5970 |
$gradingmanager = get_grading_manager($this->get_context(), 'mod_assign', 'submissions');
|
|
|
5971 |
$controller = $gradingmanager->get_active_controller();
|
|
|
5972 |
|
1254 |
ariadna |
5973 |
$gradinginfo = grade_get_grades(
|
|
|
5974 |
$this->get_course()->id,
|
|
|
5975 |
'mod',
|
|
|
5976 |
'assign',
|
|
|
5977 |
$this->get_instance()->id,
|
|
|
5978 |
$userid
|
|
|
5979 |
);
|
1 |
efrain |
5980 |
|
|
|
5981 |
$gradingitem = null;
|
|
|
5982 |
if (isset($gradinginfo->items[0])) {
|
|
|
5983 |
$gradingitem = $gradinginfo->items[0];
|
|
|
5984 |
}
|
|
|
5985 |
|
|
|
5986 |
foreach ($grades as $grade) {
|
|
|
5987 |
// First lookup the grader info.
|
|
|
5988 |
if (!$showgradername) {
|
|
|
5989 |
$grade->grader = null;
|
|
|
5990 |
} else if (isset($gradercache[$grade->grader])) {
|
|
|
5991 |
$grade->grader = $gradercache[$grade->grader];
|
|
|
5992 |
} else if ($grade->grader > 0) {
|
|
|
5993 |
// Not in cache - need to load the grader record.
|
1254 |
ariadna |
5994 |
$grade->grader = $DB->get_record('user', array('id' => $grade->grader));
|
1 |
efrain |
5995 |
if ($grade->grader) {
|
|
|
5996 |
$gradercache[$grade->grader->id] = $grade->grader;
|
|
|
5997 |
}
|
|
|
5998 |
}
|
|
|
5999 |
|
|
|
6000 |
// Now get the gradefordisplay.
|
|
|
6001 |
if ($controller) {
|
|
|
6002 |
$controller->set_grade_range(make_grades_menu($this->get_instance()->grade), $this->get_instance()->grade > 0);
|
1254 |
ariadna |
6003 |
$grade->gradefordisplay = $controller->render_grade(
|
|
|
6004 |
$PAGE,
|
|
|
6005 |
$grade->id,
|
|
|
6006 |
$gradingitem,
|
|
|
6007 |
$grade->grade,
|
|
|
6008 |
$cangrade
|
|
|
6009 |
);
|
1 |
efrain |
6010 |
} else {
|
|
|
6011 |
$grade->gradefordisplay = $this->display_grade($grade->grade, false);
|
|
|
6012 |
}
|
|
|
6013 |
}
|
|
|
6014 |
|
|
|
6015 |
return $grades;
|
|
|
6016 |
}
|
|
|
6017 |
|
|
|
6018 |
/**
|
|
|
6019 |
* Get the submissions for all previous attempts.
|
|
|
6020 |
*
|
|
|
6021 |
* @param int $userid If not set, $USER->id will be used.
|
|
|
6022 |
* @return array $submissions All submission records for this user (or group).
|
|
|
6023 |
*/
|
1254 |
ariadna |
6024 |
public function get_all_submissions($userid)
|
|
|
6025 |
{
|
1 |
efrain |
6026 |
global $DB, $USER;
|
|
|
6027 |
|
|
|
6028 |
// If the userid is not null then use userid.
|
|
|
6029 |
if (!$userid) {
|
|
|
6030 |
$userid = $USER->id;
|
|
|
6031 |
}
|
|
|
6032 |
|
|
|
6033 |
$params = array();
|
|
|
6034 |
|
|
|
6035 |
if ($this->get_instance()->teamsubmission) {
|
|
|
6036 |
$groupid = 0;
|
|
|
6037 |
$group = $this->get_submission_group($userid);
|
|
|
6038 |
if ($group) {
|
|
|
6039 |
$groupid = $group->id;
|
|
|
6040 |
}
|
|
|
6041 |
|
|
|
6042 |
// Params to get the group submissions.
|
1254 |
ariadna |
6043 |
$params = array('assignment' => $this->get_instance()->id, 'groupid' => $groupid, 'userid' => 0);
|
1 |
efrain |
6044 |
} else {
|
|
|
6045 |
// Params to get the user submissions.
|
1254 |
ariadna |
6046 |
$params = array('assignment' => $this->get_instance()->id, 'userid' => $userid);
|
1 |
efrain |
6047 |
}
|
|
|
6048 |
|
|
|
6049 |
// Return the submissions ordered by attempt.
|
|
|
6050 |
$submissions = $DB->get_records('assign_submission', $params, 'attemptnumber ASC');
|
|
|
6051 |
|
|
|
6052 |
return $submissions;
|
|
|
6053 |
}
|
|
|
6054 |
|
|
|
6055 |
/**
|
|
|
6056 |
* Creates an assign_grading_summary renderable.
|
|
|
6057 |
*
|
|
|
6058 |
* @param mixed $activitygroup int|null the group for calculating the grading summary (if null the function will determine it)
|
|
|
6059 |
* @return assign_grading_summary renderable object
|
|
|
6060 |
*/
|
1254 |
ariadna |
6061 |
public function get_assign_grading_summary_renderable($activitygroup = null)
|
|
|
6062 |
{
|
1 |
efrain |
6063 |
|
|
|
6064 |
$instance = $this->get_default_instance(); // Grading summary requires the raw dates, regardless of relativedates mode.
|
|
|
6065 |
$cm = $this->get_course_module();
|
|
|
6066 |
$course = $this->get_course();
|
|
|
6067 |
|
|
|
6068 |
$draft = ASSIGN_SUBMISSION_STATUS_DRAFT;
|
|
|
6069 |
$submitted = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
|
|
|
6070 |
$isvisible = $cm->visible;
|
|
|
6071 |
|
|
|
6072 |
if ($activitygroup === null) {
|
|
|
6073 |
$activitygroup = groups_get_activity_group($cm);
|
|
|
6074 |
}
|
|
|
6075 |
|
|
|
6076 |
if ($instance->teamsubmission) {
|
|
|
6077 |
$warnofungroupedusers = assign_grading_summary::WARN_GROUPS_NO;
|
|
|
6078 |
$defaultteammembers = $this->get_submission_group_members(0, true);
|
|
|
6079 |
if (count($defaultteammembers) > 0) {
|
|
|
6080 |
if ($instance->preventsubmissionnotingroup) {
|
|
|
6081 |
$warnofungroupedusers = assign_grading_summary::WARN_GROUPS_REQUIRED;
|
|
|
6082 |
} else {
|
|
|
6083 |
$warnofungroupedusers = assign_grading_summary::WARN_GROUPS_OPTIONAL;
|
|
|
6084 |
}
|
|
|
6085 |
}
|
|
|
6086 |
|
|
|
6087 |
$summary = new assign_grading_summary(
|
|
|
6088 |
$this->count_teams($activitygroup),
|
|
|
6089 |
$instance->submissiondrafts,
|
|
|
6090 |
$this->count_submissions_with_status($draft, $activitygroup),
|
|
|
6091 |
$this->is_any_submission_plugin_enabled(),
|
|
|
6092 |
$this->count_submissions_with_status($submitted, $activitygroup),
|
|
|
6093 |
$this->get_cutoffdate($activitygroup),
|
|
|
6094 |
$this->get_duedate($activitygroup),
|
|
|
6095 |
$this->get_timelimit($activitygroup),
|
|
|
6096 |
$this->get_course_module()->id,
|
|
|
6097 |
$this->count_submissions_need_grading($activitygroup),
|
|
|
6098 |
$instance->teamsubmission,
|
|
|
6099 |
$warnofungroupedusers,
|
|
|
6100 |
$course->relativedatesmode,
|
|
|
6101 |
$course->startdate,
|
|
|
6102 |
$this->can_grade(),
|
|
|
6103 |
$isvisible,
|
|
|
6104 |
$this->get_course_module()
|
|
|
6105 |
);
|
|
|
6106 |
} else {
|
|
|
6107 |
// The active group has already been updated in groups_print_activity_menu().
|
|
|
6108 |
$countparticipants = $this->count_participants($activitygroup);
|
|
|
6109 |
$summary = new assign_grading_summary(
|
|
|
6110 |
$countparticipants,
|
|
|
6111 |
$instance->submissiondrafts,
|
|
|
6112 |
$this->count_submissions_with_status($draft, $activitygroup),
|
|
|
6113 |
$this->is_any_submission_plugin_enabled(),
|
|
|
6114 |
$this->count_submissions_with_status($submitted, $activitygroup),
|
|
|
6115 |
$this->get_cutoffdate($activitygroup),
|
|
|
6116 |
$this->get_duedate($activitygroup),
|
|
|
6117 |
$this->get_timelimit($activitygroup),
|
|
|
6118 |
$this->get_course_module()->id,
|
|
|
6119 |
$this->count_submissions_need_grading($activitygroup),
|
|
|
6120 |
$instance->teamsubmission,
|
|
|
6121 |
assign_grading_summary::WARN_GROUPS_NO,
|
|
|
6122 |
$course->relativedatesmode,
|
|
|
6123 |
$course->startdate,
|
|
|
6124 |
$this->can_grade(),
|
|
|
6125 |
$isvisible,
|
|
|
6126 |
$this->get_course_module()
|
|
|
6127 |
);
|
|
|
6128 |
}
|
|
|
6129 |
|
|
|
6130 |
return $summary;
|
|
|
6131 |
}
|
|
|
6132 |
|
|
|
6133 |
/**
|
|
|
6134 |
* Helper function to allow up to fetch the group overrides via one query as opposed to many calls.
|
|
|
6135 |
*
|
|
|
6136 |
* @param int $activitygroup The group we want to check the overrides of
|
|
|
6137 |
* @return mixed Can return either a fetched DB object, local object or false
|
|
|
6138 |
*/
|
1254 |
ariadna |
6139 |
private function get_override_data(int $activitygroup)
|
|
|
6140 |
{
|
1 |
efrain |
6141 |
global $DB;
|
|
|
6142 |
|
|
|
6143 |
$instanceid = $this->get_instance()->id;
|
|
|
6144 |
$cachekey = "$instanceid-$activitygroup";
|
|
|
6145 |
if (isset($this->overridedata[$cachekey])) {
|
|
|
6146 |
return $this->overridedata[$cachekey];
|
|
|
6147 |
}
|
|
|
6148 |
|
|
|
6149 |
$params = ['groupid' => $activitygroup, 'assignid' => $instanceid];
|
|
|
6150 |
$this->overridedata[$cachekey] = $DB->get_record('assign_overrides', $params);
|
|
|
6151 |
return $this->overridedata[$cachekey];
|
|
|
6152 |
}
|
|
|
6153 |
|
|
|
6154 |
/**
|
|
|
6155 |
* Return group override duedate.
|
|
|
6156 |
*
|
|
|
6157 |
* @param int $activitygroup Activity active group
|
|
|
6158 |
* @return int $duedate
|
|
|
6159 |
*/
|
1254 |
ariadna |
6160 |
private function get_duedate($activitygroup = null)
|
|
|
6161 |
{
|
1 |
efrain |
6162 |
if ($activitygroup === null) {
|
|
|
6163 |
$activitygroup = groups_get_activity_group($this->get_course_module());
|
|
|
6164 |
}
|
|
|
6165 |
if ($this->can_view_grades() && !empty($activitygroup)) {
|
|
|
6166 |
$groupoverride = $this->get_override_data($activitygroup);
|
|
|
6167 |
if (!empty($groupoverride->duedate)) {
|
|
|
6168 |
return $groupoverride->duedate;
|
|
|
6169 |
}
|
|
|
6170 |
}
|
|
|
6171 |
return $this->get_instance()->duedate;
|
|
|
6172 |
}
|
|
|
6173 |
|
|
|
6174 |
/**
|
|
|
6175 |
* Return group override timelimit.
|
|
|
6176 |
*
|
|
|
6177 |
* @param null|int $activitygroup Activity active group
|
|
|
6178 |
* @return int $timelimit
|
|
|
6179 |
*/
|
1254 |
ariadna |
6180 |
private function get_timelimit(?int $activitygroup = null): int
|
|
|
6181 |
{
|
1 |
efrain |
6182 |
if ($activitygroup === null) {
|
|
|
6183 |
$activitygroup = groups_get_activity_group($this->get_course_module());
|
|
|
6184 |
}
|
|
|
6185 |
if ($this->can_view_grades() && !empty($activitygroup)) {
|
|
|
6186 |
$groupoverride = $this->get_override_data($activitygroup);
|
|
|
6187 |
if (!empty($groupoverride->timelimit)) {
|
|
|
6188 |
return $groupoverride->timelimit;
|
|
|
6189 |
}
|
|
|
6190 |
}
|
|
|
6191 |
return $this->get_instance()->timelimit;
|
|
|
6192 |
}
|
|
|
6193 |
|
|
|
6194 |
/**
|
|
|
6195 |
* Return group override cutoffdate.
|
|
|
6196 |
*
|
|
|
6197 |
* @param null|int $activitygroup Activity active group
|
|
|
6198 |
* @return int $cutoffdate
|
|
|
6199 |
*/
|
1254 |
ariadna |
6200 |
private function get_cutoffdate(?int $activitygroup = null): int
|
|
|
6201 |
{
|
1 |
efrain |
6202 |
if ($activitygroup === null) {
|
|
|
6203 |
$activitygroup = groups_get_activity_group($this->get_course_module());
|
|
|
6204 |
}
|
|
|
6205 |
if ($this->can_view_grades() && !empty($activitygroup)) {
|
|
|
6206 |
$groupoverride = $this->get_override_data($activitygroup);
|
|
|
6207 |
if (!empty($groupoverride->cutoffdate)) {
|
|
|
6208 |
return $groupoverride->cutoffdate;
|
|
|
6209 |
}
|
|
|
6210 |
}
|
|
|
6211 |
return $this->get_instance()->cutoffdate;
|
|
|
6212 |
}
|
|
|
6213 |
|
|
|
6214 |
/**
|
|
|
6215 |
* View submissions page (contains details of current submission).
|
|
|
6216 |
*
|
|
|
6217 |
* @return string
|
|
|
6218 |
*/
|
1254 |
ariadna |
6219 |
protected function view_submission_page()
|
|
|
6220 |
{
|
1 |
efrain |
6221 |
global $CFG, $DB, $USER, $PAGE;
|
|
|
6222 |
|
|
|
6223 |
$instance = $this->get_instance();
|
|
|
6224 |
|
|
|
6225 |
$this->add_grade_notices();
|
|
|
6226 |
|
|
|
6227 |
$o = '';
|
|
|
6228 |
|
|
|
6229 |
$postfix = '';
|
|
|
6230 |
if ($this->has_visible_attachments() && (!$this->get_instance($USER->id)->submissionattachments)) {
|
|
|
6231 |
$postfix = $this->render_area_files('mod_assign', ASSIGN_INTROATTACHMENT_FILEAREA, 0);
|
|
|
6232 |
}
|
|
|
6233 |
|
1254 |
ariadna |
6234 |
$o .= $this->get_renderer()->render(new assign_header(
|
|
|
6235 |
$instance,
|
|
|
6236 |
$this->get_context(),
|
|
|
6237 |
$this->show_intro(),
|
|
|
6238 |
$this->get_course_module()->id,
|
|
|
6239 |
'',
|
|
|
6240 |
'',
|
|
|
6241 |
$postfix
|
|
|
6242 |
));
|
1 |
efrain |
6243 |
|
|
|
6244 |
// Display plugin specific headers.
|
|
|
6245 |
$plugins = array_merge($this->get_submission_plugins(), $this->get_feedback_plugins());
|
|
|
6246 |
foreach ($plugins as $plugin) {
|
|
|
6247 |
if ($plugin->is_enabled() && $plugin->is_visible()) {
|
|
|
6248 |
$o .= $this->get_renderer()->render(new assign_plugin_header($plugin));
|
|
|
6249 |
}
|
|
|
6250 |
}
|
|
|
6251 |
|
|
|
6252 |
if ($this->can_view_grades()) {
|
|
|
6253 |
$actionbuttons = new \mod_assign\output\actionmenu($this->get_course_module()->id);
|
|
|
6254 |
$o .= $this->get_renderer()->submission_actionmenu($actionbuttons);
|
|
|
6255 |
|
|
|
6256 |
$summary = $this->get_assign_grading_summary_renderable();
|
|
|
6257 |
$o .= $this->get_renderer()->render($summary);
|
|
|
6258 |
}
|
|
|
6259 |
|
|
|
6260 |
if ($this->can_view_submission($USER->id)) {
|
|
|
6261 |
$o .= $this->view_submission_action_bar($instance, $USER);
|
|
|
6262 |
$o .= $this->view_student_summary($USER, true);
|
|
|
6263 |
}
|
|
|
6264 |
|
|
|
6265 |
$o .= $this->view_footer();
|
|
|
6266 |
|
|
|
6267 |
\mod_assign\event\submission_status_viewed::create_from_assign($this)->trigger();
|
|
|
6268 |
|
|
|
6269 |
return $o;
|
|
|
6270 |
}
|
|
|
6271 |
|
|
|
6272 |
/**
|
|
|
6273 |
* The action bar displayed in the submissions page.
|
|
|
6274 |
*
|
|
|
6275 |
* @param stdClass $instance The settings for the current instance of this assignment
|
|
|
6276 |
* @param stdClass $user The user to print the action bar for
|
|
|
6277 |
* @return string
|
|
|
6278 |
*/
|
1254 |
ariadna |
6279 |
public function view_submission_action_bar(stdClass $instance, stdClass $user): string
|
|
|
6280 |
{
|
1 |
efrain |
6281 |
$submission = $this->get_user_submission($user->id, false);
|
|
|
6282 |
// Figure out if we are team or solitary submission.
|
|
|
6283 |
$teamsubmission = null;
|
|
|
6284 |
if ($instance->teamsubmission) {
|
|
|
6285 |
$teamsubmission = $this->get_group_submission($user->id, 0, false);
|
|
|
6286 |
}
|
|
|
6287 |
|
|
|
6288 |
$showsubmit = ($this->submissions_open($user->id)
|
|
|
6289 |
&& $this->show_submit_button($submission, $teamsubmission, $user->id));
|
|
|
6290 |
$showedit = ($this->is_any_submission_plugin_enabled()) && $this->can_edit_submission($user->id);
|
|
|
6291 |
|
|
|
6292 |
// The method get_group_submission() says that it returns a stdClass, but it can return false >_>.
|
|
|
6293 |
if ($teamsubmission === false) {
|
|
|
6294 |
$teamsubmission = new stdClass();
|
|
|
6295 |
}
|
|
|
6296 |
// Same goes for get_user_submission().
|
|
|
6297 |
if ($submission === false) {
|
|
|
6298 |
$submission = new stdClass();
|
|
|
6299 |
}
|
|
|
6300 |
$actionbuttons = new \mod_assign\output\user_submission_actionmenu(
|
|
|
6301 |
$this->get_course_module()->id,
|
|
|
6302 |
$showsubmit,
|
|
|
6303 |
$showedit,
|
|
|
6304 |
$submission,
|
|
|
6305 |
$teamsubmission,
|
|
|
6306 |
$instance->timelimit
|
|
|
6307 |
);
|
|
|
6308 |
|
|
|
6309 |
return $this->get_renderer()->render($actionbuttons);
|
|
|
6310 |
}
|
|
|
6311 |
|
|
|
6312 |
/**
|
|
|
6313 |
* Convert the final raw grade(s) in the grading table for the gradebook.
|
|
|
6314 |
*
|
|
|
6315 |
* @param stdClass $grade
|
|
|
6316 |
* @return array
|
|
|
6317 |
*/
|
1254 |
ariadna |
6318 |
protected function convert_grade_for_gradebook(stdClass $grade)
|
|
|
6319 |
{
|
1 |
efrain |
6320 |
$gradebookgrade = array();
|
|
|
6321 |
if ($grade->grade >= 0) {
|
|
|
6322 |
$gradebookgrade['rawgrade'] = $grade->grade;
|
|
|
6323 |
}
|
|
|
6324 |
// Allow "no grade" to be chosen.
|
|
|
6325 |
if ($grade->grade == -1) {
|
|
|
6326 |
$gradebookgrade['rawgrade'] = NULL;
|
|
|
6327 |
}
|
|
|
6328 |
$gradebookgrade['userid'] = $grade->userid;
|
|
|
6329 |
$gradebookgrade['usermodified'] = $grade->grader;
|
|
|
6330 |
$gradebookgrade['datesubmitted'] = null;
|
|
|
6331 |
$gradebookgrade['dategraded'] = $grade->timemodified;
|
|
|
6332 |
if (isset($grade->feedbackformat)) {
|
|
|
6333 |
$gradebookgrade['feedbackformat'] = $grade->feedbackformat;
|
|
|
6334 |
}
|
|
|
6335 |
if (isset($grade->feedbacktext)) {
|
|
|
6336 |
$gradebookgrade['feedback'] = $grade->feedbacktext;
|
|
|
6337 |
}
|
|
|
6338 |
if (isset($grade->feedbackfiles)) {
|
|
|
6339 |
$gradebookgrade['feedbackfiles'] = $grade->feedbackfiles;
|
|
|
6340 |
}
|
|
|
6341 |
|
|
|
6342 |
return $gradebookgrade;
|
|
|
6343 |
}
|
|
|
6344 |
|
|
|
6345 |
/**
|
|
|
6346 |
* Convert submission details for the gradebook.
|
|
|
6347 |
*
|
|
|
6348 |
* @param stdClass $submission
|
|
|
6349 |
* @return array
|
|
|
6350 |
*/
|
1254 |
ariadna |
6351 |
protected function convert_submission_for_gradebook(stdClass $submission)
|
|
|
6352 |
{
|
1 |
efrain |
6353 |
$gradebookgrade = array();
|
|
|
6354 |
|
|
|
6355 |
$gradebookgrade['userid'] = $submission->userid;
|
|
|
6356 |
$gradebookgrade['usermodified'] = $submission->userid;
|
|
|
6357 |
$gradebookgrade['datesubmitted'] = $submission->timemodified;
|
|
|
6358 |
|
|
|
6359 |
return $gradebookgrade;
|
|
|
6360 |
}
|
|
|
6361 |
|
|
|
6362 |
/**
|
|
|
6363 |
* Update grades in the gradebook.
|
|
|
6364 |
*
|
|
|
6365 |
* @param mixed $submission stdClass|null
|
|
|
6366 |
* @param mixed $grade stdClass|null
|
|
|
6367 |
* @return bool
|
|
|
6368 |
*/
|
1254 |
ariadna |
6369 |
protected function gradebook_item_update($submission = null, $grade = null)
|
|
|
6370 |
{
|
1 |
efrain |
6371 |
global $CFG;
|
|
|
6372 |
|
1254 |
ariadna |
6373 |
require_once($CFG->dirroot . '/mod/assign/lib.php');
|
1 |
efrain |
6374 |
// Do not push grade to gradebook if blind marking is active as
|
|
|
6375 |
// the gradebook would reveal the students.
|
|
|
6376 |
if ($this->is_blind_marking()) {
|
|
|
6377 |
return false;
|
|
|
6378 |
}
|
|
|
6379 |
|
|
|
6380 |
// If marking workflow is enabled and grade is not released then remove any grade that may exist in the gradebook.
|
1254 |
ariadna |
6381 |
if (
|
|
|
6382 |
$this->get_instance()->markingworkflow && !empty($grade) &&
|
|
|
6383 |
$this->get_grading_status($grade->userid) != ASSIGN_MARKING_WORKFLOW_STATE_RELEASED
|
|
|
6384 |
) {
|
1 |
efrain |
6385 |
// Remove the grade (if it exists) from the gradebook as it is not 'final'.
|
|
|
6386 |
$grade->grade = -1;
|
|
|
6387 |
$grade->feedbacktext = '';
|
|
|
6388 |
$grade->feebackfiles = [];
|
|
|
6389 |
}
|
|
|
6390 |
|
|
|
6391 |
if ($submission != null) {
|
|
|
6392 |
if ($submission->userid == 0) {
|
|
|
6393 |
// This is a group submission update.
|
|
|
6394 |
$team = groups_get_members($submission->groupid, 'u.id');
|
|
|
6395 |
|
|
|
6396 |
foreach ($team as $member) {
|
|
|
6397 |
$membersubmission = clone $submission;
|
|
|
6398 |
$membersubmission->groupid = 0;
|
|
|
6399 |
$membersubmission->userid = $member->id;
|
|
|
6400 |
$this->gradebook_item_update($membersubmission, null);
|
|
|
6401 |
}
|
|
|
6402 |
return;
|
|
|
6403 |
}
|
|
|
6404 |
|
|
|
6405 |
$gradebookgrade = $this->convert_submission_for_gradebook($submission);
|
|
|
6406 |
} else {
|
|
|
6407 |
$gradebookgrade = $this->convert_grade_for_gradebook($grade);
|
|
|
6408 |
}
|
|
|
6409 |
// Grading is disabled, return.
|
|
|
6410 |
if ($this->grading_disabled($gradebookgrade['userid'])) {
|
|
|
6411 |
return false;
|
|
|
6412 |
}
|
|
|
6413 |
$assign = clone $this->get_instance();
|
|
|
6414 |
$assign->cmidnumber = $this->get_course_module()->idnumber;
|
|
|
6415 |
// Set assign gradebook feedback plugin status (enabled and visible).
|
|
|
6416 |
$assign->gradefeedbackenabled = $this->is_gradebook_feedback_enabled();
|
|
|
6417 |
return assign_grade_item_update($assign, $gradebookgrade) == GRADE_UPDATE_OK;
|
|
|
6418 |
}
|
|
|
6419 |
|
|
|
6420 |
/**
|
|
|
6421 |
* Update team submission.
|
|
|
6422 |
*
|
|
|
6423 |
* @param stdClass $submission
|
|
|
6424 |
* @param int $userid
|
|
|
6425 |
* @param bool $updatetime
|
|
|
6426 |
* @return bool
|
|
|
6427 |
*/
|
1254 |
ariadna |
6428 |
protected function update_team_submission(stdClass $submission, $userid, $updatetime)
|
|
|
6429 |
{
|
1 |
efrain |
6430 |
global $DB;
|
|
|
6431 |
|
|
|
6432 |
if ($updatetime) {
|
|
|
6433 |
$submission->timemodified = time();
|
|
|
6434 |
}
|
|
|
6435 |
|
|
|
6436 |
// First update the submission for the current user.
|
|
|
6437 |
$mysubmission = $this->get_user_submission($userid, true, $submission->attemptnumber);
|
|
|
6438 |
$mysubmission->status = $submission->status;
|
|
|
6439 |
|
|
|
6440 |
$this->update_submission($mysubmission, 0, $updatetime, false);
|
|
|
6441 |
|
|
|
6442 |
// Now check the team settings to see if this assignment qualifies as submitted or draft.
|
|
|
6443 |
$team = $this->get_submission_group_members($submission->groupid, true);
|
|
|
6444 |
|
|
|
6445 |
$allsubmitted = true;
|
|
|
6446 |
$anysubmitted = false;
|
|
|
6447 |
$result = true;
|
|
|
6448 |
if (!in_array($submission->status, [ASSIGN_SUBMISSION_STATUS_NEW, ASSIGN_SUBMISSION_STATUS_REOPENED])) {
|
|
|
6449 |
foreach ($team as $member) {
|
|
|
6450 |
$membersubmission = $this->get_user_submission($member->id, false, $submission->attemptnumber);
|
|
|
6451 |
|
|
|
6452 |
// If no submission found for team member and member is active then everyone has not submitted.
|
1254 |
ariadna |
6453 |
if (
|
|
|
6454 |
!$membersubmission || $membersubmission->status != ASSIGN_SUBMISSION_STATUS_SUBMITTED
|
|
|
6455 |
&& ($this->is_active_user($member->id))
|
|
|
6456 |
) {
|
1 |
efrain |
6457 |
$allsubmitted = false;
|
|
|
6458 |
if ($anysubmitted) {
|
|
|
6459 |
break;
|
|
|
6460 |
}
|
|
|
6461 |
} else {
|
|
|
6462 |
$anysubmitted = true;
|
|
|
6463 |
}
|
|
|
6464 |
}
|
|
|
6465 |
if ($this->get_instance()->requireallteammemberssubmit) {
|
|
|
6466 |
if ($allsubmitted) {
|
|
|
6467 |
$submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
|
|
|
6468 |
} else {
|
|
|
6469 |
$submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
|
|
|
6470 |
}
|
|
|
6471 |
$result = $DB->update_record('assign_submission', $submission);
|
|
|
6472 |
} else {
|
|
|
6473 |
if ($anysubmitted) {
|
|
|
6474 |
$submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
|
|
|
6475 |
} else {
|
|
|
6476 |
$submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
|
|
|
6477 |
}
|
|
|
6478 |
$result = $DB->update_record('assign_submission', $submission);
|
|
|
6479 |
}
|
|
|
6480 |
} else {
|
|
|
6481 |
// Set the group submission to reopened.
|
|
|
6482 |
foreach ($team as $member) {
|
|
|
6483 |
$membersubmission = $this->get_user_submission($member->id, true, $submission->attemptnumber);
|
|
|
6484 |
$membersubmission->status = $submission->status;
|
|
|
6485 |
$result = $DB->update_record('assign_submission', $membersubmission) && $result;
|
|
|
6486 |
}
|
|
|
6487 |
$result = $DB->update_record('assign_submission', $submission) && $result;
|
|
|
6488 |
}
|
|
|
6489 |
|
|
|
6490 |
$this->gradebook_item_update($submission);
|
|
|
6491 |
return $result;
|
|
|
6492 |
}
|
|
|
6493 |
|
|
|
6494 |
/**
|
|
|
6495 |
* Update grades in the gradebook based on submission time.
|
|
|
6496 |
*
|
|
|
6497 |
* @param stdClass $submission
|
|
|
6498 |
* @param int $userid
|
|
|
6499 |
* @param bool $updatetime
|
|
|
6500 |
* @param bool $teamsubmission
|
|
|
6501 |
* @return bool
|
|
|
6502 |
*/
|
1254 |
ariadna |
6503 |
protected function update_submission(stdClass $submission, $userid, $updatetime, $teamsubmission)
|
|
|
6504 |
{
|
1 |
efrain |
6505 |
global $DB;
|
|
|
6506 |
|
|
|
6507 |
if ($teamsubmission) {
|
|
|
6508 |
return $this->update_team_submission($submission, $userid, $updatetime);
|
|
|
6509 |
}
|
|
|
6510 |
|
|
|
6511 |
if ($updatetime) {
|
|
|
6512 |
$submission->timemodified = time();
|
|
|
6513 |
}
|
1254 |
ariadna |
6514 |
$result = $DB->update_record('assign_submission', $submission);
|
1 |
efrain |
6515 |
if ($result) {
|
|
|
6516 |
$this->gradebook_item_update($submission);
|
|
|
6517 |
}
|
|
|
6518 |
return $result;
|
|
|
6519 |
}
|
|
|
6520 |
|
|
|
6521 |
/**
|
|
|
6522 |
* Is this assignment open for submissions?
|
|
|
6523 |
*
|
|
|
6524 |
* Check the due date,
|
|
|
6525 |
* prevent late submissions,
|
|
|
6526 |
* has this person already submitted,
|
|
|
6527 |
* is the assignment locked?
|
|
|
6528 |
*
|
|
|
6529 |
* @param int $userid - Optional userid so we can see if a different user can submit
|
|
|
6530 |
* @param bool $skipenrolled - Skip enrollment checks (because they have been done already)
|
|
|
6531 |
* @param stdClass $submission - Pre-fetched submission record (or false to fetch it)
|
|
|
6532 |
* @param stdClass $flags - Pre-fetched user flags record (or false to fetch it)
|
|
|
6533 |
* @param stdClass $gradinginfo - Pre-fetched user gradinginfo record (or false to fetch it)
|
|
|
6534 |
* @return bool
|
|
|
6535 |
*/
|
1254 |
ariadna |
6536 |
public function submissions_open(
|
|
|
6537 |
$userid = 0,
|
|
|
6538 |
$skipenrolled = false,
|
|
|
6539 |
$submission = false,
|
|
|
6540 |
$flags = false,
|
|
|
6541 |
$gradinginfo = false
|
|
|
6542 |
) {
|
1 |
efrain |
6543 |
global $USER;
|
|
|
6544 |
|
|
|
6545 |
if (!$userid) {
|
|
|
6546 |
$userid = $USER->id;
|
|
|
6547 |
}
|
|
|
6548 |
|
|
|
6549 |
$time = time();
|
|
|
6550 |
$dateopen = true;
|
|
|
6551 |
$finaldate = false;
|
|
|
6552 |
if ($this->get_instance()->cutoffdate) {
|
|
|
6553 |
$finaldate = $this->get_instance()->cutoffdate;
|
|
|
6554 |
}
|
|
|
6555 |
|
|
|
6556 |
if ($flags === false) {
|
|
|
6557 |
$flags = $this->get_user_flags($userid, false);
|
|
|
6558 |
}
|
|
|
6559 |
if ($flags && $flags->locked) {
|
|
|
6560 |
return false;
|
|
|
6561 |
}
|
|
|
6562 |
|
|
|
6563 |
// User extensions.
|
|
|
6564 |
if ($finaldate) {
|
|
|
6565 |
if ($flags && $flags->extensionduedate) {
|
|
|
6566 |
// Extension can be before cut off date.
|
|
|
6567 |
if ($flags->extensionduedate > $finaldate) {
|
|
|
6568 |
$finaldate = $flags->extensionduedate;
|
|
|
6569 |
}
|
|
|
6570 |
}
|
|
|
6571 |
}
|
|
|
6572 |
|
|
|
6573 |
if ($finaldate) {
|
|
|
6574 |
$dateopen = ($this->get_instance()->allowsubmissionsfromdate <= $time && $time <= $finaldate);
|
|
|
6575 |
} else {
|
|
|
6576 |
$dateopen = ($this->get_instance()->allowsubmissionsfromdate <= $time);
|
|
|
6577 |
}
|
|
|
6578 |
|
|
|
6579 |
if (!$dateopen) {
|
|
|
6580 |
return false;
|
|
|
6581 |
}
|
|
|
6582 |
|
|
|
6583 |
// Now check if this user has already submitted etc.
|
|
|
6584 |
if (!$skipenrolled && !is_enrolled($this->get_course_context(), $userid)) {
|
|
|
6585 |
return false;
|
|
|
6586 |
}
|
|
|
6587 |
// Note you can pass null for submission and it will not be fetched.
|
|
|
6588 |
if ($submission === false) {
|
|
|
6589 |
if ($this->get_instance()->teamsubmission) {
|
|
|
6590 |
$submission = $this->get_group_submission($userid, 0, false);
|
|
|
6591 |
} else {
|
|
|
6592 |
$submission = $this->get_user_submission($userid, false);
|
|
|
6593 |
}
|
|
|
6594 |
}
|
|
|
6595 |
if ($submission) {
|
|
|
6596 |
|
|
|
6597 |
if ($this->get_instance()->submissiondrafts && $submission->status == ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
|
|
|
6598 |
// Drafts are tracked and the student has submitted the assignment.
|
|
|
6599 |
return false;
|
|
|
6600 |
}
|
|
|
6601 |
}
|
|
|
6602 |
|
|
|
6603 |
// See if this user grade is locked in the gradebook.
|
|
|
6604 |
if ($gradinginfo === false) {
|
1254 |
ariadna |
6605 |
$gradinginfo = grade_get_grades(
|
|
|
6606 |
$this->get_course()->id,
|
|
|
6607 |
'mod',
|
|
|
6608 |
'assign',
|
|
|
6609 |
$this->get_instance()->id,
|
|
|
6610 |
array($userid)
|
|
|
6611 |
);
|
1 |
efrain |
6612 |
}
|
1254 |
ariadna |
6613 |
if (
|
|
|
6614 |
$gradinginfo &&
|
|
|
6615 |
isset($gradinginfo->items[0]->grades[$userid]) &&
|
|
|
6616 |
$gradinginfo->items[0]->grades[$userid]->locked
|
|
|
6617 |
) {
|
1 |
efrain |
6618 |
return false;
|
|
|
6619 |
}
|
|
|
6620 |
|
|
|
6621 |
return true;
|
|
|
6622 |
}
|
|
|
6623 |
|
|
|
6624 |
/**
|
|
|
6625 |
* Render the files in file area.
|
|
|
6626 |
*
|
|
|
6627 |
* @param string $component
|
|
|
6628 |
* @param string $area
|
|
|
6629 |
* @param int $submissionid
|
|
|
6630 |
* @return string
|
|
|
6631 |
*/
|
1254 |
ariadna |
6632 |
public function render_area_files($component, $area, $submissionid)
|
|
|
6633 |
{
|
1 |
efrain |
6634 |
global $USER;
|
|
|
6635 |
|
1254 |
ariadna |
6636 |
return $this->get_renderer()->assign_files(
|
|
|
6637 |
$this->context,
|
|
|
6638 |
$submissionid,
|
|
|
6639 |
$area,
|
|
|
6640 |
$component,
|
|
|
6641 |
$this->course,
|
|
|
6642 |
$this->coursemodule
|
|
|
6643 |
);
|
1 |
efrain |
6644 |
}
|
|
|
6645 |
|
|
|
6646 |
/**
|
|
|
6647 |
* Capability check to make sure this grader can edit this submission.
|
|
|
6648 |
*
|
|
|
6649 |
* @param int $userid - The user whose submission is to be edited
|
|
|
6650 |
* @param int $graderid (optional) - The user who will do the editing (default to $USER->id).
|
|
|
6651 |
* @return bool
|
|
|
6652 |
*/
|
1254 |
ariadna |
6653 |
public function can_edit_submission($userid, $graderid = 0)
|
|
|
6654 |
{
|
1 |
efrain |
6655 |
global $USER;
|
|
|
6656 |
|
|
|
6657 |
if (empty($graderid)) {
|
|
|
6658 |
$graderid = $USER->id;
|
|
|
6659 |
}
|
|
|
6660 |
|
|
|
6661 |
$instance = $this->get_instance();
|
1254 |
ariadna |
6662 |
if (
|
|
|
6663 |
$userid == $graderid &&
|
1 |
efrain |
6664 |
$instance->teamsubmission &&
|
|
|
6665 |
$instance->preventsubmissionnotingroup &&
|
1254 |
ariadna |
6666 |
$this->get_submission_group($userid) == false
|
|
|
6667 |
) {
|
1 |
efrain |
6668 |
return false;
|
|
|
6669 |
}
|
|
|
6670 |
|
|
|
6671 |
if ($userid == $graderid) {
|
1254 |
ariadna |
6672 |
if (
|
|
|
6673 |
$this->submissions_open($userid) &&
|
|
|
6674 |
has_capability('mod/assign:submit', $this->context, $graderid)
|
|
|
6675 |
) {
|
1 |
efrain |
6676 |
// User can edit their own submission.
|
|
|
6677 |
return true;
|
|
|
6678 |
} else {
|
|
|
6679 |
// We need to return here because editothersubmission should never apply to a users own submission.
|
|
|
6680 |
return false;
|
|
|
6681 |
}
|
|
|
6682 |
}
|
|
|
6683 |
|
|
|
6684 |
if (!has_capability('mod/assign:editothersubmission', $this->context, $graderid)) {
|
|
|
6685 |
return false;
|
|
|
6686 |
}
|
|
|
6687 |
|
|
|
6688 |
$cm = $this->get_course_module();
|
|
|
6689 |
if (groups_get_activity_groupmode($cm) == SEPARATEGROUPS) {
|
|
|
6690 |
$sharedgroupmembers = $this->get_shared_group_members($cm, $graderid);
|
|
|
6691 |
return in_array($userid, $sharedgroupmembers);
|
|
|
6692 |
}
|
|
|
6693 |
return true;
|
|
|
6694 |
}
|
|
|
6695 |
|
|
|
6696 |
/**
|
|
|
6697 |
* Returns IDs of the users who share group membership with the specified user.
|
|
|
6698 |
*
|
|
|
6699 |
* @param stdClass|cm_info $cm Course-module
|
|
|
6700 |
* @param int $userid User ID
|
|
|
6701 |
* @return array An array of ID of users.
|
|
|
6702 |
*/
|
1254 |
ariadna |
6703 |
public function get_shared_group_members($cm, $userid)
|
|
|
6704 |
{
|
1 |
efrain |
6705 |
if (!isset($this->sharedgroupmembers[$userid])) {
|
|
|
6706 |
$this->sharedgroupmembers[$userid] = array();
|
|
|
6707 |
if ($members = groups_get_activity_shared_group_members($cm, $userid)) {
|
|
|
6708 |
$this->sharedgroupmembers[$userid] = array_keys($members);
|
|
|
6709 |
}
|
|
|
6710 |
}
|
|
|
6711 |
|
|
|
6712 |
return $this->sharedgroupmembers[$userid];
|
|
|
6713 |
}
|
|
|
6714 |
|
|
|
6715 |
/**
|
|
|
6716 |
* Returns a list of teachers that should be grading given submission.
|
|
|
6717 |
*
|
|
|
6718 |
* @param int $userid The submission to grade
|
|
|
6719 |
* @return array
|
|
|
6720 |
*/
|
1254 |
ariadna |
6721 |
protected function get_graders($userid)
|
|
|
6722 |
{
|
1 |
efrain |
6723 |
// Potential graders should be active users only.
|
|
|
6724 |
$potentialgraders = get_enrolled_users($this->context, "mod/assign:grade", null, 'u.*', null, null, null, true);
|
|
|
6725 |
|
|
|
6726 |
$graders = array();
|
|
|
6727 |
if (groups_get_activity_groupmode($this->get_course_module()) == SEPARATEGROUPS) {
|
|
|
6728 |
if ($groups = groups_get_all_groups($this->get_course()->id, $userid, $this->get_course_module()->groupingid)) {
|
|
|
6729 |
foreach ($groups as $group) {
|
|
|
6730 |
foreach ($potentialgraders as $grader) {
|
|
|
6731 |
if ($grader->id == $userid) {
|
|
|
6732 |
// Do not send self.
|
|
|
6733 |
continue;
|
|
|
6734 |
}
|
|
|
6735 |
if (groups_is_member($group->id, $grader->id)) {
|
|
|
6736 |
$graders[$grader->id] = $grader;
|
|
|
6737 |
}
|
|
|
6738 |
}
|
|
|
6739 |
}
|
|
|
6740 |
} else {
|
|
|
6741 |
// User not in group, try to find graders without group.
|
|
|
6742 |
foreach ($potentialgraders as $grader) {
|
|
|
6743 |
if ($grader->id == $userid) {
|
|
|
6744 |
// Do not send self.
|
|
|
6745 |
continue;
|
|
|
6746 |
}
|
|
|
6747 |
if (!groups_has_membership($this->get_course_module(), $grader->id)) {
|
|
|
6748 |
$graders[$grader->id] = $grader;
|
|
|
6749 |
}
|
|
|
6750 |
}
|
|
|
6751 |
}
|
|
|
6752 |
} else {
|
|
|
6753 |
foreach ($potentialgraders as $grader) {
|
|
|
6754 |
if ($grader->id == $userid) {
|
|
|
6755 |
// Do not send self.
|
|
|
6756 |
continue;
|
|
|
6757 |
}
|
|
|
6758 |
// Must be enrolled.
|
|
|
6759 |
if (is_enrolled($this->get_course_context(), $grader->id)) {
|
|
|
6760 |
$graders[$grader->id] = $grader;
|
|
|
6761 |
}
|
|
|
6762 |
}
|
|
|
6763 |
}
|
|
|
6764 |
return $graders;
|
|
|
6765 |
}
|
|
|
6766 |
|
|
|
6767 |
/**
|
|
|
6768 |
* Returns a list of users that should receive notification about given submission.
|
|
|
6769 |
*
|
|
|
6770 |
* @param int $userid The submission to grade
|
|
|
6771 |
* @return array
|
|
|
6772 |
*/
|
1254 |
ariadna |
6773 |
protected function get_notifiable_users($userid)
|
|
|
6774 |
{
|
1 |
efrain |
6775 |
// Potential users should be active users only.
|
1254 |
ariadna |
6776 |
$potentialusers = get_enrolled_users(
|
|
|
6777 |
$this->context,
|
|
|
6778 |
"mod/assign:receivegradernotifications",
|
|
|
6779 |
null,
|
|
|
6780 |
'u.*',
|
|
|
6781 |
null,
|
|
|
6782 |
null,
|
|
|
6783 |
null,
|
|
|
6784 |
true
|
|
|
6785 |
);
|
1 |
efrain |
6786 |
|
|
|
6787 |
$notifiableusers = array();
|
|
|
6788 |
if (groups_get_activity_groupmode($this->get_course_module()) == SEPARATEGROUPS) {
|
|
|
6789 |
if ($groups = groups_get_all_groups($this->get_course()->id, $userid, $this->get_course_module()->groupingid)) {
|
|
|
6790 |
foreach ($groups as $group) {
|
|
|
6791 |
foreach ($potentialusers as $potentialuser) {
|
|
|
6792 |
if ($potentialuser->id == $userid) {
|
|
|
6793 |
// Do not send self.
|
|
|
6794 |
continue;
|
|
|
6795 |
}
|
|
|
6796 |
if (groups_is_member($group->id, $potentialuser->id)) {
|
|
|
6797 |
$notifiableusers[$potentialuser->id] = $potentialuser;
|
|
|
6798 |
}
|
|
|
6799 |
}
|
|
|
6800 |
}
|
|
|
6801 |
} else {
|
|
|
6802 |
// User not in group, try to find graders without group.
|
|
|
6803 |
foreach ($potentialusers as $potentialuser) {
|
|
|
6804 |
if ($potentialuser->id == $userid) {
|
|
|
6805 |
// Do not send self.
|
|
|
6806 |
continue;
|
|
|
6807 |
}
|
|
|
6808 |
if (!groups_has_membership($this->get_course_module(), $potentialuser->id)) {
|
|
|
6809 |
$notifiableusers[$potentialuser->id] = $potentialuser;
|
|
|
6810 |
}
|
|
|
6811 |
}
|
|
|
6812 |
}
|
|
|
6813 |
} else {
|
|
|
6814 |
foreach ($potentialusers as $potentialuser) {
|
|
|
6815 |
if ($potentialuser->id == $userid) {
|
|
|
6816 |
// Do not send self.
|
|
|
6817 |
continue;
|
|
|
6818 |
}
|
|
|
6819 |
// Must be enrolled.
|
|
|
6820 |
if (is_enrolled($this->get_course_context(), $potentialuser->id)) {
|
|
|
6821 |
$notifiableusers[$potentialuser->id] = $potentialuser;
|
|
|
6822 |
}
|
|
|
6823 |
}
|
|
|
6824 |
}
|
|
|
6825 |
return $notifiableusers;
|
|
|
6826 |
}
|
|
|
6827 |
|
|
|
6828 |
/**
|
|
|
6829 |
* Format a notification for plain text.
|
|
|
6830 |
*
|
|
|
6831 |
* @param string $messagetype
|
|
|
6832 |
* @param stdClass $info
|
|
|
6833 |
* @param stdClass $course
|
|
|
6834 |
* @param stdClass $context
|
|
|
6835 |
* @param string $modulename
|
|
|
6836 |
* @param string $assignmentname
|
|
|
6837 |
*/
|
1254 |
ariadna |
6838 |
protected static function format_notification_message_text(
|
|
|
6839 |
$messagetype,
|
|
|
6840 |
$info,
|
|
|
6841 |
$course,
|
|
|
6842 |
$context,
|
|
|
6843 |
$modulename,
|
|
|
6844 |
$assignmentname
|
|
|
6845 |
) {
|
1 |
efrain |
6846 |
$formatparams = array('context' => $context->get_course_context());
|
|
|
6847 |
$posttext = format_string($course->shortname, true, $formatparams) .
|
1254 |
ariadna |
6848 |
' -> ' .
|
|
|
6849 |
$modulename .
|
|
|
6850 |
' -> ' .
|
|
|
6851 |
format_string($assignmentname, true, $formatparams) . "\n";
|
1 |
efrain |
6852 |
$posttext .= '---------------------------------------------------------------------' . "\n";
|
1254 |
ariadna |
6853 |
$posttext .= get_string($messagetype . 'text', 'assign', $info) . "\n";
|
1 |
efrain |
6854 |
$posttext .= "\n---------------------------------------------------------------------\n";
|
|
|
6855 |
return $posttext;
|
|
|
6856 |
}
|
|
|
6857 |
|
|
|
6858 |
/**
|
|
|
6859 |
* Format a notification for HTML.
|
|
|
6860 |
*
|
|
|
6861 |
* @param string $messagetype
|
|
|
6862 |
* @param stdClass $info
|
|
|
6863 |
* @param stdClass $course
|
|
|
6864 |
* @param stdClass $context
|
|
|
6865 |
* @param string $modulename
|
|
|
6866 |
* @param stdClass $coursemodule
|
|
|
6867 |
* @param string $assignmentname
|
|
|
6868 |
*/
|
1254 |
ariadna |
6869 |
protected static function format_notification_message_html(
|
|
|
6870 |
$messagetype,
|
|
|
6871 |
$info,
|
|
|
6872 |
$course,
|
|
|
6873 |
$context,
|
|
|
6874 |
$modulename,
|
|
|
6875 |
$coursemodule,
|
|
|
6876 |
$assignmentname
|
|
|
6877 |
) {
|
1 |
efrain |
6878 |
global $CFG;
|
|
|
6879 |
$formatparams = array('context' => $context->get_course_context());
|
|
|
6880 |
$posthtml = '<p><font face="sans-serif">' .
|
1254 |
ariadna |
6881 |
'<a href="' . $CFG->wwwroot . '/course/view.php?id=' . $course->id . '">' .
|
|
|
6882 |
format_string($course->shortname, true, $formatparams) .
|
|
|
6883 |
'</a> ->' .
|
|
|
6884 |
'<a href="' . $CFG->wwwroot . '/mod/assign/index.php?id=' . $course->id . '">' .
|
|
|
6885 |
$modulename .
|
|
|
6886 |
'</a> ->' .
|
|
|
6887 |
'<a href="' . $CFG->wwwroot . '/mod/assign/view.php?id=' . $coursemodule->id . '">' .
|
|
|
6888 |
format_string($assignmentname, true, $formatparams) .
|
|
|
6889 |
'</a></font></p>';
|
1 |
efrain |
6890 |
$posthtml .= '<hr /><font face="sans-serif">';
|
|
|
6891 |
$posthtml .= '<p>' . get_string($messagetype . 'html', 'assign', $info) . '</p>';
|
|
|
6892 |
$posthtml .= '</font><hr />';
|
|
|
6893 |
return $posthtml;
|
|
|
6894 |
}
|
|
|
6895 |
|
|
|
6896 |
/**
|
|
|
6897 |
* Message someone about something (static so it can be called from cron).
|
|
|
6898 |
*
|
|
|
6899 |
* @param stdClass $userfrom
|
|
|
6900 |
* @param stdClass $userto
|
|
|
6901 |
* @param string $messagetype
|
|
|
6902 |
* @param string $eventtype
|
|
|
6903 |
* @param int $updatetime
|
|
|
6904 |
* @param stdClass $coursemodule
|
|
|
6905 |
* @param stdClass $context
|
|
|
6906 |
* @param stdClass $course
|
|
|
6907 |
* @param string $modulename
|
|
|
6908 |
* @param string $assignmentname
|
|
|
6909 |
* @param bool $blindmarking
|
|
|
6910 |
* @param int $uniqueidforuser
|
|
|
6911 |
* @return void
|
|
|
6912 |
*/
|
1254 |
ariadna |
6913 |
public static function send_assignment_notification(
|
|
|
6914 |
$userfrom,
|
|
|
6915 |
$userto,
|
|
|
6916 |
$messagetype,
|
|
|
6917 |
$eventtype,
|
|
|
6918 |
$updatetime,
|
|
|
6919 |
$coursemodule,
|
|
|
6920 |
$context,
|
|
|
6921 |
$course,
|
|
|
6922 |
$modulename,
|
|
|
6923 |
$assignmentname,
|
|
|
6924 |
$blindmarking,
|
|
|
6925 |
$uniqueidforuser
|
|
|
6926 |
) {
|
1 |
efrain |
6927 |
global $CFG, $PAGE;
|
|
|
6928 |
|
|
|
6929 |
$info = new stdClass();
|
|
|
6930 |
if ($blindmarking) {
|
1254 |
ariadna |
6931 |
$userfrom = clone ($userfrom);
|
1 |
efrain |
6932 |
$info->username = get_string('participant', 'assign') . ' ' . $uniqueidforuser;
|
|
|
6933 |
$userfrom->firstname = get_string('participant', 'assign');
|
|
|
6934 |
$userfrom->lastname = $uniqueidforuser;
|
|
|
6935 |
$userfrom->email = $CFG->noreplyaddress;
|
|
|
6936 |
} else {
|
|
|
6937 |
$info->username = fullname($userfrom, true);
|
|
|
6938 |
}
|
1254 |
ariadna |
6939 |
$info->assignment = format_string($assignmentname, true, array('context' => $context));
|
|
|
6940 |
$info->url = $CFG->wwwroot . '/mod/assign/view.php?id=' . $coursemodule->id;
|
1 |
efrain |
6941 |
$info->timeupdated = userdate($updatetime, get_string('strftimerecentfull'));
|
|
|
6942 |
|
|
|
6943 |
$postsubject = get_string($messagetype . 'small', 'assign', $info);
|
1254 |
ariadna |
6944 |
$posttext = self::format_notification_message_text(
|
|
|
6945 |
$messagetype,
|
|
|
6946 |
$info,
|
|
|
6947 |
$course,
|
|
|
6948 |
$context,
|
|
|
6949 |
$modulename,
|
|
|
6950 |
$assignmentname
|
|
|
6951 |
);
|
1 |
efrain |
6952 |
$posthtml = '';
|
|
|
6953 |
if ($userto->mailformat == 1) {
|
1254 |
ariadna |
6954 |
$posthtml = self::format_notification_message_html(
|
|
|
6955 |
$messagetype,
|
|
|
6956 |
$info,
|
|
|
6957 |
$course,
|
|
|
6958 |
$context,
|
|
|
6959 |
$modulename,
|
|
|
6960 |
$coursemodule,
|
|
|
6961 |
$assignmentname
|
|
|
6962 |
);
|
1 |
efrain |
6963 |
}
|
|
|
6964 |
|
|
|
6965 |
$eventdata = new \core\message\message();
|
|
|
6966 |
$eventdata->courseid = $course->id;
|
|
|
6967 |
$eventdata->modulename = 'assign';
|
|
|
6968 |
$eventdata->userfrom = $userfrom;
|
|
|
6969 |
$eventdata->userto = $userto;
|
|
|
6970 |
$eventdata->subject = $postsubject;
|
|
|
6971 |
$eventdata->fullmessage = $posttext;
|
|
|
6972 |
$eventdata->fullmessageformat = FORMAT_PLAIN;
|
|
|
6973 |
$eventdata->fullmessagehtml = $posthtml;
|
|
|
6974 |
$eventdata->smallmessage = $postsubject;
|
|
|
6975 |
|
|
|
6976 |
$eventdata->name = $eventtype;
|
|
|
6977 |
$eventdata->component = 'mod_assign';
|
|
|
6978 |
$eventdata->notification = 1;
|
|
|
6979 |
$eventdata->contexturl = $info->url;
|
|
|
6980 |
$eventdata->contexturlname = $info->assignment;
|
|
|
6981 |
$customdata = [
|
|
|
6982 |
'cmid' => $coursemodule->id,
|
|
|
6983 |
'instance' => $coursemodule->instance,
|
|
|
6984 |
'messagetype' => $messagetype,
|
|
|
6985 |
'blindmarking' => $blindmarking,
|
|
|
6986 |
'uniqueidforuser' => $uniqueidforuser,
|
|
|
6987 |
];
|
|
|
6988 |
// Check if the userfrom is real and visible.
|
|
|
6989 |
if (!empty($userfrom->id) && core_user::is_real_user($userfrom->id)) {
|
|
|
6990 |
$userpicture = new user_picture($userfrom);
|
|
|
6991 |
$userpicture->size = 1; // Use f1 size.
|
|
|
6992 |
$userpicture->includetoken = $userto->id; // Generate an out-of-session token for the user receiving the message.
|
|
|
6993 |
$customdata['notificationiconurl'] = $userpicture->get_url($PAGE)->out(false);
|
|
|
6994 |
}
|
|
|
6995 |
$eventdata->customdata = $customdata;
|
|
|
6996 |
|
|
|
6997 |
message_send($eventdata);
|
|
|
6998 |
}
|
|
|
6999 |
|
|
|
7000 |
/**
|
|
|
7001 |
* Message someone about something.
|
|
|
7002 |
*
|
|
|
7003 |
* @param stdClass $userfrom
|
|
|
7004 |
* @param stdClass $userto
|
|
|
7005 |
* @param string $messagetype
|
|
|
7006 |
* @param string $eventtype
|
|
|
7007 |
* @param int $updatetime
|
|
|
7008 |
* @return void
|
|
|
7009 |
*/
|
1254 |
ariadna |
7010 |
public function send_notification($userfrom, $userto, $messagetype, $eventtype, $updatetime)
|
|
|
7011 |
{
|
1 |
efrain |
7012 |
global $USER;
|
|
|
7013 |
$userid = core_user::is_real_user($userfrom->id) ? $userfrom->id : $USER->id;
|
|
|
7014 |
$uniqueid = $this->get_uniqueid_for_user($userid);
|
1254 |
ariadna |
7015 |
self::send_assignment_notification(
|
|
|
7016 |
$userfrom,
|
|
|
7017 |
$userto,
|
|
|
7018 |
$messagetype,
|
|
|
7019 |
$eventtype,
|
|
|
7020 |
$updatetime,
|
|
|
7021 |
$this->get_course_module(),
|
|
|
7022 |
$this->get_context(),
|
|
|
7023 |
$this->get_course(),
|
|
|
7024 |
$this->get_module_name(),
|
|
|
7025 |
$this->get_instance()->name,
|
|
|
7026 |
$this->is_blind_marking(),
|
|
|
7027 |
$uniqueid
|
|
|
7028 |
);
|
1 |
efrain |
7029 |
}
|
|
|
7030 |
|
|
|
7031 |
/**
|
|
|
7032 |
* Notify student upon successful submission copy.
|
|
|
7033 |
*
|
|
|
7034 |
* @param stdClass $submission
|
|
|
7035 |
* @return void
|
|
|
7036 |
*/
|
1254 |
ariadna |
7037 |
protected function notify_student_submission_copied(stdClass $submission)
|
|
|
7038 |
{
|
1 |
efrain |
7039 |
global $DB, $USER;
|
|
|
7040 |
|
|
|
7041 |
$adminconfig = $this->get_admin_config();
|
|
|
7042 |
// Use the same setting for this - no need for another one.
|
|
|
7043 |
if (empty($adminconfig->submissionreceipts)) {
|
|
|
7044 |
// No need to do anything.
|
|
|
7045 |
return;
|
|
|
7046 |
}
|
|
|
7047 |
if ($submission->userid) {
|
1254 |
ariadna |
7048 |
$user = $DB->get_record('user', array('id' => $submission->userid), '*', MUST_EXIST);
|
1 |
efrain |
7049 |
} else {
|
|
|
7050 |
$user = $USER;
|
|
|
7051 |
}
|
1254 |
ariadna |
7052 |
$this->send_notification(
|
|
|
7053 |
$user,
|
|
|
7054 |
$user,
|
|
|
7055 |
'submissioncopied',
|
|
|
7056 |
'assign_notification',
|
|
|
7057 |
$submission->timemodified
|
|
|
7058 |
);
|
1 |
efrain |
7059 |
}
|
|
|
7060 |
/**
|
|
|
7061 |
* Notify student upon successful submission.
|
|
|
7062 |
*
|
|
|
7063 |
* @param stdClass $submission
|
|
|
7064 |
* @return void
|
|
|
7065 |
*/
|
1254 |
ariadna |
7066 |
protected function notify_student_submission_receipt(stdClass $submission)
|
|
|
7067 |
{
|
1 |
efrain |
7068 |
global $DB, $USER;
|
|
|
7069 |
|
|
|
7070 |
$adminconfig = $this->get_admin_config();
|
|
|
7071 |
if (empty($adminconfig->submissionreceipts)) {
|
|
|
7072 |
// No need to do anything.
|
|
|
7073 |
return;
|
|
|
7074 |
}
|
|
|
7075 |
if ($submission->userid) {
|
1254 |
ariadna |
7076 |
$user = $DB->get_record('user', array('id' => $submission->userid), '*', MUST_EXIST);
|
1 |
efrain |
7077 |
} else {
|
|
|
7078 |
$user = $USER;
|
|
|
7079 |
}
|
|
|
7080 |
if ($submission->userid == $USER->id) {
|
1254 |
ariadna |
7081 |
$this->send_notification(
|
|
|
7082 |
core_user::get_noreply_user(),
|
|
|
7083 |
$user,
|
|
|
7084 |
'submissionreceipt',
|
|
|
7085 |
'assign_notification',
|
|
|
7086 |
$submission->timemodified
|
|
|
7087 |
);
|
1 |
efrain |
7088 |
} else {
|
1254 |
ariadna |
7089 |
$this->send_notification(
|
|
|
7090 |
$USER,
|
|
|
7091 |
$user,
|
|
|
7092 |
'submissionreceiptother',
|
|
|
7093 |
'assign_notification',
|
|
|
7094 |
$submission->timemodified
|
|
|
7095 |
);
|
1 |
efrain |
7096 |
}
|
|
|
7097 |
}
|
|
|
7098 |
|
|
|
7099 |
/**
|
|
|
7100 |
* Send notifications to graders upon student submissions.
|
|
|
7101 |
*
|
|
|
7102 |
* @param stdClass $submission
|
|
|
7103 |
* @return void
|
|
|
7104 |
*/
|
1254 |
ariadna |
7105 |
protected function notify_graders(stdClass $submission)
|
|
|
7106 |
{
|
1 |
efrain |
7107 |
global $DB, $USER;
|
|
|
7108 |
|
|
|
7109 |
$instance = $this->get_instance();
|
|
|
7110 |
|
|
|
7111 |
$late = $instance->duedate && ($instance->duedate < time());
|
|
|
7112 |
|
|
|
7113 |
if (!$instance->sendnotifications && !($late && $instance->sendlatenotifications)) {
|
|
|
7114 |
// No need to do anything.
|
|
|
7115 |
return;
|
|
|
7116 |
}
|
|
|
7117 |
|
|
|
7118 |
if ($submission->userid) {
|
1254 |
ariadna |
7119 |
$user = $DB->get_record('user', array('id' => $submission->userid), '*', MUST_EXIST);
|
1 |
efrain |
7120 |
} else {
|
|
|
7121 |
$user = $USER;
|
|
|
7122 |
}
|
|
|
7123 |
|
|
|
7124 |
if ($notifyusers = $this->get_notifiable_users($user->id)) {
|
|
|
7125 |
foreach ($notifyusers as $notifyuser) {
|
1254 |
ariadna |
7126 |
$this->send_notification(
|
|
|
7127 |
$user,
|
|
|
7128 |
$notifyuser,
|
|
|
7129 |
'gradersubmissionupdated',
|
|
|
7130 |
'assign_notification',
|
|
|
7131 |
$submission->timemodified
|
|
|
7132 |
);
|
1 |
efrain |
7133 |
}
|
|
|
7134 |
}
|
|
|
7135 |
}
|
|
|
7136 |
|
|
|
7137 |
/**
|
|
|
7138 |
* Submit a submission for grading.
|
|
|
7139 |
*
|
|
|
7140 |
* @param stdClass $data - The form data
|
|
|
7141 |
* @param array $notices - List of error messages to display on an error condition.
|
|
|
7142 |
* @return bool Return false if the submission was not submitted.
|
|
|
7143 |
*/
|
1254 |
ariadna |
7144 |
public function submit_for_grading($data, $notices)
|
|
|
7145 |
{
|
1 |
efrain |
7146 |
global $USER;
|
|
|
7147 |
|
|
|
7148 |
$userid = $USER->id;
|
|
|
7149 |
if (!empty($data->userid)) {
|
|
|
7150 |
$userid = $data->userid;
|
|
|
7151 |
}
|
|
|
7152 |
// Need submit permission to submit an assignment.
|
|
|
7153 |
if ($userid == $USER->id) {
|
|
|
7154 |
require_capability('mod/assign:submit', $this->context);
|
|
|
7155 |
} else {
|
|
|
7156 |
if (!$this->can_edit_submission($userid, $USER->id)) {
|
|
|
7157 |
throw new \moodle_exception('nopermission');
|
|
|
7158 |
}
|
|
|
7159 |
}
|
|
|
7160 |
|
|
|
7161 |
$instance = $this->get_instance();
|
|
|
7162 |
|
|
|
7163 |
if ($instance->teamsubmission) {
|
|
|
7164 |
$submission = $this->get_group_submission($userid, 0, true);
|
|
|
7165 |
} else {
|
|
|
7166 |
$submission = $this->get_user_submission($userid, true);
|
|
|
7167 |
}
|
|
|
7168 |
|
|
|
7169 |
if (!$this->submissions_open($userid)) {
|
|
|
7170 |
$notices[] = get_string('submissionsclosed', 'assign');
|
|
|
7171 |
return false;
|
|
|
7172 |
}
|
|
|
7173 |
|
|
|
7174 |
$adminconfig = $this->get_admin_config();
|
|
|
7175 |
|
|
|
7176 |
$submissionstatement = '';
|
|
|
7177 |
if ($instance->requiresubmissionstatement) {
|
|
|
7178 |
$submissionstatement = $this->get_submissionstatement($adminconfig, $instance, $this->context);
|
|
|
7179 |
}
|
|
|
7180 |
|
1254 |
ariadna |
7181 |
if (
|
|
|
7182 |
!empty($submissionstatement) && $instance->requiresubmissionstatement
|
|
|
7183 |
&& empty($data->submissionstatement) && $USER->id == $userid
|
|
|
7184 |
) {
|
1 |
efrain |
7185 |
return false;
|
|
|
7186 |
}
|
|
|
7187 |
|
|
|
7188 |
if ($submission->status != ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
|
|
|
7189 |
// Give each submission plugin a chance to process the submission.
|
|
|
7190 |
$plugins = $this->get_submission_plugins();
|
|
|
7191 |
foreach ($plugins as $plugin) {
|
|
|
7192 |
if ($plugin->is_enabled() && $plugin->is_visible()) {
|
|
|
7193 |
$plugin->submit_for_grading($submission);
|
|
|
7194 |
}
|
|
|
7195 |
}
|
|
|
7196 |
|
|
|
7197 |
$submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
|
|
|
7198 |
$this->update_submission($submission, $userid, true, $instance->teamsubmission);
|
|
|
7199 |
$completion = new completion_info($this->get_course());
|
|
|
7200 |
if ($completion->is_enabled($this->get_course_module()) && $instance->completionsubmit) {
|
1254 |
ariadna |
7201 |
$this->update_activity_completion_records(
|
|
|
7202 |
$instance->teamsubmission,
|
|
|
7203 |
$instance->requireallteammemberssubmit,
|
|
|
7204 |
$submission,
|
|
|
7205 |
$userid,
|
|
|
7206 |
COMPLETION_COMPLETE,
|
|
|
7207 |
$completion
|
|
|
7208 |
);
|
1 |
efrain |
7209 |
}
|
|
|
7210 |
|
|
|
7211 |
if (!empty($data->submissionstatement) && $USER->id == $userid) {
|
|
|
7212 |
\mod_assign\event\statement_accepted::create_from_submission($this, $submission)->trigger();
|
|
|
7213 |
}
|
|
|
7214 |
$this->notify_graders($submission);
|
|
|
7215 |
$this->notify_student_submission_receipt($submission);
|
|
|
7216 |
|
|
|
7217 |
\mod_assign\event\assessable_submitted::create_from_submission($this, $submission, false)->trigger();
|
|
|
7218 |
|
|
|
7219 |
return true;
|
|
|
7220 |
}
|
|
|
7221 |
$notices[] = get_string('submissionsclosed', 'assign');
|
|
|
7222 |
return false;
|
|
|
7223 |
}
|
|
|
7224 |
|
|
|
7225 |
/**
|
|
|
7226 |
* A students submission is submitted for grading by a teacher.
|
|
|
7227 |
*
|
|
|
7228 |
* @return bool
|
|
|
7229 |
*/
|
1254 |
ariadna |
7230 |
protected function process_submit_other_for_grading($mform, $notices)
|
|
|
7231 |
{
|
1 |
efrain |
7232 |
global $USER, $CFG;
|
|
|
7233 |
|
|
|
7234 |
require_sesskey();
|
|
|
7235 |
|
|
|
7236 |
$userid = optional_param('userid', $USER->id, PARAM_INT);
|
|
|
7237 |
|
|
|
7238 |
if (!$this->submissions_open($userid)) {
|
|
|
7239 |
$notices[] = get_string('submissionsclosed', 'assign');
|
|
|
7240 |
return false;
|
|
|
7241 |
}
|
|
|
7242 |
$data = new stdClass();
|
|
|
7243 |
$data->userid = $userid;
|
|
|
7244 |
return $this->submit_for_grading($data, $notices);
|
|
|
7245 |
}
|
|
|
7246 |
|
|
|
7247 |
/**
|
|
|
7248 |
* Assignment submission is processed before grading.
|
|
|
7249 |
*
|
|
|
7250 |
* @param moodleform|null $mform If validation failed when submitting this form - this is the moodleform.
|
|
|
7251 |
* It can be null.
|
|
|
7252 |
* @return bool Return false if the validation fails. This affects which page is displayed next.
|
|
|
7253 |
*/
|
1254 |
ariadna |
7254 |
protected function process_submit_for_grading($mform, $notices)
|
|
|
7255 |
{
|
1 |
efrain |
7256 |
global $CFG;
|
|
|
7257 |
|
|
|
7258 |
require_once($CFG->dirroot . '/mod/assign/submissionconfirmform.php');
|
|
|
7259 |
require_sesskey();
|
|
|
7260 |
|
|
|
7261 |
if (!$this->submissions_open()) {
|
|
|
7262 |
$notices[] = get_string('submissionsclosed', 'assign');
|
|
|
7263 |
return false;
|
|
|
7264 |
}
|
|
|
7265 |
|
|
|
7266 |
$data = new stdClass();
|
|
|
7267 |
$adminconfig = $this->get_admin_config();
|
|
|
7268 |
$requiresubmissionstatement = $this->get_instance()->requiresubmissionstatement;
|
|
|
7269 |
|
|
|
7270 |
$submissionstatement = '';
|
|
|
7271 |
if ($requiresubmissionstatement) {
|
|
|
7272 |
$submissionstatement = $this->get_submissionstatement($adminconfig, $this->get_instance(), $this->get_context());
|
|
|
7273 |
}
|
|
|
7274 |
|
|
|
7275 |
// If we get back an empty submission statement, we have to set $requiredsubmisisonstatement to false to prevent
|
|
|
7276 |
// that the submission statement checkbox will be displayed.
|
|
|
7277 |
if (empty($submissionstatement)) {
|
|
|
7278 |
$requiresubmissionstatement = false;
|
|
|
7279 |
}
|
|
|
7280 |
|
|
|
7281 |
if ($mform == null) {
|
1254 |
ariadna |
7282 |
$mform = new mod_assign_confirm_submission_form(null, array(
|
|
|
7283 |
$requiresubmissionstatement,
|
|
|
7284 |
$submissionstatement,
|
|
|
7285 |
$this->get_course_module()->id,
|
|
|
7286 |
$data
|
|
|
7287 |
));
|
1 |
efrain |
7288 |
}
|
|
|
7289 |
|
|
|
7290 |
$data = $mform->get_data();
|
|
|
7291 |
if (!$mform->is_cancelled()) {
|
|
|
7292 |
if ($mform->get_data() == false) {
|
|
|
7293 |
return false;
|
|
|
7294 |
}
|
|
|
7295 |
return $this->submit_for_grading($data, $notices);
|
|
|
7296 |
}
|
|
|
7297 |
return true;
|
|
|
7298 |
}
|
|
|
7299 |
|
|
|
7300 |
/**
|
|
|
7301 |
* Save the extension date for a single user.
|
|
|
7302 |
*
|
|
|
7303 |
* @param int $userid The user id
|
|
|
7304 |
* @param mixed $extensionduedate Either an integer date or null
|
|
|
7305 |
* @return boolean
|
|
|
7306 |
*/
|
1254 |
ariadna |
7307 |
public function save_user_extension($userid, $extensionduedate)
|
|
|
7308 |
{
|
1 |
efrain |
7309 |
global $DB;
|
|
|
7310 |
|
|
|
7311 |
// Need submit permission to submit an assignment.
|
|
|
7312 |
require_capability('mod/assign:grantextension', $this->context);
|
|
|
7313 |
|
|
|
7314 |
if (!is_enrolled($this->get_course_context(), $userid)) {
|
|
|
7315 |
return false;
|
|
|
7316 |
}
|
|
|
7317 |
if (!has_capability('mod/assign:submit', $this->context, $userid)) {
|
|
|
7318 |
return false;
|
|
|
7319 |
}
|
|
|
7320 |
|
|
|
7321 |
if ($this->get_instance()->duedate && $extensionduedate) {
|
|
|
7322 |
if ($this->get_instance()->duedate > $extensionduedate) {
|
|
|
7323 |
return false;
|
|
|
7324 |
}
|
|
|
7325 |
}
|
|
|
7326 |
if ($this->get_instance()->allowsubmissionsfromdate && $extensionduedate) {
|
|
|
7327 |
if ($this->get_instance()->allowsubmissionsfromdate > $extensionduedate) {
|
|
|
7328 |
return false;
|
|
|
7329 |
}
|
|
|
7330 |
}
|
|
|
7331 |
|
|
|
7332 |
$flags = $this->get_user_flags($userid, true);
|
|
|
7333 |
$flags->extensionduedate = $extensionduedate;
|
|
|
7334 |
|
|
|
7335 |
$result = $this->update_user_flags($flags);
|
|
|
7336 |
|
|
|
7337 |
if ($result) {
|
|
|
7338 |
\mod_assign\event\extension_granted::create_from_assign($this, $userid)->trigger();
|
|
|
7339 |
}
|
|
|
7340 |
return $result;
|
|
|
7341 |
}
|
|
|
7342 |
|
|
|
7343 |
/**
|
|
|
7344 |
* Save extension date.
|
|
|
7345 |
*
|
|
|
7346 |
* @param moodleform $mform The submitted form
|
|
|
7347 |
* @return boolean
|
|
|
7348 |
*/
|
1254 |
ariadna |
7349 |
protected function process_save_extension(&$mform)
|
|
|
7350 |
{
|
1 |
efrain |
7351 |
global $DB, $CFG;
|
|
|
7352 |
|
|
|
7353 |
// Include extension form.
|
|
|
7354 |
require_once($CFG->dirroot . '/mod/assign/extensionform.php');
|
|
|
7355 |
require_sesskey();
|
|
|
7356 |
|
|
|
7357 |
$users = optional_param('userid', 0, PARAM_INT);
|
|
|
7358 |
if (!$users) {
|
|
|
7359 |
$users = required_param('selectedusers', PARAM_SEQUENCE);
|
|
|
7360 |
}
|
|
|
7361 |
$userlist = explode(',', $users);
|
|
|
7362 |
|
|
|
7363 |
$keys = array('duedate', 'cutoffdate', 'allowsubmissionsfromdate');
|
|
|
7364 |
$maxoverride = array('allowsubmissionsfromdate' => 0, 'duedate' => 0, 'cutoffdate' => 0);
|
|
|
7365 |
foreach ($userlist as $userid) {
|
|
|
7366 |
// To validate extension date with users overrides.
|
|
|
7367 |
$override = $this->override_exists($userid);
|
|
|
7368 |
foreach ($keys as $key) {
|
|
|
7369 |
if ($override->{$key}) {
|
|
|
7370 |
if ($maxoverride[$key] < $override->{$key}) {
|
|
|
7371 |
$maxoverride[$key] = $override->{$key};
|
|
|
7372 |
}
|
|
|
7373 |
} else if ($maxoverride[$key] < $this->get_instance()->{$key}) {
|
|
|
7374 |
$maxoverride[$key] = $this->get_instance()->{$key};
|
|
|
7375 |
}
|
|
|
7376 |
}
|
|
|
7377 |
}
|
|
|
7378 |
foreach ($keys as $key) {
|
|
|
7379 |
if ($maxoverride[$key]) {
|
|
|
7380 |
$this->get_instance()->{$key} = $maxoverride[$key];
|
|
|
7381 |
}
|
|
|
7382 |
}
|
|
|
7383 |
|
|
|
7384 |
$formparams = array(
|
|
|
7385 |
'instance' => $this->get_instance(),
|
|
|
7386 |
'assign' => $this,
|
|
|
7387 |
'userlist' => $userlist
|
|
|
7388 |
);
|
|
|
7389 |
|
|
|
7390 |
$mform = new mod_assign_extension_form(null, $formparams);
|
|
|
7391 |
|
|
|
7392 |
if ($mform->is_cancelled()) {
|
|
|
7393 |
return true;
|
|
|
7394 |
}
|
|
|
7395 |
|
|
|
7396 |
if ($formdata = $mform->get_data()) {
|
|
|
7397 |
if (!empty($formdata->selectedusers)) {
|
|
|
7398 |
$users = explode(',', $formdata->selectedusers);
|
|
|
7399 |
$result = true;
|
|
|
7400 |
foreach ($users as $userid) {
|
|
|
7401 |
$user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
|
|
|
7402 |
$result = $this->save_user_extension($user->id, $formdata->extensionduedate) && $result;
|
|
|
7403 |
}
|
|
|
7404 |
return $result;
|
|
|
7405 |
}
|
|
|
7406 |
if (!empty($formdata->userid)) {
|
|
|
7407 |
$user = $DB->get_record('user', array('id' => $formdata->userid), '*', MUST_EXIST);
|
|
|
7408 |
return $this->save_user_extension($user->id, $formdata->extensionduedate);
|
|
|
7409 |
}
|
|
|
7410 |
}
|
|
|
7411 |
|
|
|
7412 |
return false;
|
|
|
7413 |
}
|
|
|
7414 |
|
|
|
7415 |
/**
|
|
|
7416 |
* Save quick grades.
|
|
|
7417 |
*
|
|
|
7418 |
* @return string The result of the save operation
|
|
|
7419 |
*/
|
1254 |
ariadna |
7420 |
protected function process_save_quick_grades()
|
|
|
7421 |
{
|
1 |
efrain |
7422 |
global $USER, $DB, $CFG;
|
|
|
7423 |
|
|
|
7424 |
// Need grade permission.
|
|
|
7425 |
require_capability('mod/assign:grade', $this->context);
|
|
|
7426 |
require_sesskey();
|
|
|
7427 |
|
|
|
7428 |
// Make sure advanced grading is disabled.
|
|
|
7429 |
$gradingmanager = get_grading_manager($this->get_context(), 'mod_assign', 'submissions');
|
|
|
7430 |
$controller = $gradingmanager->get_active_controller();
|
|
|
7431 |
if (!empty($controller)) {
|
|
|
7432 |
$message = get_string('errorquickgradingvsadvancedgrading', 'assign');
|
|
|
7433 |
$this->set_error_message($message);
|
|
|
7434 |
return $message;
|
|
|
7435 |
}
|
|
|
7436 |
|
|
|
7437 |
$users = array();
|
|
|
7438 |
// First check all the last modified values.
|
|
|
7439 |
$currentgroup = groups_get_activity_group($this->get_course_module(), true);
|
|
|
7440 |
$participants = $this->list_participants($currentgroup, true);
|
|
|
7441 |
|
|
|
7442 |
// Gets a list of possible users and look for values based upon that.
|
|
|
7443 |
foreach ($participants as $userid => $unused) {
|
|
|
7444 |
$modified = optional_param('grademodified_' . $userid, -1, PARAM_INT);
|
|
|
7445 |
$attemptnumber = optional_param('gradeattempt_' . $userid, -1, PARAM_INT);
|
|
|
7446 |
// Gather the userid, updated grade and last modified value.
|
|
|
7447 |
$record = new stdClass();
|
|
|
7448 |
$record->userid = $userid;
|
|
|
7449 |
if ($modified >= 0) {
|
|
|
7450 |
$record->grade = unformat_float(optional_param('quickgrade_' . $record->userid, -1, PARAM_TEXT));
|
1254 |
ariadna |
7451 |
$record->workflowstate = optional_param('quickgrade_' . $record->userid . '_workflowstate', false, PARAM_ALPHA);
|
|
|
7452 |
$record->allocatedmarker = optional_param('quickgrade_' . $record->userid . '_allocatedmarker', false, PARAM_INT);
|
1 |
efrain |
7453 |
} else {
|
|
|
7454 |
// This user was not in the grading table.
|
|
|
7455 |
continue;
|
|
|
7456 |
}
|
|
|
7457 |
$record->attemptnumber = $attemptnumber;
|
|
|
7458 |
$record->lastmodified = $modified;
|
1254 |
ariadna |
7459 |
$record->gradinginfo = grade_get_grades(
|
|
|
7460 |
$this->get_course()->id,
|
|
|
7461 |
'mod',
|
|
|
7462 |
'assign',
|
|
|
7463 |
$this->get_instance()->id,
|
|
|
7464 |
array($userid)
|
|
|
7465 |
);
|
1 |
efrain |
7466 |
$users[$userid] = $record;
|
|
|
7467 |
}
|
|
|
7468 |
|
|
|
7469 |
if (empty($users)) {
|
|
|
7470 |
$message = get_string('nousersselected', 'assign');
|
|
|
7471 |
$this->set_error_message($message);
|
|
|
7472 |
return $message;
|
|
|
7473 |
}
|
|
|
7474 |
|
|
|
7475 |
list($userids, $params) = $DB->get_in_or_equal(array_keys($users), SQL_PARAMS_NAMED);
|
|
|
7476 |
$params['assignid1'] = $this->get_instance()->id;
|
|
|
7477 |
$params['assignid2'] = $this->get_instance()->id;
|
|
|
7478 |
|
|
|
7479 |
// Check them all for currency.
|
|
|
7480 |
$grademaxattempt = 'SELECT s.userid, s.attemptnumber AS maxattempt
|
|
|
7481 |
FROM {assign_submission} s
|
|
|
7482 |
WHERE s.assignment = :assignid1 AND s.latest = 1';
|
|
|
7483 |
|
|
|
7484 |
$sql = 'SELECT u.id AS userid, g.grade AS grade, g.timemodified AS lastmodified,
|
|
|
7485 |
uf.workflowstate, uf.allocatedmarker, gmx.maxattempt AS attemptnumber
|
|
|
7486 |
FROM {user} u
|
|
|
7487 |
LEFT JOIN ( ' . $grademaxattempt . ' ) gmx ON u.id = gmx.userid
|
|
|
7488 |
LEFT JOIN {assign_grades} g ON
|
|
|
7489 |
u.id = g.userid AND
|
|
|
7490 |
g.assignment = :assignid2 AND
|
|
|
7491 |
g.attemptnumber = gmx.maxattempt
|
|
|
7492 |
LEFT JOIN {assign_user_flags} uf ON uf.assignment = g.assignment AND uf.userid = g.userid
|
|
|
7493 |
WHERE u.id ' . $userids;
|
|
|
7494 |
$currentgrades = $DB->get_recordset_sql($sql, $params);
|
|
|
7495 |
|
|
|
7496 |
$modifiedusers = array();
|
|
|
7497 |
foreach ($currentgrades as $current) {
|
|
|
7498 |
$modified = $users[(int)$current->userid];
|
|
|
7499 |
$grade = $this->get_user_grade($modified->userid, false);
|
|
|
7500 |
// Check to see if the grade column was even visible.
|
|
|
7501 |
$gradecolpresent = optional_param('quickgrade_' . $modified->userid, false, PARAM_INT) !== false;
|
|
|
7502 |
|
|
|
7503 |
// Check to see if the outcomes were modified.
|
|
|
7504 |
if ($CFG->enableoutcomes) {
|
|
|
7505 |
foreach ($modified->gradinginfo->outcomes as $outcomeid => $outcome) {
|
|
|
7506 |
$oldoutcome = $outcome->grades[$modified->userid]->grade;
|
|
|
7507 |
$paramname = 'outcome_' . $outcomeid . '_' . $modified->userid;
|
|
|
7508 |
$newoutcome = optional_param($paramname, -1, PARAM_FLOAT);
|
|
|
7509 |
// Check to see if the outcome column was even visible.
|
|
|
7510 |
$outcomecolpresent = optional_param($paramname, false, PARAM_FLOAT) !== false;
|
|
|
7511 |
if ($outcomecolpresent && ($oldoutcome != $newoutcome)) {
|
|
|
7512 |
// Can't check modified time for outcomes because it is not reported.
|
|
|
7513 |
$modifiedusers[$modified->userid] = $modified;
|
|
|
7514 |
continue;
|
|
|
7515 |
}
|
|
|
7516 |
}
|
|
|
7517 |
}
|
|
|
7518 |
|
|
|
7519 |
// Let plugins participate.
|
|
|
7520 |
foreach ($this->feedbackplugins as $plugin) {
|
|
|
7521 |
if ($plugin->is_visible() && $plugin->is_enabled() && $plugin->supports_quickgrading()) {
|
|
|
7522 |
// The plugins must handle is_quickgrading_modified correctly - ie
|
|
|
7523 |
// handle hidden columns.
|
|
|
7524 |
if ($plugin->is_quickgrading_modified($modified->userid, $grade)) {
|
|
|
7525 |
if ((int)$current->lastmodified > (int)$modified->lastmodified) {
|
|
|
7526 |
$message = get_string('errorrecordmodified', 'assign');
|
|
|
7527 |
$this->set_error_message($message);
|
|
|
7528 |
return $message;
|
|
|
7529 |
} else {
|
|
|
7530 |
$modifiedusers[$modified->userid] = $modified;
|
|
|
7531 |
continue;
|
|
|
7532 |
}
|
|
|
7533 |
}
|
|
|
7534 |
}
|
|
|
7535 |
}
|
|
|
7536 |
|
|
|
7537 |
if (($current->grade < 0 || $current->grade === null) &&
|
1254 |
ariadna |
7538 |
($modified->grade < 0 || $modified->grade === null)
|
|
|
7539 |
) {
|
1 |
efrain |
7540 |
// Different ways to indicate no grade.
|
|
|
7541 |
$modified->grade = $current->grade; // Keep existing grade.
|
|
|
7542 |
}
|
|
|
7543 |
// Treat 0 and null as different values.
|
|
|
7544 |
if ($current->grade !== null) {
|
|
|
7545 |
$current->grade = floatval($current->grade);
|
|
|
7546 |
}
|
|
|
7547 |
$gradechanged = $gradecolpresent && grade_floats_different($current->grade, $modified->grade);
|
|
|
7548 |
$markingallocationchanged = $this->get_instance()->markingworkflow &&
|
1254 |
ariadna |
7549 |
$this->get_instance()->markingallocation &&
|
|
|
7550 |
($modified->allocatedmarker !== false) &&
|
|
|
7551 |
($current->allocatedmarker != $modified->allocatedmarker);
|
1 |
efrain |
7552 |
$workflowstatechanged = $this->get_instance()->markingworkflow &&
|
1254 |
ariadna |
7553 |
($modified->workflowstate !== false) &&
|
|
|
7554 |
($current->workflowstate != $modified->workflowstate);
|
1 |
efrain |
7555 |
if ($gradechanged || $markingallocationchanged || $workflowstatechanged) {
|
|
|
7556 |
// Grade changed.
|
|
|
7557 |
if ($this->grading_disabled($modified->userid)) {
|
|
|
7558 |
continue;
|
|
|
7559 |
}
|
|
|
7560 |
$badmodified = (int)$current->lastmodified > (int)$modified->lastmodified;
|
|
|
7561 |
$badattempt = (int)$current->attemptnumber != (int)$modified->attemptnumber;
|
|
|
7562 |
if ($badmodified || $badattempt) {
|
|
|
7563 |
// Error - record has been modified since viewing the page.
|
|
|
7564 |
$message = get_string('errorrecordmodified', 'assign');
|
|
|
7565 |
$this->set_error_message($message);
|
|
|
7566 |
return $message;
|
|
|
7567 |
} else {
|
|
|
7568 |
$modifiedusers[$modified->userid] = $modified;
|
|
|
7569 |
}
|
|
|
7570 |
}
|
|
|
7571 |
}
|
|
|
7572 |
$currentgrades->close();
|
|
|
7573 |
|
|
|
7574 |
$adminconfig = $this->get_admin_config();
|
|
|
7575 |
$gradebookplugin = $adminconfig->feedback_plugin_for_gradebook;
|
|
|
7576 |
|
|
|
7577 |
// Ok - ready to process the updates.
|
|
|
7578 |
foreach ($modifiedusers as $userid => $modified) {
|
|
|
7579 |
$grade = $this->get_user_grade($userid, true);
|
|
|
7580 |
$flags = $this->get_user_flags($userid, true);
|
1254 |
ariadna |
7581 |
$grade->grade = grade_floatval(unformat_float($modified->grade));
|
|
|
7582 |
$grade->grader = $USER->id;
|
1 |
efrain |
7583 |
$gradecolpresent = optional_param('quickgrade_' . $userid, false, PARAM_INT) !== false;
|
|
|
7584 |
|
|
|
7585 |
// Save plugins data.
|
|
|
7586 |
foreach ($this->feedbackplugins as $plugin) {
|
|
|
7587 |
if ($plugin->is_visible() && $plugin->is_enabled() && $plugin->supports_quickgrading()) {
|
|
|
7588 |
$plugin->save_quickgrading_changes($userid, $grade);
|
|
|
7589 |
if (('assignfeedback_' . $plugin->get_type()) == $gradebookplugin) {
|
|
|
7590 |
// This is the feedback plugin chose to push comments to the gradebook.
|
|
|
7591 |
$grade->feedbacktext = $plugin->text_for_gradebook($grade);
|
|
|
7592 |
$grade->feedbackformat = $plugin->format_for_gradebook($grade);
|
|
|
7593 |
$grade->feedbackfiles = $plugin->files_for_gradebook($grade);
|
|
|
7594 |
}
|
|
|
7595 |
}
|
|
|
7596 |
}
|
|
|
7597 |
|
|
|
7598 |
// These will be set to false if they are not present in the quickgrading
|
|
|
7599 |
// form (e.g. column hidden).
|
|
|
7600 |
$workflowstatemodified = ($modified->workflowstate !== false) &&
|
1254 |
ariadna |
7601 |
($flags->workflowstate != $modified->workflowstate);
|
1 |
efrain |
7602 |
|
|
|
7603 |
$allocatedmarkermodified = ($modified->allocatedmarker !== false) &&
|
1254 |
ariadna |
7604 |
($flags->allocatedmarker != $modified->allocatedmarker);
|
1 |
efrain |
7605 |
|
|
|
7606 |
if ($workflowstatemodified) {
|
|
|
7607 |
$flags->workflowstate = $modified->workflowstate;
|
|
|
7608 |
}
|
|
|
7609 |
if ($allocatedmarkermodified) {
|
|
|
7610 |
$flags->allocatedmarker = $modified->allocatedmarker;
|
|
|
7611 |
}
|
|
|
7612 |
if ($workflowstatemodified || $allocatedmarkermodified) {
|
|
|
7613 |
if ($this->update_user_flags($flags) && $workflowstatemodified) {
|
|
|
7614 |
$user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
|
|
|
7615 |
\mod_assign\event\workflow_state_updated::create_from_user($this, $user, $flags->workflowstate)->trigger();
|
|
|
7616 |
}
|
|
|
7617 |
}
|
|
|
7618 |
$this->update_grade($grade);
|
|
|
7619 |
|
|
|
7620 |
// Allow teachers to skip sending notifications.
|
|
|
7621 |
if (optional_param('sendstudentnotifications', true, PARAM_BOOL)) {
|
|
|
7622 |
$this->notify_grade_modified($grade, true);
|
|
|
7623 |
}
|
|
|
7624 |
|
|
|
7625 |
// Save outcomes.
|
|
|
7626 |
if ($CFG->enableoutcomes) {
|
|
|
7627 |
$data = array();
|
|
|
7628 |
foreach ($modified->gradinginfo->outcomes as $outcomeid => $outcome) {
|
|
|
7629 |
$oldoutcome = $outcome->grades[$modified->userid]->grade;
|
|
|
7630 |
$paramname = 'outcome_' . $outcomeid . '_' . $modified->userid;
|
|
|
7631 |
// This will be false if the input was not in the quickgrading
|
|
|
7632 |
// form (e.g. column hidden).
|
|
|
7633 |
$newoutcome = optional_param($paramname, false, PARAM_INT);
|
|
|
7634 |
if ($newoutcome !== false && ($oldoutcome != $newoutcome)) {
|
|
|
7635 |
$data[$outcomeid] = $newoutcome;
|
|
|
7636 |
}
|
|
|
7637 |
}
|
|
|
7638 |
if (count($data) > 0) {
|
1254 |
ariadna |
7639 |
grade_update_outcomes(
|
|
|
7640 |
'mod/assign',
|
|
|
7641 |
$this->course->id,
|
|
|
7642 |
'mod',
|
|
|
7643 |
'assign',
|
|
|
7644 |
$this->get_instance()->id,
|
|
|
7645 |
$userid,
|
|
|
7646 |
$data
|
|
|
7647 |
);
|
1 |
efrain |
7648 |
}
|
|
|
7649 |
}
|
|
|
7650 |
}
|
|
|
7651 |
|
|
|
7652 |
return get_string('quickgradingchangessaved', 'assign');
|
|
|
7653 |
}
|
|
|
7654 |
|
|
|
7655 |
/**
|
|
|
7656 |
* Reveal student identities to markers (and the gradebook).
|
|
|
7657 |
*
|
|
|
7658 |
* @return void
|
|
|
7659 |
*/
|
1254 |
ariadna |
7660 |
public function reveal_identities()
|
|
|
7661 |
{
|
1 |
efrain |
7662 |
global $DB;
|
|
|
7663 |
|
|
|
7664 |
require_capability('mod/assign:revealidentities', $this->context);
|
|
|
7665 |
|
|
|
7666 |
if ($this->get_instance()->revealidentities || empty($this->get_instance()->blindmarking)) {
|
|
|
7667 |
return false;
|
|
|
7668 |
}
|
|
|
7669 |
|
|
|
7670 |
// Update the assignment record.
|
|
|
7671 |
$update = new stdClass();
|
|
|
7672 |
$update->id = $this->get_instance()->id;
|
|
|
7673 |
$update->revealidentities = 1;
|
|
|
7674 |
$DB->update_record('assign', $update);
|
|
|
7675 |
|
|
|
7676 |
// Refresh the instance data.
|
|
|
7677 |
$this->instance = null;
|
|
|
7678 |
|
|
|
7679 |
// Release the grades to the gradebook.
|
|
|
7680 |
// First create the column in the gradebook.
|
|
|
7681 |
$this->update_gradebook(false, $this->get_course_module()->id);
|
|
|
7682 |
|
|
|
7683 |
// Now release all grades.
|
|
|
7684 |
|
|
|
7685 |
$adminconfig = $this->get_admin_config();
|
|
|
7686 |
$gradebookplugin = $adminconfig->feedback_plugin_for_gradebook;
|
|
|
7687 |
$gradebookplugin = str_replace('assignfeedback_', '', $gradebookplugin);
|
1254 |
ariadna |
7688 |
$grades = $DB->get_records('assign_grades', array('assignment' => $this->get_instance()->id));
|
1 |
efrain |
7689 |
|
|
|
7690 |
$plugin = $this->get_feedback_plugin_by_type($gradebookplugin);
|
|
|
7691 |
|
|
|
7692 |
foreach ($grades as $grade) {
|
|
|
7693 |
// Fetch any comments for this student.
|
|
|
7694 |
if ($plugin && $plugin->is_enabled() && $plugin->is_visible()) {
|
|
|
7695 |
$grade->feedbacktext = $plugin->text_for_gradebook($grade);
|
|
|
7696 |
$grade->feedbackformat = $plugin->format_for_gradebook($grade);
|
|
|
7697 |
$grade->feedbackfiles = $plugin->files_for_gradebook($grade);
|
|
|
7698 |
}
|
|
|
7699 |
$this->gradebook_item_update(null, $grade);
|
|
|
7700 |
}
|
|
|
7701 |
|
|
|
7702 |
\mod_assign\event\identities_revealed::create_from_assign($this)->trigger();
|
|
|
7703 |
}
|
|
|
7704 |
|
|
|
7705 |
/**
|
|
|
7706 |
* Reveal student identities to markers (and the gradebook).
|
|
|
7707 |
*
|
|
|
7708 |
* @return void
|
|
|
7709 |
*/
|
1254 |
ariadna |
7710 |
protected function process_reveal_identities()
|
|
|
7711 |
{
|
1 |
efrain |
7712 |
|
|
|
7713 |
if (!confirm_sesskey()) {
|
|
|
7714 |
return false;
|
|
|
7715 |
}
|
|
|
7716 |
|
|
|
7717 |
return $this->reveal_identities();
|
|
|
7718 |
}
|
|
|
7719 |
|
|
|
7720 |
|
|
|
7721 |
/**
|
|
|
7722 |
* Save grading options.
|
|
|
7723 |
*
|
|
|
7724 |
* @return void
|
|
|
7725 |
*/
|
1254 |
ariadna |
7726 |
protected function process_save_grading_options()
|
|
|
7727 |
{
|
1 |
efrain |
7728 |
global $USER, $CFG;
|
|
|
7729 |
|
|
|
7730 |
// Include grading options form.
|
|
|
7731 |
require_once($CFG->dirroot . '/mod/assign/gradingoptionsform.php');
|
|
|
7732 |
|
|
|
7733 |
// Need submit permission to submit an assignment.
|
|
|
7734 |
$this->require_view_grades();
|
|
|
7735 |
require_sesskey();
|
|
|
7736 |
|
|
|
7737 |
// Is advanced grading enabled?
|
|
|
7738 |
$gradingmanager = get_grading_manager($this->get_context(), 'mod_assign', 'submissions');
|
|
|
7739 |
$controller = $gradingmanager->get_active_controller();
|
|
|
7740 |
$showquickgrading = empty($controller);
|
|
|
7741 |
if (!is_null($this->context)) {
|
|
|
7742 |
$showonlyactiveenrolopt = has_capability('moodle/course:viewsuspendedusers', $this->context);
|
|
|
7743 |
} else {
|
|
|
7744 |
$showonlyactiveenrolopt = false;
|
|
|
7745 |
}
|
|
|
7746 |
|
|
|
7747 |
$markingallocation = $this->get_instance()->markingworkflow &&
|
|
|
7748 |
$this->get_instance()->markingallocation &&
|
|
|
7749 |
has_capability('mod/assign:manageallocations', $this->context);
|
|
|
7750 |
// Get markers to use in drop lists.
|
|
|
7751 |
$markingallocationoptions = array();
|
|
|
7752 |
if ($markingallocation) {
|
|
|
7753 |
$markingallocationoptions[''] = get_string('filternone', 'assign');
|
|
|
7754 |
$markingallocationoptions[ASSIGN_MARKER_FILTER_NO_MARKER] = get_string('markerfilternomarker', 'assign');
|
|
|
7755 |
list($sort, $params) = users_order_by_sql('u');
|
|
|
7756 |
// Only enrolled users could be assigned as potential markers.
|
|
|
7757 |
$markers = get_enrolled_users($this->context, 'mod/assign:grade', 0, 'u.*', $sort);
|
|
|
7758 |
foreach ($markers as $marker) {
|
|
|
7759 |
$markingallocationoptions[$marker->id] = fullname($marker);
|
|
|
7760 |
}
|
|
|
7761 |
}
|
|
|
7762 |
|
|
|
7763 |
// Get marking states to show in form.
|
|
|
7764 |
$markingworkflowoptions = $this->get_marking_workflow_filters();
|
|
|
7765 |
|
1254 |
ariadna |
7766 |
$gradingoptionsparams = array(
|
|
|
7767 |
'cm' => $this->get_course_module()->id,
|
|
|
7768 |
'contextid' => $this->context->id,
|
|
|
7769 |
'userid' => $USER->id,
|
|
|
7770 |
'submissionsenabled' => $this->is_any_submission_plugin_enabled(),
|
|
|
7771 |
'showquickgrading' => $showquickgrading,
|
|
|
7772 |
'quickgrading' => false,
|
|
|
7773 |
'markingworkflowopt' => $markingworkflowoptions,
|
|
|
7774 |
'markingallocationopt' => $markingallocationoptions,
|
|
|
7775 |
'showonlyactiveenrolopt' => $showonlyactiveenrolopt,
|
|
|
7776 |
'showonlyactiveenrol' => $this->show_only_active_users(),
|
|
|
7777 |
'downloadasfolders' => get_user_preferences('assign_downloadasfolders', 1)
|
|
|
7778 |
);
|
1 |
efrain |
7779 |
$mform = new mod_assign_grading_options_form(null, $gradingoptionsparams);
|
|
|
7780 |
if ($formdata = $mform->get_data()) {
|
|
|
7781 |
set_user_preference('assign_perpage', $formdata->perpage);
|
|
|
7782 |
if (isset($formdata->filter)) {
|
|
|
7783 |
set_user_preference('assign_filter', $formdata->filter);
|
|
|
7784 |
}
|
|
|
7785 |
if (isset($formdata->markerfilter)) {
|
|
|
7786 |
set_user_preference('assign_markerfilter', $formdata->markerfilter);
|
|
|
7787 |
}
|
|
|
7788 |
if (isset($formdata->workflowfilter)) {
|
|
|
7789 |
set_user_preference('assign_workflowfilter', $formdata->workflowfilter);
|
|
|
7790 |
}
|
|
|
7791 |
if ($showquickgrading) {
|
|
|
7792 |
set_user_preference('assign_quickgrading', isset($formdata->quickgrading));
|
|
|
7793 |
}
|
|
|
7794 |
if (isset($formdata->downloadasfolders)) {
|
|
|
7795 |
set_user_preference('assign_downloadasfolders', 1); // Enabled.
|
|
|
7796 |
} else {
|
|
|
7797 |
set_user_preference('assign_downloadasfolders', 0); // Disabled.
|
|
|
7798 |
}
|
|
|
7799 |
if (!empty($showonlyactiveenrolopt)) {
|
|
|
7800 |
$showonlyactiveenrol = isset($formdata->showonlyactiveenrol);
|
|
|
7801 |
set_user_preference('grade_report_showonlyactiveenrol', $showonlyactiveenrol);
|
|
|
7802 |
$this->showonlyactiveenrol = $showonlyactiveenrol;
|
|
|
7803 |
}
|
|
|
7804 |
}
|
|
|
7805 |
}
|
|
|
7806 |
|
|
|
7807 |
/**
|
|
|
7808 |
* @deprecated since 2.7
|
|
|
7809 |
*/
|
1254 |
ariadna |
7810 |
public function format_grade_for_log()
|
|
|
7811 |
{
|
1 |
efrain |
7812 |
throw new coding_exception(__FUNCTION__ . ' has been deprecated, please do not use it any more');
|
|
|
7813 |
}
|
|
|
7814 |
|
|
|
7815 |
/**
|
|
|
7816 |
* @deprecated since 2.7
|
|
|
7817 |
*/
|
1254 |
ariadna |
7818 |
public function format_submission_for_log()
|
|
|
7819 |
{
|
1 |
efrain |
7820 |
throw new coding_exception(__FUNCTION__ . ' has been deprecated, please do not use it any more');
|
|
|
7821 |
}
|
|
|
7822 |
|
|
|
7823 |
/**
|
|
|
7824 |
* Require a valid sess key and then call copy_previous_attempt.
|
|
|
7825 |
*
|
|
|
7826 |
* @param array $notices Any error messages that should be shown
|
|
|
7827 |
* to the user at the top of the edit submission form.
|
|
|
7828 |
* @return bool
|
|
|
7829 |
*/
|
1254 |
ariadna |
7830 |
protected function process_copy_previous_attempt(&$notices)
|
|
|
7831 |
{
|
1 |
efrain |
7832 |
require_sesskey();
|
|
|
7833 |
|
|
|
7834 |
return $this->copy_previous_attempt($notices);
|
|
|
7835 |
}
|
|
|
7836 |
|
|
|
7837 |
/**
|
|
|
7838 |
* Copy the current assignment submission from the last submitted attempt.
|
|
|
7839 |
*
|
|
|
7840 |
* @param array $notices Any error messages that should be shown
|
|
|
7841 |
* to the user at the top of the edit submission form.
|
|
|
7842 |
* @return bool
|
|
|
7843 |
*/
|
1254 |
ariadna |
7844 |
public function copy_previous_attempt(&$notices)
|
|
|
7845 |
{
|
1 |
efrain |
7846 |
global $USER, $CFG;
|
|
|
7847 |
|
|
|
7848 |
require_capability('mod/assign:submit', $this->context);
|
|
|
7849 |
|
|
|
7850 |
$instance = $this->get_instance();
|
|
|
7851 |
if ($instance->teamsubmission) {
|
|
|
7852 |
$submission = $this->get_group_submission($USER->id, 0, true);
|
|
|
7853 |
} else {
|
|
|
7854 |
$submission = $this->get_user_submission($USER->id, true);
|
|
|
7855 |
}
|
|
|
7856 |
if (!$submission || $submission->status != ASSIGN_SUBMISSION_STATUS_REOPENED) {
|
|
|
7857 |
$notices[] = get_string('submissionnotcopiedinvalidstatus', 'assign');
|
|
|
7858 |
return false;
|
|
|
7859 |
}
|
|
|
7860 |
$flags = $this->get_user_flags($USER->id, false);
|
|
|
7861 |
|
|
|
7862 |
// Get the flags to check if it is locked.
|
|
|
7863 |
if ($flags && $flags->locked) {
|
|
|
7864 |
$notices[] = get_string('submissionslocked', 'assign');
|
|
|
7865 |
return false;
|
|
|
7866 |
}
|
|
|
7867 |
if ($instance->submissiondrafts) {
|
|
|
7868 |
$submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
|
|
|
7869 |
} else {
|
|
|
7870 |
$submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
|
|
|
7871 |
}
|
|
|
7872 |
$this->update_submission($submission, $USER->id, true, $instance->teamsubmission);
|
|
|
7873 |
|
|
|
7874 |
// Find the previous submission.
|
|
|
7875 |
if ($instance->teamsubmission) {
|
|
|
7876 |
$previoussubmission = $this->get_group_submission($USER->id, 0, true, $submission->attemptnumber - 1);
|
|
|
7877 |
} else {
|
|
|
7878 |
$previoussubmission = $this->get_user_submission($USER->id, true, $submission->attemptnumber - 1);
|
|
|
7879 |
}
|
|
|
7880 |
|
|
|
7881 |
if (!$previoussubmission) {
|
|
|
7882 |
// There was no previous submission so there is nothing else to do.
|
|
|
7883 |
return true;
|
|
|
7884 |
}
|
|
|
7885 |
|
|
|
7886 |
$pluginerror = false;
|
|
|
7887 |
foreach ($this->get_submission_plugins() as $plugin) {
|
|
|
7888 |
if ($plugin->is_visible() && $plugin->is_enabled()) {
|
|
|
7889 |
if (!$plugin->copy_submission($previoussubmission, $submission)) {
|
|
|
7890 |
$notices[] = $plugin->get_error();
|
|
|
7891 |
$pluginerror = true;
|
|
|
7892 |
}
|
|
|
7893 |
}
|
|
|
7894 |
}
|
|
|
7895 |
if ($pluginerror) {
|
|
|
7896 |
return false;
|
|
|
7897 |
}
|
|
|
7898 |
|
|
|
7899 |
\mod_assign\event\submission_duplicated::create_from_submission($this, $submission)->trigger();
|
|
|
7900 |
|
|
|
7901 |
$complete = COMPLETION_INCOMPLETE;
|
|
|
7902 |
if ($submission->status == ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
|
|
|
7903 |
$complete = COMPLETION_COMPLETE;
|
|
|
7904 |
}
|
|
|
7905 |
$completion = new completion_info($this->get_course());
|
|
|
7906 |
if ($completion->is_enabled($this->get_course_module()) && $instance->completionsubmit) {
|
1254 |
ariadna |
7907 |
$this->update_activity_completion_records(
|
|
|
7908 |
$instance->teamsubmission,
|
|
|
7909 |
$instance->requireallteammemberssubmit,
|
|
|
7910 |
$submission,
|
|
|
7911 |
$USER->id,
|
|
|
7912 |
$complete,
|
|
|
7913 |
$completion
|
|
|
7914 |
);
|
1 |
efrain |
7915 |
}
|
|
|
7916 |
|
|
|
7917 |
if (!$instance->submissiondrafts) {
|
|
|
7918 |
// There is a case for not notifying the student about the submission copy,
|
|
|
7919 |
// but it provides a record of the event and if they then cancel editing it
|
|
|
7920 |
// is clear that the submission was copied.
|
|
|
7921 |
$this->notify_student_submission_copied($submission);
|
|
|
7922 |
$this->notify_graders($submission);
|
|
|
7923 |
|
|
|
7924 |
// The same logic applies here - we could not notify teachers,
|
|
|
7925 |
// but then they would wonder why there are submitted assignments
|
|
|
7926 |
// and they haven't been notified.
|
|
|
7927 |
\mod_assign\event\assessable_submitted::create_from_submission($this, $submission, true)->trigger();
|
|
|
7928 |
}
|
|
|
7929 |
return true;
|
|
|
7930 |
}
|
|
|
7931 |
|
|
|
7932 |
/**
|
|
|
7933 |
* Determine if the current submission is empty or not.
|
|
|
7934 |
*
|
|
|
7935 |
* @param submission $submission the students submission record to check.
|
|
|
7936 |
* @return bool
|
|
|
7937 |
*/
|
1254 |
ariadna |
7938 |
public function submission_empty($submission)
|
|
|
7939 |
{
|
1 |
efrain |
7940 |
$allempty = true;
|
|
|
7941 |
|
|
|
7942 |
foreach ($this->submissionplugins as $plugin) {
|
|
|
7943 |
if ($plugin->is_enabled() && $plugin->is_visible()) {
|
|
|
7944 |
if (!$allempty || !$plugin->is_empty($submission)) {
|
|
|
7945 |
$allempty = false;
|
|
|
7946 |
}
|
|
|
7947 |
}
|
|
|
7948 |
}
|
|
|
7949 |
return $allempty;
|
|
|
7950 |
}
|
|
|
7951 |
|
|
|
7952 |
/**
|
|
|
7953 |
* Determine if a new submission is empty or not
|
|
|
7954 |
*
|
|
|
7955 |
* @param stdClass $data Submission data
|
|
|
7956 |
* @return bool
|
|
|
7957 |
*/
|
1254 |
ariadna |
7958 |
public function new_submission_empty($data)
|
|
|
7959 |
{
|
1 |
efrain |
7960 |
foreach ($this->submissionplugins as $plugin) {
|
1254 |
ariadna |
7961 |
if (
|
|
|
7962 |
$plugin->is_enabled() && $plugin->is_visible() && $plugin->allow_submissions() &&
|
|
|
7963 |
!$plugin->submission_is_empty($data)
|
|
|
7964 |
) {
|
1 |
efrain |
7965 |
return false;
|
|
|
7966 |
}
|
|
|
7967 |
}
|
|
|
7968 |
return true;
|
|
|
7969 |
}
|
|
|
7970 |
|
|
|
7971 |
/**
|
|
|
7972 |
* Save assignment submission for the current user.
|
|
|
7973 |
*
|
|
|
7974 |
* @param stdClass $data
|
|
|
7975 |
* @param array $notices Any error messages that should be shown
|
|
|
7976 |
* to the user.
|
|
|
7977 |
* @return bool
|
|
|
7978 |
*/
|
1254 |
ariadna |
7979 |
public function save_submission(stdClass $data, &$notices)
|
|
|
7980 |
{
|
1 |
efrain |
7981 |
global $CFG, $USER, $DB;
|
|
|
7982 |
|
|
|
7983 |
$userid = $USER->id;
|
|
|
7984 |
if (!empty($data->userid)) {
|
|
|
7985 |
$userid = $data->userid;
|
|
|
7986 |
}
|
|
|
7987 |
|
1254 |
ariadna |
7988 |
$user = clone ($USER);
|
1 |
efrain |
7989 |
if ($userid == $USER->id) {
|
|
|
7990 |
require_capability('mod/assign:submit', $this->context);
|
|
|
7991 |
} else {
|
1254 |
ariadna |
7992 |
$user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
|
1 |
efrain |
7993 |
if (!$this->can_edit_submission($userid, $USER->id)) {
|
|
|
7994 |
throw new \moodle_exception('nopermission');
|
|
|
7995 |
}
|
|
|
7996 |
}
|
|
|
7997 |
$instance = $this->get_instance();
|
|
|
7998 |
|
|
|
7999 |
if ($instance->teamsubmission) {
|
|
|
8000 |
$submission = $this->get_group_submission($userid, 0, true);
|
|
|
8001 |
} else {
|
|
|
8002 |
$submission = $this->get_user_submission($userid, true);
|
|
|
8003 |
}
|
|
|
8004 |
|
|
|
8005 |
if ($this->new_submission_empty($data)) {
|
|
|
8006 |
$notices[] = get_string('submissionempty', 'mod_assign');
|
|
|
8007 |
return false;
|
|
|
8008 |
}
|
|
|
8009 |
|
|
|
8010 |
// Check that no one has modified the submission since we started looking at it.
|
|
|
8011 |
if (isset($data->lastmodified) && ($submission->timemodified > $data->lastmodified)) {
|
|
|
8012 |
// Another user has submitted something. Notify the current user.
|
|
|
8013 |
if ($submission->status !== ASSIGN_SUBMISSION_STATUS_NEW) {
|
|
|
8014 |
$notices[] = $instance->teamsubmission ? get_string('submissionmodifiedgroup', 'mod_assign')
|
1254 |
ariadna |
8015 |
: get_string('submissionmodified', 'mod_assign');
|
1 |
efrain |
8016 |
return false;
|
|
|
8017 |
}
|
|
|
8018 |
}
|
|
|
8019 |
|
|
|
8020 |
if ($instance->submissiondrafts) {
|
|
|
8021 |
$submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
|
|
|
8022 |
} else {
|
|
|
8023 |
$submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
|
|
|
8024 |
}
|
|
|
8025 |
|
|
|
8026 |
$flags = $this->get_user_flags($userid, false);
|
|
|
8027 |
|
|
|
8028 |
// Get the flags to check if it is locked.
|
|
|
8029 |
if ($flags && $flags->locked) {
|
|
|
8030 |
throw new \moodle_exception('submissionslocked', 'assign');
|
|
|
8031 |
return true;
|
|
|
8032 |
}
|
|
|
8033 |
|
|
|
8034 |
$pluginerror = false;
|
|
|
8035 |
foreach ($this->submissionplugins as $plugin) {
|
|
|
8036 |
if ($plugin->is_enabled() && $plugin->is_visible()) {
|
|
|
8037 |
if (!$plugin->save($submission, $data)) {
|
|
|
8038 |
$notices[] = $plugin->get_error();
|
|
|
8039 |
$pluginerror = true;
|
|
|
8040 |
}
|
|
|
8041 |
}
|
|
|
8042 |
}
|
|
|
8043 |
|
|
|
8044 |
$allempty = $this->submission_empty($submission);
|
|
|
8045 |
if ($pluginerror || $allempty) {
|
|
|
8046 |
if ($allempty) {
|
|
|
8047 |
$notices[] = get_string('submissionempty', 'mod_assign');
|
|
|
8048 |
}
|
|
|
8049 |
return false;
|
|
|
8050 |
}
|
|
|
8051 |
|
|
|
8052 |
$this->update_submission($submission, $userid, true, $instance->teamsubmission);
|
|
|
8053 |
$users = [$userid];
|
|
|
8054 |
|
|
|
8055 |
if ($instance->teamsubmission && !$instance->requireallteammemberssubmit) {
|
|
|
8056 |
$team = $this->get_submission_group_members($submission->groupid, true);
|
|
|
8057 |
|
|
|
8058 |
foreach ($team as $member) {
|
|
|
8059 |
if ($member->id != $userid) {
|
1254 |
ariadna |
8060 |
$membersubmission = clone ($submission);
|
1 |
efrain |
8061 |
$this->update_submission($membersubmission, $member->id, true, $instance->teamsubmission);
|
|
|
8062 |
$users[] = $member->id;
|
|
|
8063 |
}
|
|
|
8064 |
}
|
|
|
8065 |
}
|
|
|
8066 |
|
|
|
8067 |
$complete = COMPLETION_INCOMPLETE;
|
|
|
8068 |
if ($submission->status == ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
|
|
|
8069 |
$complete = COMPLETION_COMPLETE;
|
|
|
8070 |
}
|
|
|
8071 |
|
|
|
8072 |
$completion = new completion_info($this->get_course());
|
|
|
8073 |
if ($completion->is_enabled($this->get_course_module()) && $instance->completionsubmit) {
|
|
|
8074 |
foreach ($users as $id) {
|
|
|
8075 |
$completion->update_state($this->get_course_module(), $complete, $id);
|
|
|
8076 |
}
|
|
|
8077 |
}
|
|
|
8078 |
|
|
|
8079 |
// Logging.
|
|
|
8080 |
if (isset($data->submissionstatement) && ($userid == $USER->id)) {
|
|
|
8081 |
\mod_assign\event\statement_accepted::create_from_submission($this, $submission)->trigger();
|
|
|
8082 |
}
|
|
|
8083 |
|
|
|
8084 |
if (!$instance->submissiondrafts) {
|
|
|
8085 |
$this->notify_student_submission_receipt($submission);
|
|
|
8086 |
$this->notify_graders($submission);
|
|
|
8087 |
\mod_assign\event\assessable_submitted::create_from_submission($this, $submission, true)->trigger();
|
|
|
8088 |
}
|
|
|
8089 |
return true;
|
|
|
8090 |
}
|
|
|
8091 |
|
|
|
8092 |
/**
|
|
|
8093 |
* Save assignment submission.
|
|
|
8094 |
*
|
|
|
8095 |
* @param moodleform $mform
|
|
|
8096 |
* @param array $notices Any error messages that should be shown
|
|
|
8097 |
* to the user at the top of the edit submission form.
|
|
|
8098 |
* @return bool
|
|
|
8099 |
*/
|
1254 |
ariadna |
8100 |
protected function process_save_submission(&$mform, &$notices)
|
|
|
8101 |
{
|
1 |
efrain |
8102 |
global $CFG, $USER;
|
|
|
8103 |
|
|
|
8104 |
// Include submission form.
|
|
|
8105 |
require_once($CFG->dirroot . '/mod/assign/submission_form.php');
|
|
|
8106 |
|
|
|
8107 |
$userid = optional_param('userid', $USER->id, PARAM_INT);
|
|
|
8108 |
// Need submit permission to submit an assignment.
|
|
|
8109 |
require_sesskey();
|
|
|
8110 |
if (!$this->submissions_open($userid)) {
|
|
|
8111 |
$notices[] = get_string('duedatereached', 'assign');
|
|
|
8112 |
return false;
|
|
|
8113 |
}
|
|
|
8114 |
$instance = $this->get_instance();
|
|
|
8115 |
|
|
|
8116 |
$data = new stdClass();
|
|
|
8117 |
$data->userid = $userid;
|
|
|
8118 |
$mform = new mod_assign_submission_form(null, array($this, $data));
|
|
|
8119 |
if ($mform->is_cancelled()) {
|
|
|
8120 |
return true;
|
|
|
8121 |
}
|
|
|
8122 |
if ($data = $mform->get_data()) {
|
|
|
8123 |
return $this->save_submission($data, $notices);
|
|
|
8124 |
}
|
|
|
8125 |
return false;
|
|
|
8126 |
}
|
|
|
8127 |
|
|
|
8128 |
|
|
|
8129 |
/**
|
|
|
8130 |
* Determine if this users grade can be edited.
|
|
|
8131 |
*
|
|
|
8132 |
* @param int $userid - The student userid
|
|
|
8133 |
* @param bool $checkworkflow - whether to include a check for the workflow state.
|
|
|
8134 |
* @param stdClass $gradinginfo - optional, allow gradinginfo to be passed for performance.
|
|
|
8135 |
* @return bool $gradingdisabled
|
|
|
8136 |
*/
|
1254 |
ariadna |
8137 |
public function grading_disabled($userid, $checkworkflow = true, $gradinginfo = null)
|
|
|
8138 |
{
|
1 |
efrain |
8139 |
if ($checkworkflow && $this->get_instance()->markingworkflow) {
|
|
|
8140 |
$grade = $this->get_user_grade($userid, false);
|
|
|
8141 |
$validstates = $this->get_marking_workflow_states_for_current_user();
|
|
|
8142 |
if (!empty($grade) && !empty($grade->workflowstate) && !array_key_exists($grade->workflowstate, $validstates)) {
|
|
|
8143 |
return true;
|
|
|
8144 |
}
|
|
|
8145 |
}
|
|
|
8146 |
|
|
|
8147 |
if (is_null($gradinginfo)) {
|
1254 |
ariadna |
8148 |
$gradinginfo = grade_get_grades(
|
|
|
8149 |
$this->get_course()->id,
|
1 |
efrain |
8150 |
'mod',
|
|
|
8151 |
'assign',
|
|
|
8152 |
$this->get_instance()->id,
|
1254 |
ariadna |
8153 |
array($userid)
|
|
|
8154 |
);
|
1 |
efrain |
8155 |
}
|
|
|
8156 |
|
|
|
8157 |
if (!$gradinginfo) {
|
|
|
8158 |
return false;
|
|
|
8159 |
}
|
|
|
8160 |
|
|
|
8161 |
if (!isset($gradinginfo->items[0]->grades[$userid])) {
|
|
|
8162 |
return false;
|
|
|
8163 |
}
|
|
|
8164 |
$gradingdisabled = $gradinginfo->items[0]->grades[$userid]->locked ||
|
1254 |
ariadna |
8165 |
$gradinginfo->items[0]->grades[$userid]->overridden;
|
1 |
efrain |
8166 |
return $gradingdisabled;
|
|
|
8167 |
}
|
|
|
8168 |
|
|
|
8169 |
|
|
|
8170 |
/**
|
|
|
8171 |
* Get an instance of a grading form if advanced grading is enabled.
|
|
|
8172 |
* This is specific to the assignment, marker and student.
|
|
|
8173 |
*
|
|
|
8174 |
* @param int $userid - The student userid
|
|
|
8175 |
* @param stdClass|false $grade - The grade record
|
|
|
8176 |
* @param bool $gradingdisabled
|
|
|
8177 |
* @return mixed gradingform_instance|null $gradinginstance
|
|
|
8178 |
*/
|
1254 |
ariadna |
8179 |
protected function get_grading_instance($userid, $grade, $gradingdisabled)
|
|
|
8180 |
{
|
1 |
efrain |
8181 |
global $CFG, $USER;
|
|
|
8182 |
|
|
|
8183 |
$grademenu = make_grades_menu($this->get_instance()->grade);
|
|
|
8184 |
$allowgradedecimals = $this->get_instance()->grade > 0;
|
|
|
8185 |
|
|
|
8186 |
$advancedgradingwarning = false;
|
|
|
8187 |
$gradingmanager = get_grading_manager($this->context, 'mod_assign', 'submissions');
|
|
|
8188 |
$gradinginstance = null;
|
|
|
8189 |
if ($gradingmethod = $gradingmanager->get_active_method()) {
|
|
|
8190 |
$controller = $gradingmanager->get_controller($gradingmethod);
|
|
|
8191 |
if ($controller->is_form_available()) {
|
|
|
8192 |
$itemid = null;
|
|
|
8193 |
if ($grade) {
|
|
|
8194 |
$itemid = $grade->id;
|
|
|
8195 |
}
|
|
|
8196 |
if ($gradingdisabled && $itemid) {
|
|
|
8197 |
$gradinginstance = $controller->get_current_instance($USER->id, $itemid);
|
|
|
8198 |
} else if (!$gradingdisabled) {
|
|
|
8199 |
$instanceid = optional_param('advancedgradinginstanceid', 0, PARAM_INT);
|
1254 |
ariadna |
8200 |
$gradinginstance = $controller->get_or_create_instance(
|
|
|
8201 |
$instanceid,
|
|
|
8202 |
$USER->id,
|
|
|
8203 |
$itemid
|
|
|
8204 |
);
|
1 |
efrain |
8205 |
}
|
|
|
8206 |
} else {
|
|
|
8207 |
$advancedgradingwarning = $controller->form_unavailable_notification();
|
|
|
8208 |
}
|
|
|
8209 |
}
|
|
|
8210 |
if ($gradinginstance) {
|
|
|
8211 |
$gradinginstance->get_controller()->set_grade_range($grademenu, $allowgradedecimals);
|
|
|
8212 |
}
|
|
|
8213 |
return $gradinginstance;
|
|
|
8214 |
}
|
|
|
8215 |
|
|
|
8216 |
/**
|
|
|
8217 |
* Add elements to grade form.
|
|
|
8218 |
*
|
|
|
8219 |
* @param MoodleQuickForm $mform
|
|
|
8220 |
* @param stdClass $data
|
|
|
8221 |
* @param array $params
|
|
|
8222 |
* @return void
|
|
|
8223 |
*/
|
1254 |
ariadna |
8224 |
public function add_grade_form_elements(MoodleQuickForm $mform, stdClass $data, $params)
|
|
|
8225 |
{
|
1 |
efrain |
8226 |
global $USER, $CFG, $SESSION;
|
|
|
8227 |
$settings = $this->get_instance();
|
|
|
8228 |
|
|
|
8229 |
$rownum = isset($params['rownum']) ? $params['rownum'] : 0;
|
|
|
8230 |
$last = isset($params['last']) ? $params['last'] : true;
|
|
|
8231 |
$useridlistid = isset($params['useridlistid']) ? $params['useridlistid'] : 0;
|
|
|
8232 |
$userid = isset($params['userid']) ? $params['userid'] : 0;
|
|
|
8233 |
$attemptnumber = isset($params['attemptnumber']) ? $params['attemptnumber'] : 0;
|
|
|
8234 |
$gradingpanel = !empty($params['gradingpanel']);
|
|
|
8235 |
$bothids = ($userid && $useridlistid);
|
|
|
8236 |
|
|
|
8237 |
if (!$userid || $bothids) {
|
|
|
8238 |
$useridlist = $this->get_grading_userid_list(true, $useridlistid);
|
|
|
8239 |
} else {
|
|
|
8240 |
$useridlist = array($userid);
|
|
|
8241 |
$rownum = 0;
|
|
|
8242 |
$useridlistid = '';
|
|
|
8243 |
}
|
|
|
8244 |
|
|
|
8245 |
$userid = $useridlist[$rownum];
|
|
|
8246 |
// We need to create a grade record matching this attempt number
|
|
|
8247 |
// or the feedback plugin will have no way to know what is the correct attempt.
|
|
|
8248 |
$grade = $this->get_user_grade($userid, true, $attemptnumber);
|
|
|
8249 |
|
|
|
8250 |
$submission = null;
|
|
|
8251 |
if ($this->get_instance()->teamsubmission) {
|
|
|
8252 |
$submission = $this->get_group_submission($userid, 0, false, $attemptnumber);
|
|
|
8253 |
} else {
|
|
|
8254 |
$submission = $this->get_user_submission($userid, false, $attemptnumber);
|
|
|
8255 |
}
|
|
|
8256 |
|
|
|
8257 |
// Add advanced grading.
|
|
|
8258 |
$gradingdisabled = $this->grading_disabled($userid);
|
|
|
8259 |
$gradinginstance = $this->get_grading_instance($userid, $grade, $gradingdisabled);
|
|
|
8260 |
|
|
|
8261 |
$mform->addElement('header', 'gradeheader', get_string('gradenoun'));
|
|
|
8262 |
if ($gradinginstance) {
|
1254 |
ariadna |
8263 |
$gradingelement = $mform->addElement(
|
|
|
8264 |
'grading',
|
|
|
8265 |
'advancedgrading',
|
|
|
8266 |
get_string('gradenoun') . ':',
|
|
|
8267 |
array('gradinginstance' => $gradinginstance)
|
|
|
8268 |
);
|
1 |
efrain |
8269 |
if ($gradingdisabled) {
|
|
|
8270 |
$gradingelement->freeze();
|
|
|
8271 |
} else {
|
|
|
8272 |
$mform->addElement('hidden', 'advancedgradinginstanceid', $gradinginstance->get_id());
|
|
|
8273 |
$mform->setType('advancedgradinginstanceid', PARAM_INT);
|
|
|
8274 |
}
|
|
|
8275 |
} else {
|
|
|
8276 |
// Use simple direct grading.
|
|
|
8277 |
if ($this->get_instance()->grade > 0) {
|
|
|
8278 |
$name = get_string('gradeoutof', 'assign', $this->get_instance()->grade);
|
|
|
8279 |
if (!$gradingdisabled) {
|
|
|
8280 |
$gradingelement = $mform->addElement('text', 'grade', $name);
|
|
|
8281 |
$mform->addHelpButton('grade', 'gradeoutofhelp', 'assign');
|
|
|
8282 |
$mform->setType('grade', PARAM_RAW);
|
|
|
8283 |
} else {
|
|
|
8284 |
$strgradelocked = get_string('gradelocked', 'assign');
|
|
|
8285 |
$mform->addElement('static', 'gradedisabled', $name, $strgradelocked);
|
|
|
8286 |
$mform->addHelpButton('gradedisabled', 'gradeoutofhelp', 'assign');
|
|
|
8287 |
}
|
|
|
8288 |
} else {
|
|
|
8289 |
$grademenu = array(-1 => get_string("nograde")) + make_grades_menu($this->get_instance()->grade);
|
|
|
8290 |
if (count($grademenu) > 1) {
|
|
|
8291 |
$gradingelement = $mform->addElement('select', 'grade', get_string('gradenoun') . ':', $grademenu);
|
|
|
8292 |
|
|
|
8293 |
// The grade is already formatted with format_float so it needs to be converted back to an integer.
|
|
|
8294 |
if (!empty($data->grade)) {
|
|
|
8295 |
$data->grade = (int)unformat_float($data->grade);
|
|
|
8296 |
}
|
|
|
8297 |
$mform->setType('grade', PARAM_INT);
|
|
|
8298 |
if ($gradingdisabled) {
|
|
|
8299 |
$gradingelement->freeze();
|
|
|
8300 |
}
|
|
|
8301 |
}
|
|
|
8302 |
}
|
|
|
8303 |
}
|
|
|
8304 |
|
1254 |
ariadna |
8305 |
$gradinginfo = grade_get_grades(
|
|
|
8306 |
$this->get_course()->id,
|
|
|
8307 |
'mod',
|
|
|
8308 |
'assign',
|
|
|
8309 |
$this->get_instance()->id,
|
|
|
8310 |
$userid
|
|
|
8311 |
);
|
1 |
efrain |
8312 |
if (!empty($CFG->enableoutcomes)) {
|
|
|
8313 |
foreach ($gradinginfo->outcomes as $index => $outcome) {
|
|
|
8314 |
$options = make_grades_menu(-$outcome->scaleid);
|
|
|
8315 |
$options[0] = get_string('nooutcome', 'grades');
|
|
|
8316 |
if ($outcome->grades[$userid]->locked) {
|
1254 |
ariadna |
8317 |
$mform->addElement(
|
|
|
8318 |
'static',
|
|
|
8319 |
'outcome_' . $index . '[' . $userid . ']',
|
|
|
8320 |
$outcome->name . ':',
|
|
|
8321 |
$options[$outcome->grades[$userid]->grade]
|
|
|
8322 |
);
|
1 |
efrain |
8323 |
} else {
|
1254 |
ariadna |
8324 |
$attributes = array('id' => 'menuoutcome_' . $index);
|
|
|
8325 |
$mform->addElement(
|
|
|
8326 |
'select',
|
|
|
8327 |
'outcome_' . $index . '[' . $userid . ']',
|
|
|
8328 |
$outcome->name . ':',
|
|
|
8329 |
$options,
|
|
|
8330 |
$attributes
|
|
|
8331 |
);
|
1 |
efrain |
8332 |
$mform->setType('outcome_' . $index . '[' . $userid . ']', PARAM_INT);
|
1254 |
ariadna |
8333 |
$mform->setDefault(
|
|
|
8334 |
'outcome_' . $index . '[' . $userid . ']',
|
|
|
8335 |
$outcome->grades[$userid]->grade
|
|
|
8336 |
);
|
1 |
efrain |
8337 |
}
|
|
|
8338 |
}
|
|
|
8339 |
}
|
|
|
8340 |
|
|
|
8341 |
$capabilitylist = array('gradereport/grader:view', 'moodle/grade:viewall');
|
|
|
8342 |
$usergrade = get_string('notgraded', 'assign');
|
|
|
8343 |
if (has_all_capabilities($capabilitylist, $this->get_course_context())) {
|
1254 |
ariadna |
8344 |
$urlparams = array('id' => $this->get_course()->id);
|
1 |
efrain |
8345 |
$url = new moodle_url('/grade/report/grader/index.php', $urlparams);
|
|
|
8346 |
if (isset($gradinginfo->items[0]->grades[$userid]->grade)) {
|
|
|
8347 |
$usergrade = $gradinginfo->items[0]->grades[$userid]->str_grade;
|
|
|
8348 |
}
|
|
|
8349 |
$gradestring = $this->get_renderer()->action_link($url, $usergrade);
|
|
|
8350 |
} else {
|
1254 |
ariadna |
8351 |
if (
|
|
|
8352 |
isset($gradinginfo->items[0]->grades[$userid]) &&
|
|
|
8353 |
!$gradinginfo->items[0]->grades[$userid]->hidden
|
|
|
8354 |
) {
|
1 |
efrain |
8355 |
$usergrade = $gradinginfo->items[0]->grades[$userid]->str_grade;
|
|
|
8356 |
}
|
|
|
8357 |
$gradestring = $usergrade;
|
|
|
8358 |
}
|
|
|
8359 |
|
|
|
8360 |
if ($this->get_instance()->markingworkflow) {
|
|
|
8361 |
$states = $this->get_marking_workflow_states_for_current_user();
|
|
|
8362 |
$options = array('' => get_string('markingworkflowstatenotmarked', 'assign')) + $states;
|
|
|
8363 |
$select = $mform->addElement('select', 'workflowstate', get_string('markingworkflowstate', 'assign'), $options);
|
|
|
8364 |
$mform->addHelpButton('workflowstate', 'markingworkflowstate', 'assign');
|
|
|
8365 |
if (!empty($data->workflowstate) && !array_key_exists($data->workflowstate, $states)) {
|
|
|
8366 |
// In a workflow state that user should not be able to change, so freeze workflow selector.
|
|
|
8367 |
// Have to add the state so it shows in the frozen selector.
|
|
|
8368 |
$allworkflowstates = $this->get_all_marking_workflow_states();
|
|
|
8369 |
$select->addOption($allworkflowstates[$data->workflowstate], $data->workflowstate);
|
|
|
8370 |
$mform->freeze('workflowstate');
|
|
|
8371 |
}
|
|
|
8372 |
$gradingstatus = $this->get_grading_status($userid);
|
|
|
8373 |
if ($gradingstatus != ASSIGN_MARKING_WORKFLOW_STATE_RELEASED) {
|
|
|
8374 |
if ($grade->grade && $grade->grade != -1) {
|
|
|
8375 |
if ($settings->grade > 0) {
|
|
|
8376 |
$assigngradestring = format_float($grade->grade, $this->get_grade_item()->get_decimals());
|
|
|
8377 |
} else {
|
|
|
8378 |
$assigngradestring = make_grades_menu($settings->grade)[grade_floatval($grade->grade)];
|
|
|
8379 |
}
|
|
|
8380 |
$assigngradestring = html_writer::span($assigngradestring, 'currentgrade');
|
|
|
8381 |
$label = get_string('currentassigngrade', 'assign');
|
|
|
8382 |
$mform->addElement('static', 'currentassigngrade', $label, $assigngradestring);
|
|
|
8383 |
}
|
|
|
8384 |
}
|
|
|
8385 |
}
|
|
|
8386 |
|
1254 |
ariadna |
8387 |
if (
|
|
|
8388 |
$this->get_instance()->markingworkflow &&
|
1 |
efrain |
8389 |
$this->get_instance()->markingallocation &&
|
1254 |
ariadna |
8390 |
has_capability('mod/assign:manageallocations', $this->context)
|
|
|
8391 |
) {
|
1 |
efrain |
8392 |
|
|
|
8393 |
list($sort, $params) = users_order_by_sql('u');
|
|
|
8394 |
// Only enrolled users could be assigned as potential markers.
|
|
|
8395 |
$markers = get_enrolled_users($this->context, 'mod/assign:grade', 0, 'u.*', $sort);
|
|
|
8396 |
$markerlist = array('' => get_string('choosemarker', 'assign'));
|
|
|
8397 |
$viewfullnames = has_capability('moodle/site:viewfullnames', $this->context);
|
|
|
8398 |
foreach ($markers as $marker) {
|
|
|
8399 |
$markerlist[$marker->id] = fullname($marker, $viewfullnames);
|
|
|
8400 |
}
|
|
|
8401 |
$mform->addElement('select', 'allocatedmarker', get_string('allocatedmarker', 'assign'), $markerlist);
|
|
|
8402 |
$mform->addHelpButton('allocatedmarker', 'allocatedmarker', 'assign');
|
|
|
8403 |
$mform->disabledIf('allocatedmarker', 'workflowstate', 'eq', ASSIGN_MARKING_WORKFLOW_STATE_READYFORREVIEW);
|
|
|
8404 |
$mform->disabledIf('allocatedmarker', 'workflowstate', 'eq', ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW);
|
|
|
8405 |
$mform->disabledIf('allocatedmarker', 'workflowstate', 'eq', ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE);
|
|
|
8406 |
$mform->disabledIf('allocatedmarker', 'workflowstate', 'eq', ASSIGN_MARKING_WORKFLOW_STATE_RELEASED);
|
|
|
8407 |
}
|
|
|
8408 |
|
|
|
8409 |
$gradestring = '<span class="currentgrade">' . $gradestring . '</span>';
|
|
|
8410 |
$mform->addElement('static', 'currentgrade', get_string('currentgrade', 'assign'), $gradestring);
|
|
|
8411 |
|
|
|
8412 |
if (count($useridlist) > 1) {
|
1254 |
ariadna |
8413 |
$strparams = array('current' => $rownum + 1, 'total' => count($useridlist));
|
1 |
efrain |
8414 |
$name = get_string('outof', 'assign', $strparams);
|
|
|
8415 |
$mform->addElement('static', 'gradingstudent', get_string('gradingstudent', 'assign'), $name);
|
|
|
8416 |
}
|
|
|
8417 |
|
|
|
8418 |
// Let feedback plugins add elements to the grading form.
|
|
|
8419 |
$this->add_plugin_grade_elements($grade, $mform, $data, $userid);
|
|
|
8420 |
|
|
|
8421 |
// Hidden params.
|
|
|
8422 |
$mform->addElement('hidden', 'id', $this->get_course_module()->id);
|
|
|
8423 |
$mform->setType('id', PARAM_INT);
|
|
|
8424 |
$mform->addElement('hidden', 'rownum', $rownum);
|
|
|
8425 |
$mform->setType('rownum', PARAM_INT);
|
|
|
8426 |
$mform->setConstant('rownum', $rownum);
|
|
|
8427 |
$mform->addElement('hidden', 'useridlistid', $useridlistid);
|
|
|
8428 |
$mform->setType('useridlistid', PARAM_ALPHANUM);
|
|
|
8429 |
$mform->addElement('hidden', 'attemptnumber', $attemptnumber);
|
|
|
8430 |
$mform->setType('attemptnumber', PARAM_INT);
|
|
|
8431 |
$mform->addElement('hidden', 'ajax', optional_param('ajax', 0, PARAM_INT));
|
|
|
8432 |
$mform->setType('ajax', PARAM_INT);
|
|
|
8433 |
$mform->addElement('hidden', 'userid', optional_param('userid', 0, PARAM_INT));
|
|
|
8434 |
$mform->setType('userid', PARAM_INT);
|
|
|
8435 |
|
|
|
8436 |
if ($this->get_instance()->teamsubmission) {
|
|
|
8437 |
$mform->addElement('header', 'groupsubmissionsettings', get_string('groupsubmissionsettings', 'assign'));
|
|
|
8438 |
$mform->addElement('selectyesno', 'applytoall', get_string('applytoteam', 'assign'));
|
|
|
8439 |
$mform->setDefault('applytoall', 1);
|
|
|
8440 |
}
|
|
|
8441 |
|
|
|
8442 |
// Do not show if we are editing a previous attempt.
|
|
|
8443 |
if (($attemptnumber == -1 ||
|
1254 |
ariadna |
8444 |
($attemptnumber + 1) == count($this->get_all_submissions($userid))) &&
|
|
|
8445 |
$this->get_instance()->attemptreopenmethod != ASSIGN_ATTEMPT_REOPEN_METHOD_NONE
|
|
|
8446 |
) {
|
1 |
efrain |
8447 |
$mform->addElement('header', 'attemptsettings', get_string('attemptsettings', 'assign'));
|
|
|
8448 |
$attemptreopenmethod = get_string('attemptreopenmethod_' . $this->get_instance()->attemptreopenmethod, 'assign');
|
|
|
8449 |
$mform->addElement('static', 'attemptreopenmethod', get_string('attemptreopenmethod', 'assign'), $attemptreopenmethod);
|
|
|
8450 |
|
|
|
8451 |
$attemptnumber = 0;
|
|
|
8452 |
if ($submission) {
|
|
|
8453 |
$attemptnumber = $submission->attemptnumber;
|
|
|
8454 |
}
|
|
|
8455 |
$maxattempts = $this->get_instance()->maxattempts;
|
|
|
8456 |
if ($maxattempts == ASSIGN_UNLIMITED_ATTEMPTS) {
|
|
|
8457 |
$maxattempts = get_string('unlimitedattempts', 'assign');
|
|
|
8458 |
}
|
|
|
8459 |
$mform->addelement('static', 'maxattemptslabel', get_string('maxattempts', 'assign'), $maxattempts);
|
|
|
8460 |
$mform->addelement('static', 'attemptnumberlabel', get_string('attemptnumber', 'assign'), $attemptnumber + 1);
|
|
|
8461 |
|
|
|
8462 |
$ismanual = $this->get_instance()->attemptreopenmethod == ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL;
|
|
|
8463 |
$issubmission = !empty($submission);
|
|
|
8464 |
$isunlimited = $this->get_instance()->maxattempts == ASSIGN_UNLIMITED_ATTEMPTS;
|
1254 |
ariadna |
8465 |
$islessthanmaxattempts = $issubmission && ($submission->attemptnumber < ($this->get_instance()->maxattempts - 1));
|
1 |
efrain |
8466 |
|
|
|
8467 |
if ($ismanual && (!$issubmission || $isunlimited || $islessthanmaxattempts)) {
|
|
|
8468 |
$mform->addElement('selectyesno', 'addattempt', get_string('addattempt', 'assign'));
|
|
|
8469 |
$mform->setDefault('addattempt', 0);
|
|
|
8470 |
}
|
|
|
8471 |
}
|
|
|
8472 |
if (!$gradingpanel) {
|
|
|
8473 |
$mform->addElement('selectyesno', 'sendstudentnotifications', get_string('sendstudentnotifications', 'assign'));
|
|
|
8474 |
} else {
|
|
|
8475 |
$mform->addElement('hidden', 'sendstudentnotifications', get_string('sendstudentnotifications', 'assign'));
|
|
|
8476 |
$mform->setType('sendstudentnotifications', PARAM_BOOL);
|
|
|
8477 |
}
|
|
|
8478 |
// Get assignment visibility information for student.
|
|
|
8479 |
$modinfo = get_fast_modinfo($settings->course, $userid);
|
|
|
8480 |
$cm = $modinfo->get_cm($this->get_course_module()->id);
|
|
|
8481 |
|
|
|
8482 |
// Don't allow notification to be sent if the student can't access the assignment,
|
|
|
8483 |
// or until in "Released" state if using marking workflow.
|
|
|
8484 |
if (!$cm->uservisible) {
|
|
|
8485 |
$mform->setDefault('sendstudentnotifications', 0);
|
|
|
8486 |
$mform->freeze('sendstudentnotifications');
|
|
|
8487 |
} else if ($this->get_instance()->markingworkflow) {
|
|
|
8488 |
$mform->setDefault('sendstudentnotifications', 0);
|
|
|
8489 |
if (!$gradingpanel) {
|
|
|
8490 |
$mform->disabledIf('sendstudentnotifications', 'workflowstate', 'neq', ASSIGN_MARKING_WORKFLOW_STATE_RELEASED);
|
|
|
8491 |
}
|
|
|
8492 |
} else {
|
|
|
8493 |
$mform->setDefault('sendstudentnotifications', $this->get_instance()->sendstudentnotifications);
|
|
|
8494 |
}
|
|
|
8495 |
|
|
|
8496 |
$mform->addElement('hidden', 'action', 'submitgrade');
|
|
|
8497 |
$mform->setType('action', PARAM_ALPHA);
|
|
|
8498 |
|
|
|
8499 |
if (!$gradingpanel) {
|
|
|
8500 |
|
|
|
8501 |
$buttonarray = array();
|
|
|
8502 |
$name = get_string('savechanges', 'assign');
|
|
|
8503 |
$buttonarray[] = $mform->createElement('submit', 'savegrade', $name);
|
|
|
8504 |
if (!$last) {
|
|
|
8505 |
$name = get_string('savenext', 'assign');
|
|
|
8506 |
$buttonarray[] = $mform->createElement('submit', 'saveandshownext', $name);
|
|
|
8507 |
}
|
|
|
8508 |
$buttonarray[] = $mform->createElement('cancel', 'cancelbutton', get_string('cancel'));
|
|
|
8509 |
$mform->addGroup($buttonarray, 'buttonar', '', array(' '), false);
|
|
|
8510 |
$mform->closeHeaderBefore('buttonar');
|
|
|
8511 |
$buttonarray = array();
|
|
|
8512 |
|
|
|
8513 |
if ($rownum > 0) {
|
|
|
8514 |
$name = get_string('previous', 'assign');
|
|
|
8515 |
$buttonarray[] = $mform->createElement('submit', 'nosaveandprevious', $name);
|
|
|
8516 |
}
|
|
|
8517 |
|
|
|
8518 |
if (!$last) {
|
|
|
8519 |
$name = get_string('nosavebutnext', 'assign');
|
|
|
8520 |
$buttonarray[] = $mform->createElement('submit', 'nosaveandnext', $name);
|
|
|
8521 |
}
|
|
|
8522 |
if (!empty($buttonarray)) {
|
|
|
8523 |
$mform->addGroup($buttonarray, 'navar', '', array(' '), false);
|
|
|
8524 |
}
|
|
|
8525 |
}
|
|
|
8526 |
// The grading form does not work well with shortforms.
|
|
|
8527 |
$mform->setDisableShortforms();
|
|
|
8528 |
}
|
|
|
8529 |
|
|
|
8530 |
/**
|
|
|
8531 |
* Add elements in submission plugin form.
|
|
|
8532 |
*
|
|
|
8533 |
* @param mixed $submission stdClass|null
|
|
|
8534 |
* @param MoodleQuickForm $mform
|
|
|
8535 |
* @param stdClass $data
|
|
|
8536 |
* @param int $userid The current userid (same as $USER->id)
|
|
|
8537 |
* @return void
|
|
|
8538 |
*/
|
1254 |
ariadna |
8539 |
protected function add_plugin_submission_elements(
|
|
|
8540 |
$submission,
|
|
|
8541 |
MoodleQuickForm $mform,
|
|
|
8542 |
stdClass $data,
|
|
|
8543 |
$userid
|
|
|
8544 |
) {
|
1 |
efrain |
8545 |
foreach ($this->submissionplugins as $plugin) {
|
|
|
8546 |
if ($plugin->is_enabled() && $plugin->is_visible() && $plugin->allow_submissions()) {
|
|
|
8547 |
$plugin->get_form_elements_for_user($submission, $mform, $data, $userid);
|
|
|
8548 |
}
|
|
|
8549 |
}
|
|
|
8550 |
}
|
|
|
8551 |
|
|
|
8552 |
/**
|
|
|
8553 |
* Check if feedback plugins installed are enabled.
|
|
|
8554 |
*
|
|
|
8555 |
* @return bool
|
|
|
8556 |
*/
|
1254 |
ariadna |
8557 |
public function is_any_feedback_plugin_enabled()
|
|
|
8558 |
{
|
1 |
efrain |
8559 |
if (!isset($this->cache['any_feedback_plugin_enabled'])) {
|
|
|
8560 |
$this->cache['any_feedback_plugin_enabled'] = false;
|
|
|
8561 |
foreach ($this->feedbackplugins as $plugin) {
|
|
|
8562 |
if ($plugin->is_enabled() && $plugin->is_visible()) {
|
|
|
8563 |
$this->cache['any_feedback_plugin_enabled'] = true;
|
|
|
8564 |
break;
|
|
|
8565 |
}
|
|
|
8566 |
}
|
|
|
8567 |
}
|
|
|
8568 |
|
|
|
8569 |
return $this->cache['any_feedback_plugin_enabled'];
|
|
|
8570 |
}
|
|
|
8571 |
|
|
|
8572 |
/**
|
|
|
8573 |
* Check if submission plugins installed are enabled.
|
|
|
8574 |
*
|
|
|
8575 |
* @return bool
|
|
|
8576 |
*/
|
1254 |
ariadna |
8577 |
public function is_any_submission_plugin_enabled()
|
|
|
8578 |
{
|
1 |
efrain |
8579 |
if (!isset($this->cache['any_submission_plugin_enabled'])) {
|
|
|
8580 |
$this->cache['any_submission_plugin_enabled'] = false;
|
|
|
8581 |
foreach ($this->submissionplugins as $plugin) {
|
|
|
8582 |
if ($plugin->is_enabled() && $plugin->is_visible() && $plugin->allow_submissions()) {
|
|
|
8583 |
$this->cache['any_submission_plugin_enabled'] = true;
|
|
|
8584 |
break;
|
|
|
8585 |
}
|
|
|
8586 |
}
|
|
|
8587 |
}
|
|
|
8588 |
|
|
|
8589 |
return $this->cache['any_submission_plugin_enabled'];
|
|
|
8590 |
}
|
|
|
8591 |
|
|
|
8592 |
/**
|
|
|
8593 |
* Add elements to submission form.
|
|
|
8594 |
* @param MoodleQuickForm $mform
|
|
|
8595 |
* @param stdClass $data
|
|
|
8596 |
* @return void
|
|
|
8597 |
*/
|
1254 |
ariadna |
8598 |
public function add_submission_form_elements(MoodleQuickForm $mform, stdClass $data)
|
|
|
8599 |
{
|
1 |
efrain |
8600 |
global $USER;
|
|
|
8601 |
|
|
|
8602 |
$userid = $data->userid;
|
|
|
8603 |
// Team submissions.
|
|
|
8604 |
if ($this->get_instance()->teamsubmission) {
|
|
|
8605 |
$submission = $this->get_group_submission($userid, 0, false);
|
|
|
8606 |
} else {
|
|
|
8607 |
$submission = $this->get_user_submission($userid, false);
|
|
|
8608 |
}
|
|
|
8609 |
|
|
|
8610 |
// Submission statement.
|
|
|
8611 |
$adminconfig = $this->get_admin_config();
|
|
|
8612 |
$requiresubmissionstatement = $this->get_instance()->requiresubmissionstatement;
|
|
|
8613 |
|
|
|
8614 |
$draftsenabled = $this->get_instance()->submissiondrafts;
|
|
|
8615 |
$submissionstatement = '';
|
|
|
8616 |
|
|
|
8617 |
if ($requiresubmissionstatement) {
|
|
|
8618 |
$submissionstatement = $this->get_submissionstatement($adminconfig, $this->get_instance(), $this->get_context());
|
|
|
8619 |
}
|
|
|
8620 |
|
|
|
8621 |
// If we get back an empty submission statement, we have to set $requiredsubmisisonstatement to false to prevent
|
|
|
8622 |
// that the submission statement checkbox will be displayed.
|
|
|
8623 |
if (empty($submissionstatement)) {
|
|
|
8624 |
$requiresubmissionstatement = false;
|
|
|
8625 |
}
|
|
|
8626 |
|
|
|
8627 |
$mform->addElement('header', 'submission header', get_string('addsubmission', 'mod_assign'));
|
|
|
8628 |
|
|
|
8629 |
// Only show submission statement if we are editing our own submission.
|
|
|
8630 |
if ($requiresubmissionstatement && !$draftsenabled && $userid == $USER->id) {
|
|
|
8631 |
$mform->addElement('checkbox', 'submissionstatement', '', $submissionstatement);
|
1254 |
ariadna |
8632 |
$mform->addRule(
|
|
|
8633 |
'submissionstatement',
|
|
|
8634 |
get_string('submissionstatementrequired', 'mod_assign'),
|
|
|
8635 |
'required',
|
|
|
8636 |
null,
|
|
|
8637 |
'client'
|
|
|
8638 |
);
|
1 |
efrain |
8639 |
}
|
|
|
8640 |
|
|
|
8641 |
$this->add_plugin_submission_elements($submission, $mform, $data, $userid);
|
|
|
8642 |
|
|
|
8643 |
// Hidden params.
|
|
|
8644 |
$mform->addElement('hidden', 'id', $this->get_course_module()->id);
|
|
|
8645 |
$mform->setType('id', PARAM_INT);
|
|
|
8646 |
|
|
|
8647 |
$mform->addElement('hidden', 'userid', $userid);
|
|
|
8648 |
$mform->setType('userid', PARAM_INT);
|
|
|
8649 |
|
|
|
8650 |
$mform->addElement('hidden', 'action', 'savesubmission');
|
|
|
8651 |
$mform->setType('action', PARAM_ALPHA);
|
|
|
8652 |
}
|
|
|
8653 |
|
|
|
8654 |
/**
|
|
|
8655 |
* Remove any data from the current submission.
|
|
|
8656 |
*
|
|
|
8657 |
* @param int $userid
|
|
|
8658 |
* @return boolean
|
|
|
8659 |
* @throws coding_exception
|
|
|
8660 |
*/
|
1254 |
ariadna |
8661 |
public function remove_submission($userid)
|
|
|
8662 |
{
|
1 |
efrain |
8663 |
global $USER;
|
|
|
8664 |
|
|
|
8665 |
if (!$this->can_edit_submission($userid, $USER->id)) {
|
|
|
8666 |
$user = core_user::get_user($userid);
|
|
|
8667 |
$message = get_string('usersubmissioncannotberemoved', 'assign', fullname($user));
|
|
|
8668 |
$this->set_error_message($message);
|
|
|
8669 |
return false;
|
|
|
8670 |
}
|
|
|
8671 |
|
|
|
8672 |
if ($this->get_instance()->teamsubmission) {
|
|
|
8673 |
$submission = $this->get_group_submission($userid, 0, false);
|
|
|
8674 |
} else {
|
|
|
8675 |
$submission = $this->get_user_submission($userid, false);
|
|
|
8676 |
}
|
|
|
8677 |
|
|
|
8678 |
if (!$submission) {
|
|
|
8679 |
return false;
|
|
|
8680 |
}
|
|
|
8681 |
$submission->status = $submission->attemptnumber ? ASSIGN_SUBMISSION_STATUS_REOPENED : ASSIGN_SUBMISSION_STATUS_NEW;
|
|
|
8682 |
$this->update_submission($submission, $userid, false, $this->get_instance()->teamsubmission);
|
|
|
8683 |
|
|
|
8684 |
// Tell each submission plugin we were saved with no data.
|
|
|
8685 |
$plugins = $this->get_submission_plugins();
|
|
|
8686 |
foreach ($plugins as $plugin) {
|
|
|
8687 |
if ($plugin->is_enabled() && $plugin->is_visible()) {
|
|
|
8688 |
$plugin->remove($submission);
|
|
|
8689 |
}
|
|
|
8690 |
}
|
|
|
8691 |
|
|
|
8692 |
$completion = new completion_info($this->get_course());
|
1254 |
ariadna |
8693 |
if (
|
|
|
8694 |
$completion->is_enabled($this->get_course_module()) &&
|
|
|
8695 |
$this->get_instance()->completionsubmit
|
|
|
8696 |
) {
|
1 |
efrain |
8697 |
$completion->update_state($this->get_course_module(), COMPLETION_INCOMPLETE, $userid);
|
|
|
8698 |
}
|
|
|
8699 |
|
|
|
8700 |
submission_removed::create_from_submission($this, $submission)->trigger();
|
|
|
8701 |
submission_status_updated::create_from_submission($this, $submission)->trigger();
|
|
|
8702 |
return true;
|
|
|
8703 |
}
|
|
|
8704 |
|
|
|
8705 |
/**
|
|
|
8706 |
* Revert to draft.
|
|
|
8707 |
*
|
|
|
8708 |
* @param int $userid
|
|
|
8709 |
* @return boolean
|
|
|
8710 |
*/
|
1254 |
ariadna |
8711 |
public function revert_to_draft($userid)
|
|
|
8712 |
{
|
1 |
efrain |
8713 |
global $DB, $USER;
|
|
|
8714 |
|
|
|
8715 |
// Need grade permission.
|
|
|
8716 |
require_capability('mod/assign:grade', $this->context);
|
|
|
8717 |
|
|
|
8718 |
if ($this->get_instance()->teamsubmission) {
|
|
|
8719 |
$submission = $this->get_group_submission($userid, 0, false);
|
|
|
8720 |
} else {
|
|
|
8721 |
$submission = $this->get_user_submission($userid, false);
|
|
|
8722 |
}
|
|
|
8723 |
|
|
|
8724 |
if (!$submission) {
|
|
|
8725 |
return false;
|
|
|
8726 |
}
|
|
|
8727 |
$submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
|
|
|
8728 |
$this->update_submission($submission, $userid, false, $this->get_instance()->teamsubmission);
|
|
|
8729 |
|
|
|
8730 |
// Give each submission plugin a chance to process the reverting to draft.
|
|
|
8731 |
$plugins = $this->get_submission_plugins();
|
|
|
8732 |
foreach ($plugins as $plugin) {
|
|
|
8733 |
if ($plugin->is_enabled() && $plugin->is_visible()) {
|
|
|
8734 |
$plugin->revert_to_draft($submission);
|
|
|
8735 |
}
|
|
|
8736 |
}
|
|
|
8737 |
// Update the modified time on the grade (grader modified).
|
|
|
8738 |
$grade = $this->get_user_grade($userid, true);
|
|
|
8739 |
$grade->grader = $USER->id;
|
|
|
8740 |
$this->update_grade($grade);
|
|
|
8741 |
|
|
|
8742 |
$completion = new completion_info($this->get_course());
|
1254 |
ariadna |
8743 |
if (
|
|
|
8744 |
$completion->is_enabled($this->get_course_module()) &&
|
|
|
8745 |
$this->get_instance()->completionsubmit
|
|
|
8746 |
) {
|
1 |
efrain |
8747 |
$completion->update_state($this->get_course_module(), COMPLETION_INCOMPLETE, $userid);
|
|
|
8748 |
}
|
|
|
8749 |
\mod_assign\event\submission_status_updated::create_from_submission($this, $submission)->trigger();
|
|
|
8750 |
return true;
|
|
|
8751 |
}
|
|
|
8752 |
|
|
|
8753 |
/**
|
|
|
8754 |
* Remove the current submission.
|
|
|
8755 |
*
|
|
|
8756 |
* @param int $userid
|
|
|
8757 |
* @return boolean
|
|
|
8758 |
*/
|
1254 |
ariadna |
8759 |
protected function process_remove_submission($userid = 0)
|
|
|
8760 |
{
|
1 |
efrain |
8761 |
require_sesskey();
|
|
|
8762 |
|
|
|
8763 |
if (!$userid) {
|
|
|
8764 |
$userid = required_param('userid', PARAM_INT);
|
|
|
8765 |
}
|
|
|
8766 |
|
|
|
8767 |
return $this->remove_submission($userid);
|
|
|
8768 |
}
|
|
|
8769 |
|
|
|
8770 |
/**
|
|
|
8771 |
* Revert to draft.
|
|
|
8772 |
* Uses url parameter userid if userid not supplied as a parameter.
|
|
|
8773 |
*
|
|
|
8774 |
* @param int $userid
|
|
|
8775 |
* @return boolean
|
|
|
8776 |
*/
|
1254 |
ariadna |
8777 |
protected function process_revert_to_draft($userid = 0)
|
|
|
8778 |
{
|
1 |
efrain |
8779 |
require_sesskey();
|
|
|
8780 |
|
|
|
8781 |
if (!$userid) {
|
|
|
8782 |
$userid = required_param('userid', PARAM_INT);
|
|
|
8783 |
}
|
|
|
8784 |
|
|
|
8785 |
return $this->revert_to_draft($userid);
|
|
|
8786 |
}
|
|
|
8787 |
|
|
|
8788 |
/**
|
|
|
8789 |
* Prevent student updates to this submission
|
|
|
8790 |
*
|
|
|
8791 |
* @param int $userid
|
|
|
8792 |
* @return bool
|
|
|
8793 |
*/
|
1254 |
ariadna |
8794 |
public function lock_submission($userid)
|
|
|
8795 |
{
|
1 |
efrain |
8796 |
global $USER, $DB;
|
|
|
8797 |
// Need grade permission.
|
|
|
8798 |
require_capability('mod/assign:grade', $this->context);
|
|
|
8799 |
|
|
|
8800 |
// Give each submission plugin a chance to process the locking.
|
|
|
8801 |
$plugins = $this->get_submission_plugins();
|
|
|
8802 |
$submission = $this->get_user_submission($userid, false);
|
|
|
8803 |
|
|
|
8804 |
$flags = $this->get_user_flags($userid, true);
|
|
|
8805 |
$flags->locked = 1;
|
|
|
8806 |
$this->update_user_flags($flags);
|
|
|
8807 |
|
|
|
8808 |
foreach ($plugins as $plugin) {
|
|
|
8809 |
if ($plugin->is_enabled() && $plugin->is_visible()) {
|
|
|
8810 |
$plugin->lock($submission, $flags);
|
|
|
8811 |
}
|
|
|
8812 |
}
|
|
|
8813 |
|
|
|
8814 |
$user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
|
|
|
8815 |
\mod_assign\event\submission_locked::create_from_user($this, $user)->trigger();
|
|
|
8816 |
return true;
|
|
|
8817 |
}
|
|
|
8818 |
|
|
|
8819 |
|
|
|
8820 |
/**
|
|
|
8821 |
* Set the workflow state for multiple users
|
|
|
8822 |
*
|
|
|
8823 |
* @return void
|
|
|
8824 |
*/
|
1254 |
ariadna |
8825 |
protected function process_set_batch_marking_workflow_state()
|
|
|
8826 |
{
|
1 |
efrain |
8827 |
global $CFG, $DB;
|
|
|
8828 |
|
|
|
8829 |
// Include batch marking workflow form.
|
|
|
8830 |
require_once($CFG->dirroot . '/mod/assign/batchsetmarkingworkflowstateform.php');
|
|
|
8831 |
|
|
|
8832 |
$formparams = array(
|
|
|
8833 |
'userscount' => 0, // This form is never re-displayed, so we don't need to
|
|
|
8834 |
'usershtml' => '', // initialise these parameters with real information.
|
|
|
8835 |
'markingworkflowstates' => $this->get_marking_workflow_states_for_current_user()
|
|
|
8836 |
);
|
|
|
8837 |
|
|
|
8838 |
$mform = new mod_assign_batch_set_marking_workflow_state_form(null, $formparams);
|
|
|
8839 |
|
|
|
8840 |
if ($mform->is_cancelled()) {
|
|
|
8841 |
return true;
|
|
|
8842 |
}
|
|
|
8843 |
|
|
|
8844 |
if ($formdata = $mform->get_data()) {
|
|
|
8845 |
$useridlist = explode(',', $formdata->selectedusers);
|
|
|
8846 |
$state = $formdata->markingworkflowstate;
|
|
|
8847 |
|
|
|
8848 |
foreach ($useridlist as $userid) {
|
|
|
8849 |
$flags = $this->get_user_flags($userid, true);
|
|
|
8850 |
|
|
|
8851 |
$flags->workflowstate = $state;
|
|
|
8852 |
|
|
|
8853 |
// Clear the mailed flag if notification is requested, the student hasn't been
|
|
|
8854 |
// notified previously, the student can access the assignment, and the state
|
|
|
8855 |
// is "Released".
|
|
|
8856 |
$modinfo = get_fast_modinfo($this->course, $userid);
|
|
|
8857 |
$cm = $modinfo->get_cm($this->get_course_module()->id);
|
1254 |
ariadna |
8858 |
if (
|
|
|
8859 |
$formdata->sendstudentnotifications && $cm->uservisible &&
|
|
|
8860 |
$state == ASSIGN_MARKING_WORKFLOW_STATE_RELEASED
|
|
|
8861 |
) {
|
1 |
efrain |
8862 |
$flags->mailed = 0;
|
|
|
8863 |
}
|
|
|
8864 |
|
|
|
8865 |
$gradingdisabled = $this->grading_disabled($userid);
|
|
|
8866 |
|
|
|
8867 |
// Will not apply update if user does not have permission to assign this workflow state.
|
|
|
8868 |
if (!$gradingdisabled && $this->update_user_flags($flags)) {
|
|
|
8869 |
// Update Gradebook.
|
|
|
8870 |
$grade = $this->get_user_grade($userid, true);
|
|
|
8871 |
// Fetch any feedback for this student.
|
|
|
8872 |
$gradebookplugin = $this->get_admin_config()->feedback_plugin_for_gradebook;
|
|
|
8873 |
$gradebookplugin = str_replace('assignfeedback_', '', $gradebookplugin);
|
|
|
8874 |
$plugin = $this->get_feedback_plugin_by_type($gradebookplugin);
|
|
|
8875 |
if ($plugin && $plugin->is_enabled() && $plugin->is_visible()) {
|
|
|
8876 |
$grade->feedbacktext = $plugin->text_for_gradebook($grade);
|
|
|
8877 |
$grade->feedbackformat = $plugin->format_for_gradebook($grade);
|
|
|
8878 |
$grade->feedbackfiles = $plugin->files_for_gradebook($grade);
|
|
|
8879 |
}
|
|
|
8880 |
$this->update_grade($grade);
|
|
|
8881 |
$assign = clone $this->get_instance();
|
|
|
8882 |
$assign->cmidnumber = $this->get_course_module()->idnumber;
|
|
|
8883 |
// Set assign gradebook feedback plugin status.
|
|
|
8884 |
$assign->gradefeedbackenabled = $this->is_gradebook_feedback_enabled();
|
|
|
8885 |
|
|
|
8886 |
// If markinganonymous is enabled then allow to release grades anonymously.
|
|
|
8887 |
if (isset($assign->markinganonymous) && $assign->markinganonymous == 1) {
|
|
|
8888 |
assign_update_grades($assign, $userid);
|
|
|
8889 |
}
|
|
|
8890 |
$user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
|
|
|
8891 |
\mod_assign\event\workflow_state_updated::create_from_user($this, $user, $state)->trigger();
|
|
|
8892 |
}
|
|
|
8893 |
}
|
|
|
8894 |
}
|
|
|
8895 |
}
|
|
|
8896 |
|
|
|
8897 |
/**
|
|
|
8898 |
* Set the marking allocation for multiple users
|
|
|
8899 |
*
|
|
|
8900 |
* @return void
|
|
|
8901 |
*/
|
1254 |
ariadna |
8902 |
protected function process_set_batch_marking_allocation()
|
|
|
8903 |
{
|
1 |
efrain |
8904 |
global $CFG, $DB;
|
|
|
8905 |
|
|
|
8906 |
// Include batch marking allocation form.
|
|
|
8907 |
require_once($CFG->dirroot . '/mod/assign/batchsetallocatedmarkerform.php');
|
|
|
8908 |
|
|
|
8909 |
$formparams = array(
|
|
|
8910 |
'userscount' => 0, // This form is never re-displayed, so we don't need to
|
|
|
8911 |
'usershtml' => '' // initialise these parameters with real information.
|
|
|
8912 |
);
|
|
|
8913 |
|
|
|
8914 |
list($sort, $params) = users_order_by_sql('u');
|
|
|
8915 |
// Only enrolled users could be assigned as potential markers.
|
|
|
8916 |
$markers = get_enrolled_users($this->get_context(), 'mod/assign:grade', 0, 'u.*', $sort);
|
|
|
8917 |
$markerlist = array();
|
|
|
8918 |
foreach ($markers as $marker) {
|
|
|
8919 |
$markerlist[$marker->id] = fullname($marker);
|
|
|
8920 |
}
|
|
|
8921 |
|
|
|
8922 |
$formparams['markers'] = $markerlist;
|
|
|
8923 |
|
|
|
8924 |
$mform = new mod_assign_batch_set_allocatedmarker_form(null, $formparams);
|
|
|
8925 |
|
|
|
8926 |
if ($mform->is_cancelled()) {
|
|
|
8927 |
return true;
|
|
|
8928 |
}
|
|
|
8929 |
|
|
|
8930 |
if ($formdata = $mform->get_data()) {
|
|
|
8931 |
$useridlist = explode(',', $formdata->selectedusers);
|
|
|
8932 |
$marker = $DB->get_record('user', array('id' => $formdata->allocatedmarker), '*', MUST_EXIST);
|
|
|
8933 |
|
|
|
8934 |
foreach ($useridlist as $userid) {
|
|
|
8935 |
$flags = $this->get_user_flags($userid, true);
|
1254 |
ariadna |
8936 |
if (
|
|
|
8937 |
$flags->workflowstate == ASSIGN_MARKING_WORKFLOW_STATE_READYFORREVIEW ||
|
1 |
efrain |
8938 |
$flags->workflowstate == ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW ||
|
|
|
8939 |
$flags->workflowstate == ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE ||
|
1254 |
ariadna |
8940 |
$flags->workflowstate == ASSIGN_MARKING_WORKFLOW_STATE_RELEASED
|
|
|
8941 |
) {
|
1 |
efrain |
8942 |
|
|
|
8943 |
continue; // Allocated marker can only be changed in certain workflow states.
|
|
|
8944 |
}
|
|
|
8945 |
|
|
|
8946 |
$flags->allocatedmarker = $marker->id;
|
|
|
8947 |
|
|
|
8948 |
if ($this->update_user_flags($flags)) {
|
|
|
8949 |
$user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
|
|
|
8950 |
\mod_assign\event\marker_updated::create_from_marker($this, $user, $marker)->trigger();
|
|
|
8951 |
}
|
|
|
8952 |
}
|
|
|
8953 |
}
|
|
|
8954 |
}
|
|
|
8955 |
|
|
|
8956 |
|
|
|
8957 |
/**
|
|
|
8958 |
* Prevent student updates to this submission.
|
|
|
8959 |
* Uses url parameter userid.
|
|
|
8960 |
*
|
|
|
8961 |
* @param int $userid
|
|
|
8962 |
* @return void
|
|
|
8963 |
*/
|
1254 |
ariadna |
8964 |
protected function process_lock_submission($userid = 0)
|
|
|
8965 |
{
|
1 |
efrain |
8966 |
|
|
|
8967 |
require_sesskey();
|
|
|
8968 |
|
|
|
8969 |
if (!$userid) {
|
|
|
8970 |
$userid = required_param('userid', PARAM_INT);
|
|
|
8971 |
}
|
|
|
8972 |
|
|
|
8973 |
return $this->lock_submission($userid);
|
|
|
8974 |
}
|
|
|
8975 |
|
|
|
8976 |
/**
|
|
|
8977 |
* Unlock the student submission.
|
|
|
8978 |
*
|
|
|
8979 |
* @param int $userid
|
|
|
8980 |
* @return bool
|
|
|
8981 |
*/
|
1254 |
ariadna |
8982 |
public function unlock_submission($userid)
|
|
|
8983 |
{
|
1 |
efrain |
8984 |
global $USER, $DB;
|
|
|
8985 |
|
|
|
8986 |
// Need grade permission.
|
|
|
8987 |
require_capability('mod/assign:grade', $this->context);
|
|
|
8988 |
|
|
|
8989 |
// Give each submission plugin a chance to process the unlocking.
|
|
|
8990 |
$plugins = $this->get_submission_plugins();
|
|
|
8991 |
$submission = $this->get_user_submission($userid, false);
|
|
|
8992 |
|
|
|
8993 |
$flags = $this->get_user_flags($userid, true);
|
|
|
8994 |
$flags->locked = 0;
|
|
|
8995 |
$this->update_user_flags($flags);
|
|
|
8996 |
|
|
|
8997 |
foreach ($plugins as $plugin) {
|
|
|
8998 |
if ($plugin->is_enabled() && $plugin->is_visible()) {
|
|
|
8999 |
$plugin->unlock($submission, $flags);
|
|
|
9000 |
}
|
|
|
9001 |
}
|
|
|
9002 |
|
|
|
9003 |
$user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
|
|
|
9004 |
\mod_assign\event\submission_unlocked::create_from_user($this, $user)->trigger();
|
|
|
9005 |
return true;
|
|
|
9006 |
}
|
|
|
9007 |
|
|
|
9008 |
/**
|
|
|
9009 |
* Unlock the student submission.
|
|
|
9010 |
* Uses url parameter userid.
|
|
|
9011 |
*
|
|
|
9012 |
* @param int $userid
|
|
|
9013 |
* @return bool
|
|
|
9014 |
*/
|
1254 |
ariadna |
9015 |
protected function process_unlock_submission($userid = 0)
|
|
|
9016 |
{
|
1 |
efrain |
9017 |
|
|
|
9018 |
require_sesskey();
|
|
|
9019 |
|
|
|
9020 |
if (!$userid) {
|
|
|
9021 |
$userid = required_param('userid', PARAM_INT);
|
|
|
9022 |
}
|
|
|
9023 |
|
|
|
9024 |
return $this->unlock_submission($userid);
|
|
|
9025 |
}
|
|
|
9026 |
|
|
|
9027 |
/**
|
|
|
9028 |
* Apply a grade from a grading form to a user (may be called multiple times for a group submission).
|
|
|
9029 |
*
|
|
|
9030 |
* @param stdClass $formdata - the data from the form
|
|
|
9031 |
* @param int $userid - the user to apply the grade to
|
|
|
9032 |
* @param int $attemptnumber - The attempt number to apply the grade to.
|
|
|
9033 |
* @return void
|
|
|
9034 |
*/
|
1254 |
ariadna |
9035 |
protected function apply_grade_to_user($formdata, $userid, $attemptnumber)
|
|
|
9036 |
{
|
1 |
efrain |
9037 |
global $USER, $CFG, $DB;
|
|
|
9038 |
|
|
|
9039 |
$grade = $this->get_user_grade($userid, true, $attemptnumber);
|
|
|
9040 |
$originalgrade = $grade->grade;
|
|
|
9041 |
$gradingdisabled = $this->grading_disabled($userid);
|
|
|
9042 |
$gradinginstance = $this->get_grading_instance($userid, $grade, $gradingdisabled);
|
|
|
9043 |
if (!$gradingdisabled) {
|
|
|
9044 |
if ($gradinginstance) {
|
1254 |
ariadna |
9045 |
$grade->grade = $gradinginstance->submit_and_get_grade(
|
|
|
9046 |
$formdata->advancedgrading,
|
|
|
9047 |
$grade->id
|
|
|
9048 |
);
|
1 |
efrain |
9049 |
} else {
|
|
|
9050 |
// Handle the case when grade is set to No Grade.
|
|
|
9051 |
if (isset($formdata->grade)) {
|
|
|
9052 |
$grade->grade = grade_floatval(unformat_float($formdata->grade));
|
|
|
9053 |
}
|
|
|
9054 |
}
|
|
|
9055 |
if (isset($formdata->workflowstate) || isset($formdata->allocatedmarker)) {
|
|
|
9056 |
$flags = $this->get_user_flags($userid, true);
|
|
|
9057 |
$oldworkflowstate = $flags->workflowstate;
|
|
|
9058 |
$flags->workflowstate = isset($formdata->workflowstate) ? $formdata->workflowstate : $flags->workflowstate;
|
|
|
9059 |
$flags->allocatedmarker = isset($formdata->allocatedmarker) ? $formdata->allocatedmarker : $flags->allocatedmarker;
|
1254 |
ariadna |
9060 |
if (
|
|
|
9061 |
$this->update_user_flags($flags) &&
|
|
|
9062 |
isset($formdata->workflowstate) &&
|
|
|
9063 |
$formdata->workflowstate !== $oldworkflowstate
|
|
|
9064 |
) {
|
1 |
efrain |
9065 |
$user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
|
|
|
9066 |
\mod_assign\event\workflow_state_updated::create_from_user($this, $user, $formdata->workflowstate)->trigger();
|
|
|
9067 |
}
|
|
|
9068 |
}
|
|
|
9069 |
}
|
1254 |
ariadna |
9070 |
$grade->grader = $USER->id;
|
1 |
efrain |
9071 |
|
|
|
9072 |
$adminconfig = $this->get_admin_config();
|
|
|
9073 |
$gradebookplugin = $adminconfig->feedback_plugin_for_gradebook;
|
|
|
9074 |
|
|
|
9075 |
$feedbackmodified = false;
|
|
|
9076 |
|
|
|
9077 |
// Call save in plugins.
|
|
|
9078 |
foreach ($this->feedbackplugins as $plugin) {
|
|
|
9079 |
if ($plugin->is_enabled() && $plugin->is_visible()) {
|
|
|
9080 |
$gradingmodified = $plugin->is_feedback_modified($grade, $formdata);
|
|
|
9081 |
if ($gradingmodified) {
|
|
|
9082 |
if (!$plugin->save($grade, $formdata)) {
|
|
|
9083 |
$result = false;
|
|
|
9084 |
throw new \moodle_exception($plugin->get_error());
|
|
|
9085 |
}
|
|
|
9086 |
// If $feedbackmodified is true, keep it true.
|
|
|
9087 |
$feedbackmodified = $feedbackmodified || $gradingmodified;
|
|
|
9088 |
}
|
|
|
9089 |
if (('assignfeedback_' . $plugin->get_type()) == $gradebookplugin) {
|
|
|
9090 |
// This is the feedback plugin chose to push comments to the gradebook.
|
|
|
9091 |
$grade->feedbacktext = $plugin->text_for_gradebook($grade);
|
|
|
9092 |
$grade->feedbackformat = $plugin->format_for_gradebook($grade);
|
|
|
9093 |
$grade->feedbackfiles = $plugin->files_for_gradebook($grade);
|
|
|
9094 |
}
|
|
|
9095 |
}
|
|
|
9096 |
}
|
|
|
9097 |
|
|
|
9098 |
// We do not want to update the timemodified if no grade was added.
|
1254 |
ariadna |
9099 |
if (
|
|
|
9100 |
!empty($formdata->addattempt) ||
|
|
|
9101 |
($originalgrade !== null && $originalgrade != -1) ||
|
|
|
9102 |
($grade->grade !== null && $grade->grade != -1) ||
|
|
|
9103 |
$feedbackmodified
|
|
|
9104 |
) {
|
1 |
efrain |
9105 |
$this->update_grade($grade, !empty($formdata->addattempt));
|
|
|
9106 |
}
|
|
|
9107 |
|
|
|
9108 |
// We never send notifications if we have marking workflow and the grade is not released.
|
1254 |
ariadna |
9109 |
if (
|
|
|
9110 |
$this->get_instance()->markingworkflow &&
|
|
|
9111 |
isset($formdata->workflowstate) &&
|
|
|
9112 |
$formdata->workflowstate != ASSIGN_MARKING_WORKFLOW_STATE_RELEASED
|
|
|
9113 |
) {
|
1 |
efrain |
9114 |
$formdata->sendstudentnotifications = false;
|
|
|
9115 |
}
|
|
|
9116 |
|
|
|
9117 |
// Note the default if not provided for this option is true (e.g. webservices).
|
|
|
9118 |
// This is for backwards compatibility.
|
|
|
9119 |
if (!isset($formdata->sendstudentnotifications) || $formdata->sendstudentnotifications) {
|
|
|
9120 |
$this->notify_grade_modified($grade, true);
|
|
|
9121 |
}
|
|
|
9122 |
}
|
|
|
9123 |
|
|
|
9124 |
|
|
|
9125 |
/**
|
|
|
9126 |
* Save outcomes submitted from grading form.
|
|
|
9127 |
*
|
|
|
9128 |
* @param int $userid
|
|
|
9129 |
* @param stdClass $formdata
|
|
|
9130 |
* @param int $sourceuserid The user ID under which the outcome data is accessible. This is relevant
|
|
|
9131 |
* for an outcome set to a user but applied to an entire group.
|
|
|
9132 |
*/
|
1254 |
ariadna |
9133 |
protected function process_outcomes($userid, $formdata, $sourceuserid = null)
|
|
|
9134 |
{
|
1 |
efrain |
9135 |
global $CFG, $USER;
|
|
|
9136 |
|
|
|
9137 |
if (empty($CFG->enableoutcomes)) {
|
|
|
9138 |
return;
|
|
|
9139 |
}
|
|
|
9140 |
if ($this->grading_disabled($userid)) {
|
|
|
9141 |
return;
|
|
|
9142 |
}
|
|
|
9143 |
|
1254 |
ariadna |
9144 |
require_once($CFG->libdir . '/gradelib.php');
|
1 |
efrain |
9145 |
|
|
|
9146 |
$data = array();
|
1254 |
ariadna |
9147 |
$gradinginfo = grade_get_grades(
|
|
|
9148 |
$this->get_course()->id,
|
|
|
9149 |
'mod',
|
|
|
9150 |
'assign',
|
|
|
9151 |
$this->get_instance()->id,
|
|
|
9152 |
$userid
|
|
|
9153 |
);
|
1 |
efrain |
9154 |
|
|
|
9155 |
if (!empty($gradinginfo->outcomes)) {
|
|
|
9156 |
foreach ($gradinginfo->outcomes as $index => $oldoutcome) {
|
1254 |
ariadna |
9157 |
$name = 'outcome_' . $index;
|
1 |
efrain |
9158 |
$sourceuserid = $sourceuserid !== null ? $sourceuserid : $userid;
|
1254 |
ariadna |
9159 |
if (
|
|
|
9160 |
isset($formdata->{$name}[$sourceuserid]) &&
|
|
|
9161 |
$oldoutcome->grades[$userid]->grade != $formdata->{$name}[$sourceuserid]
|
|
|
9162 |
) {
|
1 |
efrain |
9163 |
$data[$index] = $formdata->{$name}[$sourceuserid];
|
|
|
9164 |
}
|
|
|
9165 |
}
|
|
|
9166 |
}
|
|
|
9167 |
if (count($data) > 0) {
|
1254 |
ariadna |
9168 |
grade_update_outcomes(
|
|
|
9169 |
'mod/assign',
|
|
|
9170 |
$this->course->id,
|
|
|
9171 |
'mod',
|
|
|
9172 |
'assign',
|
|
|
9173 |
$this->get_instance()->id,
|
|
|
9174 |
$userid,
|
|
|
9175 |
$data
|
|
|
9176 |
);
|
1 |
efrain |
9177 |
}
|
|
|
9178 |
}
|
|
|
9179 |
|
|
|
9180 |
/**
|
|
|
9181 |
* If the requirements are met - reopen the submission for another attempt.
|
|
|
9182 |
* Only call this function when grading the latest attempt.
|
|
|
9183 |
*
|
|
|
9184 |
* @param int $userid The userid.
|
|
|
9185 |
* @param stdClass $submission The submission (may be a group submission).
|
|
|
9186 |
* @param bool $addattempt - True if the "allow another attempt" checkbox was checked.
|
|
|
9187 |
* @return bool - true if another attempt was added.
|
|
|
9188 |
*/
|
1254 |
ariadna |
9189 |
protected function reopen_submission_if_required($userid, $submission, $addattempt)
|
|
|
9190 |
{
|
1 |
efrain |
9191 |
$instance = $this->get_instance();
|
|
|
9192 |
$maxattemptsreached = !empty($submission) &&
|
1254 |
ariadna |
9193 |
$submission->attemptnumber >= ($instance->maxattempts - 1) &&
|
|
|
9194 |
$instance->maxattempts != ASSIGN_UNLIMITED_ATTEMPTS;
|
1 |
efrain |
9195 |
$shouldreopen = false;
|
|
|
9196 |
if ($instance->attemptreopenmethod == ASSIGN_ATTEMPT_REOPEN_METHOD_UNTILPASS) {
|
|
|
9197 |
// Check the gradetopass from the gradebook.
|
|
|
9198 |
$gradeitem = $this->get_grade_item();
|
|
|
9199 |
if ($gradeitem) {
|
|
|
9200 |
$gradegrade = grade_grade::fetch(array('userid' => $userid, 'itemid' => $gradeitem->id));
|
|
|
9201 |
|
|
|
9202 |
// Do not reopen if is_passed returns null, e.g. if there is no pass criterion set.
|
|
|
9203 |
if ($gradegrade && ($gradegrade->is_passed() === false)) {
|
|
|
9204 |
$shouldreopen = true;
|
|
|
9205 |
}
|
|
|
9206 |
}
|
|
|
9207 |
}
|
1254 |
ariadna |
9208 |
if (
|
|
|
9209 |
$instance->attemptreopenmethod == ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL &&
|
|
|
9210 |
!empty($addattempt)
|
|
|
9211 |
) {
|
1 |
efrain |
9212 |
$shouldreopen = true;
|
|
|
9213 |
}
|
|
|
9214 |
if ($shouldreopen && !$maxattemptsreached) {
|
|
|
9215 |
$this->add_attempt($userid);
|
|
|
9216 |
return true;
|
|
|
9217 |
}
|
|
|
9218 |
return false;
|
|
|
9219 |
}
|
|
|
9220 |
|
|
|
9221 |
/**
|
|
|
9222 |
* Save grade update.
|
|
|
9223 |
*
|
|
|
9224 |
* @param int $userid
|
|
|
9225 |
* @param stdClass $data
|
|
|
9226 |
* @return bool - was the grade saved
|
|
|
9227 |
*/
|
1254 |
ariadna |
9228 |
public function save_grade($userid, $data)
|
|
|
9229 |
{
|
1 |
efrain |
9230 |
|
|
|
9231 |
// Need grade permission.
|
|
|
9232 |
require_capability('mod/assign:grade', $this->context);
|
|
|
9233 |
|
|
|
9234 |
$instance = $this->get_instance();
|
|
|
9235 |
$submission = null;
|
|
|
9236 |
if ($instance->teamsubmission) {
|
|
|
9237 |
// We need to know what the most recent group submission is.
|
|
|
9238 |
// Specifically when determining if we are adding another attempt (we only want to add one attempt per team),
|
|
|
9239 |
// and when deciding if we need to update the gradebook with an edited grade.
|
|
|
9240 |
$mostrecentsubmission = $this->get_group_submission($userid, 0, false, -1);
|
|
|
9241 |
$this->set_most_recent_team_submission($mostrecentsubmission);
|
|
|
9242 |
// Get the submission that we are saving grades for. The data attempt number determines which submission attempt.
|
|
|
9243 |
$submission = $this->get_group_submission($userid, 0, false, $data->attemptnumber);
|
|
|
9244 |
} else {
|
|
|
9245 |
$submission = $this->get_user_submission($userid, false, $data->attemptnumber);
|
|
|
9246 |
}
|
|
|
9247 |
if ($instance->teamsubmission && !empty($data->applytoall)) {
|
|
|
9248 |
$groupid = 0;
|
|
|
9249 |
if ($this->get_submission_group($userid)) {
|
|
|
9250 |
$group = $this->get_submission_group($userid);
|
|
|
9251 |
if ($group) {
|
|
|
9252 |
$groupid = $group->id;
|
|
|
9253 |
}
|
|
|
9254 |
}
|
|
|
9255 |
$members = $this->get_submission_group_members($groupid, true, $this->show_only_active_users());
|
|
|
9256 |
foreach ($members as $member) {
|
|
|
9257 |
// We only want to update the grade for this group submission attempt. The data attempt number could be
|
|
|
9258 |
// -1 which may end up in additional attempts being created for each group member instead of just one
|
|
|
9259 |
// additional attempt for the group.
|
|
|
9260 |
$this->apply_grade_to_user($data, $member->id, $submission->attemptnumber);
|
|
|
9261 |
$this->process_outcomes($member->id, $data, $userid);
|
|
|
9262 |
}
|
|
|
9263 |
} else {
|
|
|
9264 |
$this->apply_grade_to_user($data, $userid, $data->attemptnumber);
|
|
|
9265 |
|
|
|
9266 |
$this->process_outcomes($userid, $data);
|
|
|
9267 |
}
|
|
|
9268 |
|
|
|
9269 |
return true;
|
|
|
9270 |
}
|
|
|
9271 |
|
|
|
9272 |
/**
|
|
|
9273 |
* Save grade.
|
|
|
9274 |
*
|
|
|
9275 |
* @param moodleform $mform
|
|
|
9276 |
* @return bool - was the grade saved
|
|
|
9277 |
*/
|
1254 |
ariadna |
9278 |
protected function process_save_grade(&$mform)
|
|
|
9279 |
{
|
1 |
efrain |
9280 |
global $CFG, $SESSION;
|
|
|
9281 |
// Include grade form.
|
|
|
9282 |
require_once($CFG->dirroot . '/mod/assign/gradeform.php');
|
|
|
9283 |
|
|
|
9284 |
require_sesskey();
|
|
|
9285 |
|
|
|
9286 |
$instance = $this->get_instance();
|
|
|
9287 |
$rownum = required_param('rownum', PARAM_INT);
|
|
|
9288 |
$attemptnumber = optional_param('attemptnumber', -1, PARAM_INT);
|
|
|
9289 |
$useridlistid = optional_param('useridlistid', $this->get_useridlist_key_id(), PARAM_ALPHANUM);
|
|
|
9290 |
$userid = optional_param('userid', 0, PARAM_INT);
|
|
|
9291 |
if (!$userid) {
|
|
|
9292 |
if (empty($SESSION->mod_assign_useridlist[$this->get_useridlist_key($useridlistid)])) {
|
|
|
9293 |
// If the userid list is not stored we must not save, as it is possible that the user in a
|
|
|
9294 |
// given row position may not be the same now as when the grading page was generated.
|
|
|
9295 |
$url = new moodle_url('/mod/assign/view.php', array('id' => $this->get_course_module()->id));
|
|
|
9296 |
throw new moodle_exception('useridlistnotcached', 'mod_assign', $url);
|
|
|
9297 |
}
|
|
|
9298 |
$useridlist = $SESSION->mod_assign_useridlist[$this->get_useridlist_key($useridlistid)];
|
|
|
9299 |
} else {
|
|
|
9300 |
$useridlist = array($userid);
|
|
|
9301 |
$rownum = 0;
|
|
|
9302 |
}
|
|
|
9303 |
|
|
|
9304 |
$last = false;
|
|
|
9305 |
$userid = $useridlist[$rownum];
|
|
|
9306 |
if ($rownum == count($useridlist) - 1) {
|
|
|
9307 |
$last = true;
|
|
|
9308 |
}
|
|
|
9309 |
|
|
|
9310 |
$data = new stdClass();
|
|
|
9311 |
|
1254 |
ariadna |
9312 |
$gradeformparams = array(
|
|
|
9313 |
'rownum' => $rownum,
|
|
|
9314 |
'useridlistid' => $useridlistid,
|
|
|
9315 |
'last' => $last,
|
|
|
9316 |
'attemptnumber' => $attemptnumber,
|
|
|
9317 |
'userid' => $userid
|
|
|
9318 |
);
|
|
|
9319 |
$mform = new mod_assign_grade_form(
|
|
|
9320 |
null,
|
|
|
9321 |
array($this, $data, $gradeformparams),
|
|
|
9322 |
'post',
|
|
|
9323 |
'',
|
|
|
9324 |
array('class' => 'gradeform')
|
|
|
9325 |
);
|
1 |
efrain |
9326 |
|
|
|
9327 |
if ($formdata = $mform->get_data()) {
|
|
|
9328 |
return $this->save_grade($userid, $formdata);
|
|
|
9329 |
} else {
|
|
|
9330 |
return false;
|
|
|
9331 |
}
|
|
|
9332 |
}
|
|
|
9333 |
|
|
|
9334 |
/**
|
|
|
9335 |
* This function is a static wrapper around can_upgrade.
|
|
|
9336 |
*
|
|
|
9337 |
* @param string $type The plugin type
|
|
|
9338 |
* @param int $version The plugin version
|
|
|
9339 |
* @return bool
|
|
|
9340 |
*/
|
1254 |
ariadna |
9341 |
public static function can_upgrade_assignment($type, $version)
|
|
|
9342 |
{
|
1 |
efrain |
9343 |
$assignment = new assign(null, null, null);
|
|
|
9344 |
return $assignment->can_upgrade($type, $version);
|
|
|
9345 |
}
|
|
|
9346 |
|
|
|
9347 |
/**
|
|
|
9348 |
* This function returns true if it can upgrade an assignment from the 2.2 module.
|
|
|
9349 |
*
|
|
|
9350 |
* @param string $type The plugin type
|
|
|
9351 |
* @param int $version The plugin version
|
|
|
9352 |
* @return bool
|
|
|
9353 |
*/
|
1254 |
ariadna |
9354 |
public function can_upgrade($type, $version)
|
|
|
9355 |
{
|
1 |
efrain |
9356 |
if ($type == 'offline' && $version >= 2011112900) {
|
|
|
9357 |
return true;
|
|
|
9358 |
}
|
|
|
9359 |
foreach ($this->submissionplugins as $plugin) {
|
|
|
9360 |
if ($plugin->can_upgrade($type, $version)) {
|
|
|
9361 |
return true;
|
|
|
9362 |
}
|
|
|
9363 |
}
|
|
|
9364 |
foreach ($this->feedbackplugins as $plugin) {
|
|
|
9365 |
if ($plugin->can_upgrade($type, $version)) {
|
|
|
9366 |
return true;
|
|
|
9367 |
}
|
|
|
9368 |
}
|
|
|
9369 |
return false;
|
|
|
9370 |
}
|
|
|
9371 |
|
|
|
9372 |
/**
|
|
|
9373 |
* Copy all the files from the old assignment files area to the new one.
|
|
|
9374 |
* This is used by the plugin upgrade code.
|
|
|
9375 |
*
|
|
|
9376 |
* @param int $oldcontextid The old assignment context id
|
|
|
9377 |
* @param int $oldcomponent The old assignment component ('assignment')
|
|
|
9378 |
* @param int $oldfilearea The old assignment filearea ('submissions')
|
|
|
9379 |
* @param int $olditemid The old submissionid (can be null e.g. intro)
|
|
|
9380 |
* @param int $newcontextid The new assignment context id
|
|
|
9381 |
* @param int $newcomponent The new assignment component ('assignment')
|
|
|
9382 |
* @param int $newfilearea The new assignment filearea ('submissions')
|
|
|
9383 |
* @param int $newitemid The new submissionid (can be null e.g. intro)
|
|
|
9384 |
* @return int The number of files copied
|
|
|
9385 |
*/
|
1254 |
ariadna |
9386 |
public function copy_area_files_for_upgrade(
|
|
|
9387 |
$oldcontextid,
|
|
|
9388 |
$oldcomponent,
|
|
|
9389 |
$oldfilearea,
|
|
|
9390 |
$olditemid,
|
|
|
9391 |
$newcontextid,
|
|
|
9392 |
$newcomponent,
|
|
|
9393 |
$newfilearea,
|
|
|
9394 |
$newitemid
|
|
|
9395 |
) {
|
1 |
efrain |
9396 |
// Note, this code is based on some code in filestorage - but that code
|
|
|
9397 |
// deleted the old files (which we don't want).
|
|
|
9398 |
$count = 0;
|
|
|
9399 |
|
|
|
9400 |
$fs = get_file_storage();
|
|
|
9401 |
|
1254 |
ariadna |
9402 |
$oldfiles = $fs->get_area_files(
|
|
|
9403 |
$oldcontextid,
|
|
|
9404 |
$oldcomponent,
|
|
|
9405 |
$oldfilearea,
|
|
|
9406 |
$olditemid,
|
|
|
9407 |
'id',
|
|
|
9408 |
false
|
|
|
9409 |
);
|
1 |
efrain |
9410 |
foreach ($oldfiles as $oldfile) {
|
|
|
9411 |
$filerecord = new stdClass();
|
|
|
9412 |
$filerecord->contextid = $newcontextid;
|
|
|
9413 |
$filerecord->component = $newcomponent;
|
|
|
9414 |
$filerecord->filearea = $newfilearea;
|
|
|
9415 |
$filerecord->itemid = $newitemid;
|
|
|
9416 |
$fs->create_file_from_storedfile($filerecord, $oldfile);
|
|
|
9417 |
$count += 1;
|
|
|
9418 |
}
|
|
|
9419 |
|
|
|
9420 |
return $count;
|
|
|
9421 |
}
|
|
|
9422 |
|
|
|
9423 |
/**
|
|
|
9424 |
* Add a new attempt for each user in the list - but reopen each group assignment
|
|
|
9425 |
* at most 1 time.
|
|
|
9426 |
*
|
|
|
9427 |
* @param array $useridlist Array of userids to reopen.
|
|
|
9428 |
* @return bool
|
|
|
9429 |
*/
|
1254 |
ariadna |
9430 |
protected function process_add_attempt_group($useridlist)
|
|
|
9431 |
{
|
1 |
efrain |
9432 |
$groupsprocessed = array();
|
|
|
9433 |
$result = true;
|
|
|
9434 |
|
|
|
9435 |
foreach ($useridlist as $userid) {
|
|
|
9436 |
$groupid = 0;
|
|
|
9437 |
$group = $this->get_submission_group($userid);
|
|
|
9438 |
if ($group) {
|
|
|
9439 |
$groupid = $group->id;
|
|
|
9440 |
}
|
|
|
9441 |
|
|
|
9442 |
if (empty($groupsprocessed[$groupid])) {
|
|
|
9443 |
// We need to know what the most recent group submission is.
|
|
|
9444 |
// Specifically when determining if we are adding another attempt (we only want to add one attempt per team),
|
|
|
9445 |
// and when deciding if we need to update the gradebook with an edited grade.
|
|
|
9446 |
$currentsubmission = $this->get_group_submission($userid, 0, false, -1);
|
|
|
9447 |
$this->set_most_recent_team_submission($currentsubmission);
|
|
|
9448 |
$result = $this->process_add_attempt($userid) && $result;
|
|
|
9449 |
$groupsprocessed[$groupid] = true;
|
|
|
9450 |
}
|
|
|
9451 |
}
|
|
|
9452 |
return $result;
|
|
|
9453 |
}
|
|
|
9454 |
|
|
|
9455 |
/**
|
|
|
9456 |
* Check for a sess key and then call add_attempt.
|
|
|
9457 |
*
|
|
|
9458 |
* @param int $userid int The user to add the attempt for
|
|
|
9459 |
* @return bool - true if successful.
|
|
|
9460 |
*/
|
1254 |
ariadna |
9461 |
protected function process_add_attempt($userid)
|
|
|
9462 |
{
|
1 |
efrain |
9463 |
require_sesskey();
|
|
|
9464 |
|
|
|
9465 |
return $this->add_attempt($userid);
|
|
|
9466 |
}
|
|
|
9467 |
|
|
|
9468 |
/**
|
|
|
9469 |
* Add a new attempt for a user.
|
|
|
9470 |
*
|
|
|
9471 |
* @param int $userid int The user to add the attempt for
|
|
|
9472 |
* @return bool - true if successful.
|
|
|
9473 |
*/
|
1254 |
ariadna |
9474 |
protected function add_attempt($userid)
|
|
|
9475 |
{
|
1 |
efrain |
9476 |
require_capability('mod/assign:grade', $this->context);
|
|
|
9477 |
|
|
|
9478 |
if ($this->get_instance()->attemptreopenmethod == ASSIGN_ATTEMPT_REOPEN_METHOD_NONE) {
|
|
|
9479 |
return false;
|
|
|
9480 |
}
|
|
|
9481 |
|
|
|
9482 |
if ($this->get_instance()->teamsubmission) {
|
|
|
9483 |
$oldsubmission = $this->get_group_submission($userid, 0, false);
|
|
|
9484 |
} else {
|
|
|
9485 |
$oldsubmission = $this->get_user_submission($userid, false);
|
|
|
9486 |
}
|
|
|
9487 |
|
|
|
9488 |
if (!$oldsubmission) {
|
|
|
9489 |
return false;
|
|
|
9490 |
}
|
|
|
9491 |
|
|
|
9492 |
// No more than max attempts allowed.
|
1254 |
ariadna |
9493 |
if (
|
|
|
9494 |
$this->get_instance()->maxattempts != ASSIGN_UNLIMITED_ATTEMPTS &&
|
|
|
9495 |
$oldsubmission->attemptnumber >= ($this->get_instance()->maxattempts - 1)
|
|
|
9496 |
) {
|
1 |
efrain |
9497 |
return false;
|
|
|
9498 |
}
|
|
|
9499 |
|
|
|
9500 |
// Create the new submission record for the group/user.
|
|
|
9501 |
if ($this->get_instance()->teamsubmission) {
|
|
|
9502 |
if (isset($this->mostrecentteamsubmission)) {
|
|
|
9503 |
// Team submissions can end up in this function for each user (via save_grade). We don't want to create
|
|
|
9504 |
// more than one attempt for the whole team.
|
|
|
9505 |
if ($this->mostrecentteamsubmission->attemptnumber == $oldsubmission->attemptnumber) {
|
|
|
9506 |
$newsubmission = $this->get_group_submission($userid, 0, true, $oldsubmission->attemptnumber + 1);
|
|
|
9507 |
} else {
|
|
|
9508 |
$newsubmission = $this->get_group_submission($userid, 0, false, $oldsubmission->attemptnumber);
|
|
|
9509 |
}
|
|
|
9510 |
} else {
|
|
|
9511 |
debugging('Please use set_most_recent_team_submission() before calling add_attempt', DEBUG_DEVELOPER);
|
|
|
9512 |
$newsubmission = $this->get_group_submission($userid, 0, true, $oldsubmission->attemptnumber + 1);
|
|
|
9513 |
}
|
|
|
9514 |
} else {
|
|
|
9515 |
$newsubmission = $this->get_user_submission($userid, true, $oldsubmission->attemptnumber + 1);
|
|
|
9516 |
}
|
|
|
9517 |
|
|
|
9518 |
// Set the status of the new attempt to reopened.
|
|
|
9519 |
$newsubmission->status = ASSIGN_SUBMISSION_STATUS_REOPENED;
|
|
|
9520 |
|
|
|
9521 |
// Give each submission plugin a chance to process the add_attempt.
|
|
|
9522 |
$plugins = $this->get_submission_plugins();
|
|
|
9523 |
foreach ($plugins as $plugin) {
|
|
|
9524 |
if ($plugin->is_enabled() && $plugin->is_visible()) {
|
|
|
9525 |
$plugin->add_attempt($oldsubmission, $newsubmission);
|
|
|
9526 |
}
|
|
|
9527 |
}
|
|
|
9528 |
|
|
|
9529 |
$this->update_submission($newsubmission, $userid, false, $this->get_instance()->teamsubmission);
|
|
|
9530 |
$flags = $this->get_user_flags($userid, false);
|
|
|
9531 |
if (isset($flags->locked) && $flags->locked) { // May not exist.
|
|
|
9532 |
$this->process_unlock_submission($userid);
|
|
|
9533 |
}
|
|
|
9534 |
return true;
|
|
|
9535 |
}
|
|
|
9536 |
|
|
|
9537 |
/**
|
|
|
9538 |
* Get an upto date list of user grades and feedback for the gradebook.
|
|
|
9539 |
*
|
|
|
9540 |
* @param int $userid int or 0 for all users
|
|
|
9541 |
* @return array of grade data formated for the gradebook api
|
|
|
9542 |
* The data required by the gradebook api is userid,
|
|
|
9543 |
* rawgrade,
|
|
|
9544 |
* feedback,
|
|
|
9545 |
* feedbackformat,
|
|
|
9546 |
* usermodified,
|
|
|
9547 |
* dategraded,
|
|
|
9548 |
* datesubmitted
|
|
|
9549 |
*/
|
1254 |
ariadna |
9550 |
public function get_user_grades_for_gradebook($userid)
|
|
|
9551 |
{
|
1 |
efrain |
9552 |
global $DB, $CFG;
|
|
|
9553 |
$grades = array();
|
|
|
9554 |
$assignmentid = $this->get_instance()->id;
|
|
|
9555 |
|
|
|
9556 |
$adminconfig = $this->get_admin_config();
|
|
|
9557 |
$gradebookpluginname = $adminconfig->feedback_plugin_for_gradebook;
|
|
|
9558 |
$gradebookplugin = null;
|
|
|
9559 |
|
|
|
9560 |
// Find the gradebook plugin.
|
|
|
9561 |
foreach ($this->feedbackplugins as $plugin) {
|
|
|
9562 |
if ($plugin->is_enabled() && $plugin->is_visible()) {
|
|
|
9563 |
if (('assignfeedback_' . $plugin->get_type()) == $gradebookpluginname) {
|
|
|
9564 |
$gradebookplugin = $plugin;
|
|
|
9565 |
}
|
|
|
9566 |
}
|
|
|
9567 |
}
|
|
|
9568 |
if ($userid) {
|
|
|
9569 |
$where = ' WHERE u.id = :userid ';
|
|
|
9570 |
} else {
|
|
|
9571 |
$where = ' WHERE u.id != :userid ';
|
|
|
9572 |
}
|
|
|
9573 |
|
|
|
9574 |
// When the gradebook asks us for grades - only return the last attempt for each user.
|
1254 |
ariadna |
9575 |
$params = array(
|
|
|
9576 |
'assignid1' => $assignmentid,
|
|
|
9577 |
'assignid2' => $assignmentid,
|
|
|
9578 |
'userid' => $userid
|
|
|
9579 |
);
|
1 |
efrain |
9580 |
$graderesults = $DB->get_recordset_sql('SELECT
|
|
|
9581 |
u.id as userid,
|
|
|
9582 |
s.timemodified as datesubmitted,
|
|
|
9583 |
g.grade as rawgrade,
|
|
|
9584 |
g.timemodified as dategraded,
|
|
|
9585 |
g.grader as usermodified
|
|
|
9586 |
FROM {user} u
|
|
|
9587 |
LEFT JOIN {assign_submission} s
|
|
|
9588 |
ON u.id = s.userid and s.assignment = :assignid1 AND
|
|
|
9589 |
s.latest = 1
|
|
|
9590 |
JOIN {assign_grades} g
|
|
|
9591 |
ON u.id = g.userid and g.assignment = :assignid2 AND
|
|
|
9592 |
g.attemptnumber = s.attemptnumber' .
|
1254 |
ariadna |
9593 |
$where, $params);
|
1 |
efrain |
9594 |
|
|
|
9595 |
foreach ($graderesults as $result) {
|
|
|
9596 |
$gradingstatus = $this->get_grading_status($result->userid);
|
|
|
9597 |
if (!$this->get_instance()->markingworkflow || $gradingstatus == ASSIGN_MARKING_WORKFLOW_STATE_RELEASED) {
|
|
|
9598 |
$gradebookgrade = clone $result;
|
|
|
9599 |
// Now get the feedback.
|
|
|
9600 |
if ($gradebookplugin) {
|
|
|
9601 |
$grade = $this->get_user_grade($result->userid, false);
|
|
|
9602 |
if ($grade) {
|
|
|
9603 |
$feedbacktext = $gradebookplugin->text_for_gradebook($grade);
|
|
|
9604 |
if (!empty($feedbacktext)) {
|
|
|
9605 |
$gradebookgrade->feedback = $feedbacktext;
|
|
|
9606 |
}
|
|
|
9607 |
$gradebookgrade->feedbackformat = $gradebookplugin->format_for_gradebook($grade);
|
|
|
9608 |
$gradebookgrade->feedbackfiles = $gradebookplugin->files_for_gradebook($grade);
|
|
|
9609 |
}
|
|
|
9610 |
}
|
|
|
9611 |
$grades[$gradebookgrade->userid] = $gradebookgrade;
|
|
|
9612 |
}
|
|
|
9613 |
}
|
|
|
9614 |
|
|
|
9615 |
$graderesults->close();
|
|
|
9616 |
return $grades;
|
|
|
9617 |
}
|
|
|
9618 |
|
|
|
9619 |
/**
|
|
|
9620 |
* Call the static version of this function
|
|
|
9621 |
*
|
|
|
9622 |
* @param int $userid The userid to lookup
|
|
|
9623 |
* @return int The unique id
|
|
|
9624 |
*/
|
1254 |
ariadna |
9625 |
public function get_uniqueid_for_user($userid)
|
|
|
9626 |
{
|
1 |
efrain |
9627 |
return self::get_uniqueid_for_user_static($this->get_instance()->id, $userid);
|
|
|
9628 |
}
|
|
|
9629 |
|
|
|
9630 |
/**
|
|
|
9631 |
* Foreach participant in the course - assign them a random id.
|
|
|
9632 |
*
|
|
|
9633 |
* @param int $assignid The assignid to lookup
|
|
|
9634 |
*/
|
1254 |
ariadna |
9635 |
public static function allocate_unique_ids($assignid)
|
|
|
9636 |
{
|
1 |
efrain |
9637 |
global $DB;
|
|
|
9638 |
|
|
|
9639 |
$cm = get_coursemodule_from_instance('assign', $assignid, 0, false, MUST_EXIST);
|
|
|
9640 |
$context = context_module::instance($cm->id);
|
|
|
9641 |
|
|
|
9642 |
$currentgroup = groups_get_activity_group($cm, true);
|
|
|
9643 |
$users = get_enrolled_users($context, "mod/assign:submit", $currentgroup, 'u.id');
|
|
|
9644 |
|
|
|
9645 |
// Shuffle the users.
|
|
|
9646 |
shuffle($users);
|
|
|
9647 |
|
|
|
9648 |
foreach ($users as $user) {
|
1254 |
ariadna |
9649 |
$record = $DB->get_record(
|
|
|
9650 |
'assign_user_mapping',
|
|
|
9651 |
array('assignment' => $assignid, 'userid' => $user->id),
|
|
|
9652 |
'id'
|
|
|
9653 |
);
|
1 |
efrain |
9654 |
if (!$record) {
|
|
|
9655 |
$record = new stdClass();
|
|
|
9656 |
$record->assignment = $assignid;
|
|
|
9657 |
$record->userid = $user->id;
|
|
|
9658 |
$DB->insert_record('assign_user_mapping', $record);
|
|
|
9659 |
}
|
|
|
9660 |
}
|
|
|
9661 |
}
|
|
|
9662 |
|
|
|
9663 |
/**
|
|
|
9664 |
* Lookup this user id and return the unique id for this assignment.
|
|
|
9665 |
*
|
|
|
9666 |
* @param int $assignid The assignment id
|
|
|
9667 |
* @param int $userid The userid to lookup
|
|
|
9668 |
* @return int The unique id
|
|
|
9669 |
*/
|
1254 |
ariadna |
9670 |
public static function get_uniqueid_for_user_static($assignid, $userid)
|
|
|
9671 |
{
|
1 |
efrain |
9672 |
global $DB;
|
|
|
9673 |
|
|
|
9674 |
// Search for a record.
|
1254 |
ariadna |
9675 |
$params = array('assignment' => $assignid, 'userid' => $userid);
|
1 |
efrain |
9676 |
if ($record = $DB->get_record('assign_user_mapping', $params, 'id')) {
|
|
|
9677 |
return $record->id;
|
|
|
9678 |
}
|
|
|
9679 |
|
|
|
9680 |
// Be a little smart about this - there is no record for the current user.
|
|
|
9681 |
// We should ensure any unallocated ids for the current participant
|
|
|
9682 |
// list are distrubited randomly.
|
|
|
9683 |
self::allocate_unique_ids($assignid);
|
|
|
9684 |
|
|
|
9685 |
// Retry the search for a record.
|
|
|
9686 |
if ($record = $DB->get_record('assign_user_mapping', $params, 'id')) {
|
|
|
9687 |
return $record->id;
|
|
|
9688 |
}
|
|
|
9689 |
|
|
|
9690 |
// The requested user must not be a participant. Add a record anyway.
|
|
|
9691 |
$record = new stdClass();
|
|
|
9692 |
$record->assignment = $assignid;
|
|
|
9693 |
$record->userid = $userid;
|
|
|
9694 |
|
|
|
9695 |
return $DB->insert_record('assign_user_mapping', $record);
|
|
|
9696 |
}
|
|
|
9697 |
|
|
|
9698 |
/**
|
|
|
9699 |
* Call the static version of this function.
|
|
|
9700 |
*
|
|
|
9701 |
* @param int $uniqueid The uniqueid to lookup
|
|
|
9702 |
* @return int The user id or false if they don't exist
|
|
|
9703 |
*/
|
1254 |
ariadna |
9704 |
public function get_user_id_for_uniqueid($uniqueid)
|
|
|
9705 |
{
|
1 |
efrain |
9706 |
return self::get_user_id_for_uniqueid_static($this->get_instance()->id, $uniqueid);
|
|
|
9707 |
}
|
|
|
9708 |
|
|
|
9709 |
/**
|
|
|
9710 |
* Lookup this unique id and return the user id for this assignment.
|
|
|
9711 |
*
|
|
|
9712 |
* @param int $assignid The id of the assignment this user mapping is in
|
|
|
9713 |
* @param int $uniqueid The uniqueid to lookup
|
|
|
9714 |
* @return int The user id or false if they don't exist
|
|
|
9715 |
*/
|
1254 |
ariadna |
9716 |
public static function get_user_id_for_uniqueid_static($assignid, $uniqueid)
|
|
|
9717 |
{
|
1 |
efrain |
9718 |
global $DB;
|
|
|
9719 |
|
|
|
9720 |
// Search for a record.
|
1254 |
ariadna |
9721 |
if ($record = $DB->get_record(
|
|
|
9722 |
'assign_user_mapping',
|
|
|
9723 |
array('assignment' => $assignid, 'id' => $uniqueid),
|
|
|
9724 |
'userid',
|
|
|
9725 |
IGNORE_MISSING
|
|
|
9726 |
)) {
|
1 |
efrain |
9727 |
return $record->userid;
|
|
|
9728 |
}
|
|
|
9729 |
|
|
|
9730 |
return false;
|
|
|
9731 |
}
|
|
|
9732 |
|
|
|
9733 |
/**
|
|
|
9734 |
* Get the list of marking_workflow states the current user has permission to transition a grade to.
|
|
|
9735 |
*
|
|
|
9736 |
* @return array of state => description
|
|
|
9737 |
*/
|
1254 |
ariadna |
9738 |
public function get_marking_workflow_states_for_current_user()
|
|
|
9739 |
{
|
1 |
efrain |
9740 |
if (!empty($this->markingworkflowstates)) {
|
|
|
9741 |
return $this->markingworkflowstates;
|
|
|
9742 |
}
|
|
|
9743 |
$states = array();
|
|
|
9744 |
if (has_capability('mod/assign:grade', $this->context)) {
|
|
|
9745 |
$states[ASSIGN_MARKING_WORKFLOW_STATE_INMARKING] = get_string('markingworkflowstateinmarking', 'assign');
|
|
|
9746 |
$states[ASSIGN_MARKING_WORKFLOW_STATE_READYFORREVIEW] = get_string('markingworkflowstatereadyforreview', 'assign');
|
|
|
9747 |
}
|
1254 |
ariadna |
9748 |
if (has_any_capability(array(
|
|
|
9749 |
'mod/assign:reviewgrades',
|
|
|
9750 |
'mod/assign:managegrades'
|
|
|
9751 |
), $this->context)) {
|
1 |
efrain |
9752 |
$states[ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW] = get_string('markingworkflowstateinreview', 'assign');
|
|
|
9753 |
$states[ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE] = get_string('markingworkflowstatereadyforrelease', 'assign');
|
|
|
9754 |
}
|
1254 |
ariadna |
9755 |
if (has_any_capability(array(
|
|
|
9756 |
'mod/assign:releasegrades',
|
|
|
9757 |
'mod/assign:managegrades'
|
|
|
9758 |
), $this->context)) {
|
1 |
efrain |
9759 |
$states[ASSIGN_MARKING_WORKFLOW_STATE_RELEASED] = get_string('markingworkflowstatereleased', 'assign');
|
|
|
9760 |
}
|
|
|
9761 |
$this->markingworkflowstates = $states;
|
|
|
9762 |
return $this->markingworkflowstates;
|
|
|
9763 |
}
|
|
|
9764 |
|
|
|
9765 |
/**
|
|
|
9766 |
* Get the list of marking_workflow states.
|
|
|
9767 |
*
|
|
|
9768 |
* @return array Array of multiple state => description.
|
|
|
9769 |
*/
|
1254 |
ariadna |
9770 |
public function get_all_marking_workflow_states(): array
|
|
|
9771 |
{
|
1 |
efrain |
9772 |
if (!empty($this->allmarkingworkflowstates)) {
|
|
|
9773 |
return $this->allmarkingworkflowstates;
|
|
|
9774 |
}
|
|
|
9775 |
|
|
|
9776 |
$this->allmarkingworkflowstates = [
|
|
|
9777 |
ASSIGN_MARKING_WORKFLOW_STATE_NOTMARKED => get_string('markingworkflowstatenotmarked', 'assign'),
|
|
|
9778 |
ASSIGN_MARKING_WORKFLOW_STATE_INMARKING => get_string('markingworkflowstateinmarking', 'assign'),
|
|
|
9779 |
ASSIGN_MARKING_WORKFLOW_STATE_READYFORREVIEW => get_string('markingworkflowstatereadyforreview', 'assign'),
|
|
|
9780 |
ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW => get_string('markingworkflowstateinreview', 'assign'),
|
|
|
9781 |
ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE => get_string('markingworkflowstatereadyforrelease', 'assign'),
|
|
|
9782 |
ASSIGN_MARKING_WORKFLOW_STATE_RELEASED => get_string('markingworkflowstatereleased', 'assign'),
|
|
|
9783 |
];
|
|
|
9784 |
|
|
|
9785 |
return $this->allmarkingworkflowstates;
|
|
|
9786 |
}
|
|
|
9787 |
|
|
|
9788 |
/**
|
|
|
9789 |
* Check is only active users in course should be shown.
|
|
|
9790 |
*
|
|
|
9791 |
* @return bool true if only active users should be shown.
|
|
|
9792 |
*/
|
1254 |
ariadna |
9793 |
public function show_only_active_users()
|
|
|
9794 |
{
|
1 |
efrain |
9795 |
global $CFG;
|
|
|
9796 |
|
|
|
9797 |
if (is_null($this->showonlyactiveenrol)) {
|
|
|
9798 |
$defaultgradeshowactiveenrol = !empty($CFG->grade_report_showonlyactiveenrol);
|
|
|
9799 |
$this->showonlyactiveenrol = get_user_preferences('grade_report_showonlyactiveenrol', $defaultgradeshowactiveenrol);
|
|
|
9800 |
|
|
|
9801 |
if (!is_null($this->context)) {
|
|
|
9802 |
$this->showonlyactiveenrol = $this->showonlyactiveenrol ||
|
1254 |
ariadna |
9803 |
!has_capability('moodle/course:viewsuspendedusers', $this->context);
|
1 |
efrain |
9804 |
}
|
|
|
9805 |
}
|
|
|
9806 |
return $this->showonlyactiveenrol;
|
|
|
9807 |
}
|
|
|
9808 |
|
|
|
9809 |
/**
|
|
|
9810 |
* Return true is user is active user in course else false
|
|
|
9811 |
*
|
|
|
9812 |
* @param int $userid
|
|
|
9813 |
* @return bool true is user is active in course.
|
|
|
9814 |
*/
|
1254 |
ariadna |
9815 |
public function is_active_user($userid)
|
|
|
9816 |
{
|
1 |
efrain |
9817 |
return !in_array($userid, get_suspended_userids($this->context, true));
|
|
|
9818 |
}
|
|
|
9819 |
|
|
|
9820 |
/**
|
|
|
9821 |
* Returns true if gradebook feedback plugin is enabled
|
|
|
9822 |
*
|
|
|
9823 |
* @return bool true if gradebook feedback plugin is enabled and visible else false.
|
|
|
9824 |
*/
|
1254 |
ariadna |
9825 |
public function is_gradebook_feedback_enabled()
|
|
|
9826 |
{
|
1 |
efrain |
9827 |
// Get default grade book feedback plugin.
|
|
|
9828 |
$adminconfig = $this->get_admin_config();
|
|
|
9829 |
$gradebookplugin = $adminconfig->feedback_plugin_for_gradebook;
|
|
|
9830 |
$gradebookplugin = str_replace('assignfeedback_', '', $gradebookplugin);
|
|
|
9831 |
|
|
|
9832 |
// Check if default gradebook feedback is visible and enabled.
|
|
|
9833 |
$gradebookfeedbackplugin = $this->get_feedback_plugin_by_type($gradebookplugin);
|
|
|
9834 |
|
|
|
9835 |
if (empty($gradebookfeedbackplugin)) {
|
|
|
9836 |
return false;
|
|
|
9837 |
}
|
|
|
9838 |
|
|
|
9839 |
if ($gradebookfeedbackplugin->is_visible() && $gradebookfeedbackplugin->is_enabled()) {
|
|
|
9840 |
return true;
|
|
|
9841 |
}
|
|
|
9842 |
|
|
|
9843 |
// Gradebook feedback plugin is either not visible/enabled.
|
|
|
9844 |
return false;
|
|
|
9845 |
}
|
|
|
9846 |
|
|
|
9847 |
/**
|
|
|
9848 |
* Returns the grading status.
|
|
|
9849 |
*
|
|
|
9850 |
* @param int $userid the user id
|
|
|
9851 |
* @return string returns the grading status
|
|
|
9852 |
*/
|
1254 |
ariadna |
9853 |
public function get_grading_status($userid)
|
|
|
9854 |
{
|
1 |
efrain |
9855 |
if ($this->get_instance()->markingworkflow) {
|
|
|
9856 |
$flags = $this->get_user_flags($userid, false);
|
|
|
9857 |
if (!empty($flags->workflowstate)) {
|
|
|
9858 |
return $flags->workflowstate;
|
|
|
9859 |
}
|
|
|
9860 |
return ASSIGN_MARKING_WORKFLOW_STATE_NOTMARKED;
|
|
|
9861 |
} else {
|
|
|
9862 |
$attemptnumber = optional_param('attemptnumber', -1, PARAM_INT);
|
|
|
9863 |
$grade = $this->get_user_grade($userid, false, $attemptnumber);
|
|
|
9864 |
|
|
|
9865 |
if (!empty($grade) && $grade->grade !== null && $grade->grade >= 0) {
|
|
|
9866 |
return ASSIGN_GRADING_STATUS_GRADED;
|
|
|
9867 |
} else {
|
|
|
9868 |
return ASSIGN_GRADING_STATUS_NOT_GRADED;
|
|
|
9869 |
}
|
|
|
9870 |
}
|
|
|
9871 |
}
|
|
|
9872 |
|
|
|
9873 |
/**
|
|
|
9874 |
* The id used to uniquily identify the cache for this instance of the assign object.
|
|
|
9875 |
*
|
|
|
9876 |
* @return string
|
|
|
9877 |
*/
|
1254 |
ariadna |
9878 |
public function get_useridlist_key_id()
|
|
|
9879 |
{
|
1 |
efrain |
9880 |
return $this->useridlistid;
|
|
|
9881 |
}
|
|
|
9882 |
|
|
|
9883 |
/**
|
|
|
9884 |
* Generates the key that should be used for an entry in the useridlist cache.
|
|
|
9885 |
*
|
|
|
9886 |
* @param string $id Generate a key for this instance (optional)
|
|
|
9887 |
* @return string The key for the id, or new entry if no $id is passed.
|
|
|
9888 |
*/
|
1254 |
ariadna |
9889 |
public function get_useridlist_key($id = null)
|
|
|
9890 |
{
|
1 |
efrain |
9891 |
global $SESSION;
|
|
|
9892 |
|
|
|
9893 |
// Ensure the user id list cache is initialised.
|
|
|
9894 |
if (!isset($SESSION->mod_assign_useridlist)) {
|
|
|
9895 |
$SESSION->mod_assign_useridlist = [];
|
|
|
9896 |
}
|
|
|
9897 |
|
|
|
9898 |
if ($id === null) {
|
|
|
9899 |
$id = $this->get_useridlist_key_id();
|
|
|
9900 |
}
|
|
|
9901 |
return $this->get_course_module()->id . '_' . $id;
|
|
|
9902 |
}
|
|
|
9903 |
|
|
|
9904 |
/**
|
|
|
9905 |
* Updates and creates the completion records in mdl_course_modules_completion.
|
|
|
9906 |
*
|
|
|
9907 |
* @param int $teamsubmission value of 0 or 1 to indicate whether this is a group activity
|
|
|
9908 |
* @param int $requireallteammemberssubmit value of 0 or 1 to indicate whether all group members must click Submit
|
|
|
9909 |
* @param obj $submission the submission
|
|
|
9910 |
* @param int $userid the user id
|
|
|
9911 |
* @param int $complete
|
|
|
9912 |
* @param obj $completion
|
|
|
9913 |
*
|
|
|
9914 |
* @return null
|
|
|
9915 |
*/
|
1254 |
ariadna |
9916 |
protected function update_activity_completion_records(
|
|
|
9917 |
$teamsubmission,
|
|
|
9918 |
$requireallteammemberssubmit,
|
|
|
9919 |
$submission,
|
|
|
9920 |
$userid,
|
|
|
9921 |
$complete,
|
|
|
9922 |
$completion
|
|
|
9923 |
) {
|
1 |
efrain |
9924 |
|
|
|
9925 |
if (($teamsubmission && $submission->groupid > 0 && !$requireallteammemberssubmit) ||
|
|
|
9926 |
($teamsubmission && $submission->groupid > 0 && $requireallteammemberssubmit &&
|
1254 |
ariadna |
9927 |
$submission->status == ASSIGN_SUBMISSION_STATUS_SUBMITTED)
|
|
|
9928 |
) {
|
1 |
efrain |
9929 |
|
|
|
9930 |
$members = groups_get_members($submission->groupid);
|
|
|
9931 |
|
|
|
9932 |
foreach ($members as $member) {
|
|
|
9933 |
$completion->update_state($this->get_course_module(), $complete, $member->id);
|
|
|
9934 |
}
|
|
|
9935 |
} else {
|
|
|
9936 |
$completion->update_state($this->get_course_module(), $complete, $userid);
|
|
|
9937 |
}
|
|
|
9938 |
|
|
|
9939 |
return;
|
|
|
9940 |
}
|
|
|
9941 |
|
|
|
9942 |
/**
|
|
|
9943 |
* Update the module completion status (set it viewed) and trigger module viewed event.
|
|
|
9944 |
*
|
|
|
9945 |
* @since Moodle 3.2
|
|
|
9946 |
*/
|
1254 |
ariadna |
9947 |
public function set_module_viewed()
|
|
|
9948 |
{
|
1 |
efrain |
9949 |
$completion = new completion_info($this->get_course());
|
|
|
9950 |
$completion->set_module_viewed($this->get_course_module());
|
|
|
9951 |
|
|
|
9952 |
// Trigger the course module viewed event.
|
|
|
9953 |
$assigninstance = $this->get_instance();
|
|
|
9954 |
$params = [
|
|
|
9955 |
'objectid' => $assigninstance->id,
|
|
|
9956 |
'context' => $this->get_context()
|
|
|
9957 |
];
|
|
|
9958 |
if ($this->is_blind_marking()) {
|
|
|
9959 |
$params['anonymous'] = 1;
|
|
|
9960 |
}
|
|
|
9961 |
|
|
|
9962 |
$event = \mod_assign\event\course_module_viewed::create($params);
|
|
|
9963 |
|
|
|
9964 |
$event->add_record_snapshot('assign', $assigninstance);
|
|
|
9965 |
$event->trigger();
|
|
|
9966 |
}
|
|
|
9967 |
|
|
|
9968 |
/**
|
|
|
9969 |
* Checks for any grade notices, and adds notifications. Will display on assignment main page and grading table.
|
|
|
9970 |
*
|
|
|
9971 |
* @return void The notifications API will render the notifications at the appropriate part of the page.
|
|
|
9972 |
*/
|
1254 |
ariadna |
9973 |
protected function add_grade_notices()
|
|
|
9974 |
{
|
1 |
efrain |
9975 |
if (has_capability('mod/assign:grade', $this->get_context()) && get_config('assign', 'has_rescaled_null_grades_' . $this->get_instance()->id)) {
|
|
|
9976 |
$link = new \moodle_url('/mod/assign/view.php', array('id' => $this->get_course_module()->id, 'action' => 'fixrescalednullgrades'));
|
|
|
9977 |
\core\notification::warning(get_string('fixrescalednullgrades', 'mod_assign', ['link' => $link->out()]));
|
|
|
9978 |
}
|
|
|
9979 |
}
|
|
|
9980 |
|
|
|
9981 |
/**
|
|
|
9982 |
* View fix rescaled null grades.
|
|
|
9983 |
*
|
|
|
9984 |
* @return bool True if null all grades are now fixed.
|
|
|
9985 |
*/
|
1254 |
ariadna |
9986 |
protected function fix_null_grades()
|
|
|
9987 |
{
|
1 |
efrain |
9988 |
global $DB;
|
|
|
9989 |
$result = $DB->set_field_select(
|
|
|
9990 |
'assign_grades',
|
|
|
9991 |
'grade',
|
|
|
9992 |
ASSIGN_GRADE_NOT_SET,
|
|
|
9993 |
'grade <> ? AND grade < 0',
|
|
|
9994 |
[ASSIGN_GRADE_NOT_SET]
|
|
|
9995 |
);
|
|
|
9996 |
$assign = clone $this->get_instance();
|
|
|
9997 |
$assign->cmidnumber = $this->get_course_module()->idnumber;
|
|
|
9998 |
assign_update_grades($assign);
|
|
|
9999 |
return $result;
|
|
|
10000 |
}
|
|
|
10001 |
|
|
|
10002 |
/**
|
|
|
10003 |
* View fix rescaled null grades.
|
|
|
10004 |
*
|
|
|
10005 |
* @return void The notifications API will render the notifications at the appropriate part of the page.
|
|
|
10006 |
*/
|
1254 |
ariadna |
10007 |
protected function view_fix_rescaled_null_grades()
|
|
|
10008 |
{
|
1 |
efrain |
10009 |
global $OUTPUT;
|
|
|
10010 |
|
|
|
10011 |
$o = '';
|
|
|
10012 |
|
|
|
10013 |
require_capability('mod/assign:grade', $this->get_context());
|
|
|
10014 |
|
|
|
10015 |
$instance = $this->get_instance();
|
|
|
10016 |
|
|
|
10017 |
$o .= $this->get_renderer()->render(
|
|
|
10018 |
new assign_header(
|
|
|
10019 |
$instance,
|
|
|
10020 |
$this->get_context(),
|
|
|
10021 |
$this->show_intro(),
|
|
|
10022 |
$this->get_course_module()->id
|
|
|
10023 |
)
|
|
|
10024 |
);
|
|
|
10025 |
|
|
|
10026 |
$confirm = optional_param('confirm', 0, PARAM_BOOL);
|
|
|
10027 |
|
|
|
10028 |
if ($confirm) {
|
|
|
10029 |
if (confirm_sesskey()) {
|
|
|
10030 |
// Fix the grades.
|
|
|
10031 |
$this->fix_null_grades();
|
|
|
10032 |
unset_config('has_rescaled_null_grades_' . $instance->id, 'assign');
|
|
|
10033 |
// Display the success notice.
|
|
|
10034 |
$o .= $this->get_renderer()->notification(get_string('fixrescalednullgradesdone', 'assign'), 'notifysuccess');
|
|
|
10035 |
} else {
|
|
|
10036 |
// If the sesskey is not valid, then display the error notice.
|
|
|
10037 |
$o .= $this->get_renderer()->notification(get_string('invalidsesskey', 'error'), 'notifyerror');
|
|
|
10038 |
}
|
|
|
10039 |
$url = new moodle_url(
|
|
|
10040 |
url: '/mod/assign/view.php',
|
|
|
10041 |
params: [
|
|
|
10042 |
'id' => $this->get_course_module()->id,
|
|
|
10043 |
'action' => 'grading',
|
|
|
10044 |
],
|
|
|
10045 |
);
|
|
|
10046 |
$o .= $this->get_renderer()->continue_button($url);
|
|
|
10047 |
} else {
|
|
|
10048 |
// Ask for confirmation.
|
|
|
10049 |
$continue = new \moodle_url('/mod/assign/view.php', array('id' => $this->get_course_module()->id, 'action' => 'fixrescalednullgrades', 'confirm' => true, 'sesskey' => sesskey()));
|
|
|
10050 |
$cancel = new \moodle_url('/mod/assign/view.php', array('id' => $this->get_course_module()->id));
|
|
|
10051 |
$o .= $OUTPUT->confirm(get_string('fixrescalednullgradesconfirm', 'mod_assign'), $continue, $cancel);
|
|
|
10052 |
}
|
|
|
10053 |
|
|
|
10054 |
$o .= $this->view_footer();
|
|
|
10055 |
|
|
|
10056 |
return $o;
|
|
|
10057 |
}
|
|
|
10058 |
|
|
|
10059 |
/**
|
|
|
10060 |
* Set the most recent submission for the team.
|
|
|
10061 |
* The most recent team submission is used to determine if another attempt should be created when allowing another
|
|
|
10062 |
* attempt on a group assignment, and whether the gradebook should be updated.
|
|
|
10063 |
*
|
|
|
10064 |
* @since Moodle 3.4
|
|
|
10065 |
* @param stdClass $submission The most recent submission of the group.
|
|
|
10066 |
*/
|
1254 |
ariadna |
10067 |
public function set_most_recent_team_submission($submission)
|
|
|
10068 |
{
|
1 |
efrain |
10069 |
$this->mostrecentteamsubmission = $submission;
|
|
|
10070 |
}
|
|
|
10071 |
|
|
|
10072 |
/**
|
|
|
10073 |
* Return array of valid grading allocation filters for the grading interface.
|
|
|
10074 |
*
|
|
|
10075 |
* @param boolean $export Export the list of filters for a template.
|
|
|
10076 |
* @return array
|
|
|
10077 |
*/
|
1254 |
ariadna |
10078 |
public function get_marking_allocation_filters($export = false)
|
|
|
10079 |
{
|
1 |
efrain |
10080 |
$markingallocation = $this->get_instance()->markingworkflow &&
|
|
|
10081 |
$this->get_instance()->markingallocation &&
|
|
|
10082 |
has_capability('mod/assign:manageallocations', $this->context);
|
|
|
10083 |
// Get markers to use in drop lists.
|
|
|
10084 |
$markingallocationoptions = array();
|
|
|
10085 |
if ($markingallocation) {
|
|
|
10086 |
list($sort, $params) = users_order_by_sql('u');
|
|
|
10087 |
// Only enrolled users could be assigned as potential markers.
|
|
|
10088 |
$markers = get_enrolled_users($this->context, 'mod/assign:grade', 0, 'u.*', $sort);
|
|
|
10089 |
$markingallocationoptions[''] = get_string('filternone', 'assign');
|
|
|
10090 |
$markingallocationoptions[ASSIGN_MARKER_FILTER_NO_MARKER] = get_string('markerfilternomarker', 'assign');
|
|
|
10091 |
$viewfullnames = has_capability('moodle/site:viewfullnames', $this->context);
|
|
|
10092 |
foreach ($markers as $marker) {
|
|
|
10093 |
$markingallocationoptions[$marker->id] = fullname($marker, $viewfullnames);
|
|
|
10094 |
}
|
|
|
10095 |
}
|
|
|
10096 |
if ($export) {
|
|
|
10097 |
$allocationfilter = get_user_preferences('assign_markerfilter', '');
|
|
|
10098 |
$result = [];
|
|
|
10099 |
foreach ($markingallocationoptions as $option => $label) {
|
|
|
10100 |
array_push($result, [
|
|
|
10101 |
'key' => $option,
|
|
|
10102 |
'name' => $label,
|
|
|
10103 |
'active' => ($allocationfilter == $option),
|
|
|
10104 |
]);
|
|
|
10105 |
}
|
|
|
10106 |
return $result;
|
|
|
10107 |
}
|
|
|
10108 |
return $markingworkflowoptions;
|
|
|
10109 |
}
|
|
|
10110 |
|
|
|
10111 |
/**
|
|
|
10112 |
* Return array of valid grading workflow filters for the grading interface.
|
|
|
10113 |
*
|
|
|
10114 |
* @param boolean $export Export the list of filters for a template.
|
|
|
10115 |
* @return array
|
|
|
10116 |
*/
|
1254 |
ariadna |
10117 |
public function get_marking_workflow_filters($export = false)
|
|
|
10118 |
{
|
1 |
efrain |
10119 |
$markingworkflow = $this->get_instance()->markingworkflow;
|
|
|
10120 |
// Get marking states to show in form.
|
|
|
10121 |
$markingworkflowoptions = array();
|
|
|
10122 |
if ($markingworkflow) {
|
|
|
10123 |
$notmarked = get_string('markingworkflowstatenotmarked', 'assign');
|
|
|
10124 |
$markingworkflowoptions[''] = get_string('filternone', 'assign');
|
|
|
10125 |
$markingworkflowoptions[ASSIGN_MARKING_WORKFLOW_STATE_NOTMARKED] = $notmarked;
|
|
|
10126 |
$markingworkflowoptions = array_merge($markingworkflowoptions, $this->get_marking_workflow_states_for_current_user());
|
|
|
10127 |
}
|
|
|
10128 |
if ($export) {
|
|
|
10129 |
$workflowfilter = get_user_preferences('assign_workflowfilter', '');
|
|
|
10130 |
$result = [];
|
|
|
10131 |
foreach ($markingworkflowoptions as $option => $label) {
|
|
|
10132 |
array_push($result, [
|
|
|
10133 |
'key' => $option,
|
|
|
10134 |
'name' => $label,
|
|
|
10135 |
'active' => ($workflowfilter == $option),
|
|
|
10136 |
]);
|
|
|
10137 |
}
|
|
|
10138 |
return $result;
|
|
|
10139 |
}
|
|
|
10140 |
return $markingworkflowoptions;
|
|
|
10141 |
}
|
|
|
10142 |
|
|
|
10143 |
/**
|
|
|
10144 |
* Return array of valid search filters for the grading interface.
|
|
|
10145 |
*
|
|
|
10146 |
* @return array
|
|
|
10147 |
*/
|
1254 |
ariadna |
10148 |
public function get_filters()
|
|
|
10149 |
{
|
1 |
efrain |
10150 |
$filterkeys = [
|
|
|
10151 |
ASSIGN_FILTER_NOT_SUBMITTED,
|
|
|
10152 |
ASSIGN_FILTER_DRAFT,
|
|
|
10153 |
ASSIGN_FILTER_SUBMITTED,
|
|
|
10154 |
ASSIGN_FILTER_REQUIRE_GRADING,
|
|
|
10155 |
ASSIGN_FILTER_GRANTED_EXTENSION
|
|
|
10156 |
];
|
|
|
10157 |
|
|
|
10158 |
$current = get_user_preferences('assign_filter', '');
|
|
|
10159 |
|
|
|
10160 |
$filters = [];
|
|
|
10161 |
// First is always "no filter" option.
|
|
|
10162 |
array_push($filters, [
|
|
|
10163 |
'key' => 'none',
|
|
|
10164 |
'name' => get_string('filternone', 'assign'),
|
|
|
10165 |
'active' => ($current == '')
|
|
|
10166 |
]);
|
|
|
10167 |
|
|
|
10168 |
foreach ($filterkeys as $key) {
|
|
|
10169 |
array_push($filters, [
|
|
|
10170 |
'key' => $key,
|
|
|
10171 |
'name' => get_string('filter' . $key, 'assign'),
|
|
|
10172 |
'active' => ($current == $key)
|
|
|
10173 |
]);
|
|
|
10174 |
}
|
|
|
10175 |
return $filters;
|
|
|
10176 |
}
|
|
|
10177 |
|
|
|
10178 |
/**
|
|
|
10179 |
* Get the correct submission statement depending on single submisison, team submission or team submission
|
|
|
10180 |
* where all team memebers must submit.
|
|
|
10181 |
*
|
|
|
10182 |
* @param stdClass $adminconfig
|
|
|
10183 |
* @param stdClass $instance
|
|
|
10184 |
* @param context $context
|
|
|
10185 |
*
|
|
|
10186 |
* @return string
|
|
|
10187 |
*/
|
1254 |
ariadna |
10188 |
protected function get_submissionstatement($adminconfig, $instance, $context)
|
|
|
10189 |
{
|
1 |
efrain |
10190 |
$submissionstatement = '';
|
|
|
10191 |
|
|
|
10192 |
if (!($context instanceof context)) {
|
|
|
10193 |
return $submissionstatement;
|
|
|
10194 |
}
|
|
|
10195 |
|
|
|
10196 |
// Single submission.
|
|
|
10197 |
if (!$instance->teamsubmission) {
|
|
|
10198 |
// Single submission statement is not empty.
|
|
|
10199 |
if (!empty($adminconfig->submissionstatement)) {
|
|
|
10200 |
// Format the submission statement before its sent. We turn off para because this is going within
|
|
|
10201 |
// a form element.
|
|
|
10202 |
$options = array(
|
|
|
10203 |
'context' => $context,
|
|
|
10204 |
'para' => false
|
|
|
10205 |
);
|
|
|
10206 |
$submissionstatement = format_text($adminconfig->submissionstatement, FORMAT_MOODLE, $options);
|
|
|
10207 |
}
|
|
|
10208 |
} else { // Team submission.
|
|
|
10209 |
// One user can submit for the whole team.
|
|
|
10210 |
if (!empty($adminconfig->submissionstatementteamsubmission) && !$instance->requireallteammemberssubmit) {
|
|
|
10211 |
// Format the submission statement before its sent. We turn off para because this is going within
|
|
|
10212 |
// a form element.
|
|
|
10213 |
$options = array(
|
|
|
10214 |
'context' => $context,
|
|
|
10215 |
'para' => false
|
|
|
10216 |
);
|
1254 |
ariadna |
10217 |
$submissionstatement = format_text(
|
|
|
10218 |
$adminconfig->submissionstatementteamsubmission,
|
|
|
10219 |
FORMAT_MOODLE,
|
|
|
10220 |
$options
|
|
|
10221 |
);
|
|
|
10222 |
} else if (
|
|
|
10223 |
!empty($adminconfig->submissionstatementteamsubmissionallsubmit) &&
|
|
|
10224 |
$instance->requireallteammemberssubmit
|
|
|
10225 |
) {
|
1 |
efrain |
10226 |
// All team members must submit.
|
|
|
10227 |
// Format the submission statement before its sent. We turn off para because this is going within
|
|
|
10228 |
// a form element.
|
|
|
10229 |
$options = array(
|
|
|
10230 |
'context' => $context,
|
|
|
10231 |
'para' => false
|
|
|
10232 |
);
|
1254 |
ariadna |
10233 |
$submissionstatement = format_text(
|
|
|
10234 |
$adminconfig->submissionstatementteamsubmissionallsubmit,
|
|
|
10235 |
FORMAT_MOODLE,
|
|
|
10236 |
$options
|
|
|
10237 |
);
|
1 |
efrain |
10238 |
}
|
|
|
10239 |
}
|
|
|
10240 |
|
|
|
10241 |
return $submissionstatement;
|
|
|
10242 |
}
|
|
|
10243 |
|
|
|
10244 |
/**
|
|
|
10245 |
* Check if time limit for assignment enabled and set up.
|
|
|
10246 |
*
|
|
|
10247 |
* @param int|null $userid User ID. If null, use global user.
|
|
|
10248 |
* @return bool
|
|
|
10249 |
*/
|
1254 |
ariadna |
10250 |
public function is_time_limit_enabled(?int $userid = null): bool
|
|
|
10251 |
{
|
1 |
efrain |
10252 |
$instance = $this->get_instance($userid);
|
|
|
10253 |
return get_config('assign', 'enabletimelimit') && !empty($instance->timelimit);
|
|
|
10254 |
}
|
|
|
10255 |
|
|
|
10256 |
/**
|
|
|
10257 |
* Check if an assignment submission is already started and not yet submitted.
|
|
|
10258 |
*
|
|
|
10259 |
* @param int|null $userid User ID. If null, use global user.
|
|
|
10260 |
* @param int $groupid Group ID. If 0, use user id to determine group.
|
|
|
10261 |
* @param int $attemptnumber Attempt number. If -1, check latest submission.
|
|
|
10262 |
* @return bool
|
|
|
10263 |
*/
|
1254 |
ariadna |
10264 |
public function is_attempt_in_progress(?int $userid = null, int $groupid = 0, int $attemptnumber = -1): bool
|
|
|
10265 |
{
|
1 |
efrain |
10266 |
if ($this->get_instance($userid)->teamsubmission) {
|
|
|
10267 |
$submission = $this->get_group_submission($userid, $groupid, false, $attemptnumber);
|
|
|
10268 |
} else {
|
|
|
10269 |
$submission = $this->get_user_submission($userid, false, $attemptnumber);
|
|
|
10270 |
}
|
|
|
10271 |
|
|
|
10272 |
// If time limit is enabled, we only assume it is in progress if there is a start time for submission.
|
|
|
10273 |
$timedattemptstarted = true;
|
|
|
10274 |
if ($this->is_time_limit_enabled($userid)) {
|
|
|
10275 |
$timedattemptstarted = !empty($submission) && !empty($submission->timestarted);
|
|
|
10276 |
}
|
|
|
10277 |
|
|
|
10278 |
return !empty($submission) && $submission->status !== ASSIGN_SUBMISSION_STATUS_SUBMITTED && $timedattemptstarted;
|
|
|
10279 |
}
|
|
|
10280 |
}
|
|
|
10281 |
|
|
|
10282 |
/**
|
|
|
10283 |
* Portfolio caller class for mod_assign.
|
|
|
10284 |
*
|
|
|
10285 |
* @package mod_assign
|
|
|
10286 |
* @copyright 2012 NetSpot {@link http://www.netspot.com.au}
|
|
|
10287 |
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
10288 |
*/
|
1254 |
ariadna |
10289 |
class assign_portfolio_caller extends portfolio_module_caller_base
|
|
|
10290 |
{
|
1 |
efrain |
10291 |
|
|
|
10292 |
/** @var int callback arg - the id of submission we export */
|
|
|
10293 |
protected $sid;
|
|
|
10294 |
|
|
|
10295 |
/** @var string component of the submission files we export*/
|
|
|
10296 |
protected $component;
|
|
|
10297 |
|
|
|
10298 |
/** @var string callback arg - the area of submission files we export */
|
|
|
10299 |
protected $area;
|
|
|
10300 |
|
|
|
10301 |
/** @var int callback arg - the id of file we export */
|
|
|
10302 |
protected $fileid;
|
|
|
10303 |
|
|
|
10304 |
/** @var int callback arg - the cmid of the assignment we export */
|
|
|
10305 |
protected $cmid;
|
|
|
10306 |
|
|
|
10307 |
/** @var string callback arg - the plugintype of the editor we export */
|
|
|
10308 |
protected $plugin;
|
|
|
10309 |
|
|
|
10310 |
/** @var string callback arg - the name of the editor field we export */
|
|
|
10311 |
protected $editor;
|
|
|
10312 |
|
|
|
10313 |
/**
|
|
|
10314 |
* Callback arg for a single file export.
|
|
|
10315 |
*/
|
1254 |
ariadna |
10316 |
public static function expected_callbackargs()
|
|
|
10317 |
{
|
1 |
efrain |
10318 |
return array(
|
|
|
10319 |
'cmid' => true,
|
|
|
10320 |
'sid' => false,
|
|
|
10321 |
'area' => false,
|
|
|
10322 |
'component' => false,
|
|
|
10323 |
'fileid' => false,
|
|
|
10324 |
'plugin' => false,
|
|
|
10325 |
'editor' => false,
|
|
|
10326 |
);
|
|
|
10327 |
}
|
|
|
10328 |
|
|
|
10329 |
/**
|
|
|
10330 |
* The constructor.
|
|
|
10331 |
*
|
|
|
10332 |
* @param array $callbackargs
|
|
|
10333 |
*/
|
1254 |
ariadna |
10334 |
public function __construct($callbackargs)
|
|
|
10335 |
{
|
1 |
efrain |
10336 |
parent::__construct($callbackargs);
|
|
|
10337 |
$this->cm = get_coursemodule_from_id('assign', $this->cmid, 0, false, MUST_EXIST);
|
|
|
10338 |
}
|
|
|
10339 |
|
|
|
10340 |
/**
|
|
|
10341 |
* Load data needed for the portfolio export.
|
|
|
10342 |
*
|
|
|
10343 |
* If the assignment type implements portfolio_load_data(), the processing is delegated
|
|
|
10344 |
* to it. Otherwise, the caller must provide either fileid (to export single file) or
|
|
|
10345 |
* submissionid and filearea (to export all data attached to the given submission file area)
|
|
|
10346 |
* via callback arguments.
|
|
|
10347 |
*
|
|
|
10348 |
* @throws portfolio_caller_exception
|
|
|
10349 |
*/
|
1254 |
ariadna |
10350 |
public function load_data()
|
|
|
10351 |
{
|
1 |
efrain |
10352 |
global $DB;
|
|
|
10353 |
|
|
|
10354 |
$context = context_module::instance($this->cmid);
|
|
|
10355 |
|
|
|
10356 |
if (empty($this->fileid)) {
|
|
|
10357 |
if (empty($this->sid) || empty($this->area)) {
|
|
|
10358 |
throw new portfolio_caller_exception('invalidfileandsubmissionid', 'mod_assign');
|
|
|
10359 |
}
|
|
|
10360 |
|
|
|
10361 |
$submission = $DB->get_record('assign_submission', array('id' => $this->sid));
|
|
|
10362 |
} else {
|
|
|
10363 |
$submissionid = $DB->get_field('files', 'itemid', array('id' => $this->fileid, 'contextid' => $context->id));
|
|
|
10364 |
if ($submissionid) {
|
|
|
10365 |
$submission = $DB->get_record('assign_submission', array('id' => $submissionid));
|
|
|
10366 |
}
|
|
|
10367 |
}
|
|
|
10368 |
|
|
|
10369 |
if (empty($submission)) {
|
|
|
10370 |
throw new portfolio_caller_exception('filenotfound');
|
|
|
10371 |
} else if ($submission->userid == 0) {
|
|
|
10372 |
// This must be a group submission.
|
|
|
10373 |
if (!groups_is_member($submission->groupid, $this->user->id)) {
|
|
|
10374 |
throw new portfolio_caller_exception('filenotfound');
|
|
|
10375 |
}
|
|
|
10376 |
} else if ($this->user->id != $submission->userid) {
|
|
|
10377 |
throw new portfolio_caller_exception('filenotfound');
|
|
|
10378 |
}
|
|
|
10379 |
|
|
|
10380 |
// Export either an area of files or a single file (see function for more detail).
|
|
|
10381 |
// The first arg is an id or null. If it is an id, the rest of the args are ignored.
|
|
|
10382 |
// If it is null, the rest of the args are used to load a list of files from get_areafiles.
|
1254 |
ariadna |
10383 |
$this->set_file_and_format_data(
|
|
|
10384 |
$this->fileid,
|
|
|
10385 |
$context->id,
|
|
|
10386 |
$this->component,
|
|
|
10387 |
$this->area,
|
|
|
10388 |
$this->sid,
|
|
|
10389 |
'timemodified',
|
|
|
10390 |
false
|
|
|
10391 |
);
|
1 |
efrain |
10392 |
}
|
|
|
10393 |
|
|
|
10394 |
/**
|
|
|
10395 |
* Prepares the package up before control is passed to the portfolio plugin.
|
|
|
10396 |
*
|
|
|
10397 |
* @throws portfolio_caller_exception
|
|
|
10398 |
* @return mixed
|
|
|
10399 |
*/
|
1254 |
ariadna |
10400 |
public function prepare_package()
|
|
|
10401 |
{
|
1 |
efrain |
10402 |
|
|
|
10403 |
if ($this->plugin && $this->editor) {
|
|
|
10404 |
$options = portfolio_format_text_options();
|
|
|
10405 |
$context = context_module::instance($this->cmid);
|
|
|
10406 |
$options->context = $context;
|
|
|
10407 |
|
|
|
10408 |
$plugin = $this->get_submission_plugin();
|
|
|
10409 |
|
|
|
10410 |
$text = $plugin->get_editor_text($this->editor, $this->sid);
|
|
|
10411 |
$format = $plugin->get_editor_format($this->editor, $this->sid);
|
|
|
10412 |
|
|
|
10413 |
$html = format_text($text, $format, $options);
|
1254 |
ariadna |
10414 |
$html = portfolio_rewrite_pluginfile_urls(
|
|
|
10415 |
$html,
|
|
|
10416 |
$context->id,
|
|
|
10417 |
'mod_assign',
|
|
|
10418 |
$this->area,
|
|
|
10419 |
$this->sid,
|
|
|
10420 |
$this->exporter->get('format')
|
|
|
10421 |
);
|
1 |
efrain |
10422 |
|
|
|
10423 |
$exporterclass = $this->exporter->get('formatclass');
|
|
|
10424 |
if (in_array($exporterclass, array(PORTFOLIO_FORMAT_PLAINHTML, PORTFOLIO_FORMAT_RICHHTML))) {
|
|
|
10425 |
if ($files = $this->exporter->get('caller')->get('multifiles')) {
|
|
|
10426 |
foreach ($files as $file) {
|
|
|
10427 |
$this->exporter->copy_existing_file($file);
|
|
|
10428 |
}
|
|
|
10429 |
}
|
|
|
10430 |
return $this->exporter->write_new_file($html, 'assignment.html', !empty($files));
|
|
|
10431 |
} else if ($this->exporter->get('formatclass') == PORTFOLIO_FORMAT_LEAP2A) {
|
|
|
10432 |
$leapwriter = $this->exporter->get('format')->leap2a_writer();
|
1254 |
ariadna |
10433 |
$entry = new portfolio_format_leap2a_entry(
|
|
|
10434 |
$this->area . $this->cmid,
|
|
|
10435 |
$context->get_context_name(),
|
|
|
10436 |
'resource',
|
|
|
10437 |
$html
|
|
|
10438 |
);
|
1 |
efrain |
10439 |
|
|
|
10440 |
$entry->add_category('web', 'resource_type');
|
|
|
10441 |
$entry->author = $this->user;
|
|
|
10442 |
$leapwriter->add_entry($entry);
|
|
|
10443 |
if ($files = $this->exporter->get('caller')->get('multifiles')) {
|
|
|
10444 |
$leapwriter->link_files($entry, $files, $this->area . $this->cmid . 'file');
|
|
|
10445 |
foreach ($files as $file) {
|
|
|
10446 |
$this->exporter->copy_existing_file($file);
|
|
|
10447 |
}
|
|
|
10448 |
}
|
1254 |
ariadna |
10449 |
return $this->exporter->write_new_file(
|
|
|
10450 |
$leapwriter->to_xml(),
|
|
|
10451 |
$this->exporter->get('format')->manifest_name(),
|
|
|
10452 |
true
|
|
|
10453 |
);
|
1 |
efrain |
10454 |
} else {
|
|
|
10455 |
debugging('invalid format class: ' . $this->exporter->get('formatclass'));
|
|
|
10456 |
}
|
|
|
10457 |
}
|
|
|
10458 |
|
|
|
10459 |
if ($this->exporter->get('formatclass') == PORTFOLIO_FORMAT_LEAP2A) {
|
|
|
10460 |
$leapwriter = $this->exporter->get('format')->leap2a_writer();
|
|
|
10461 |
$files = array();
|
|
|
10462 |
if ($this->singlefile) {
|
|
|
10463 |
$files[] = $this->singlefile;
|
|
|
10464 |
} else if ($this->multifiles) {
|
|
|
10465 |
$files = $this->multifiles;
|
|
|
10466 |
} else {
|
1254 |
ariadna |
10467 |
throw new portfolio_caller_exception(
|
|
|
10468 |
'invalidpreparepackagefile',
|
|
|
10469 |
'portfolio',
|
|
|
10470 |
$this->get_return_url()
|
|
|
10471 |
);
|
1 |
efrain |
10472 |
}
|
|
|
10473 |
|
|
|
10474 |
$entryids = array();
|
|
|
10475 |
foreach ($files as $file) {
|
|
|
10476 |
$entry = new portfolio_format_leap2a_file($file->get_filename(), $file);
|
|
|
10477 |
$entry->author = $this->user;
|
|
|
10478 |
$leapwriter->add_entry($entry);
|
|
|
10479 |
$this->exporter->copy_existing_file($file);
|
|
|
10480 |
$entryids[] = $entry->id;
|
|
|
10481 |
}
|
|
|
10482 |
if (count($files) > 1) {
|
|
|
10483 |
$baseid = 'assign' . $this->cmid . $this->area;
|
|
|
10484 |
$context = context_module::instance($this->cmid);
|
|
|
10485 |
|
|
|
10486 |
// If we have multiple files, they should be grouped together into a folder.
|
1254 |
ariadna |
10487 |
$entry = new portfolio_format_leap2a_entry(
|
|
|
10488 |
$baseid . 'group',
|
|
|
10489 |
$context->get_context_name(),
|
|
|
10490 |
'selection'
|
|
|
10491 |
);
|
1 |
efrain |
10492 |
$leapwriter->add_entry($entry);
|
|
|
10493 |
$leapwriter->make_selection($entry, $entryids, 'Folder');
|
|
|
10494 |
}
|
1254 |
ariadna |
10495 |
return $this->exporter->write_new_file(
|
|
|
10496 |
$leapwriter->to_xml(),
|
|
|
10497 |
$this->exporter->get('format')->manifest_name(),
|
|
|
10498 |
true
|
|
|
10499 |
);
|
1 |
efrain |
10500 |
}
|
|
|
10501 |
return $this->prepare_package_file();
|
|
|
10502 |
}
|
|
|
10503 |
|
|
|
10504 |
/**
|
|
|
10505 |
* Fetch the plugin by its type.
|
|
|
10506 |
*
|
|
|
10507 |
* @return assign_submission_plugin
|
|
|
10508 |
*/
|
1254 |
ariadna |
10509 |
protected function get_submission_plugin()
|
|
|
10510 |
{
|
1 |
efrain |
10511 |
global $CFG;
|
|
|
10512 |
if (!$this->plugin || !$this->cmid) {
|
|
|
10513 |
return null;
|
|
|
10514 |
}
|
|
|
10515 |
|
|
|
10516 |
require_once($CFG->dirroot . '/mod/assign/locallib.php');
|
|
|
10517 |
|
|
|
10518 |
$context = context_module::instance($this->cmid);
|
|
|
10519 |
|
|
|
10520 |
$assignment = new assign($context, null, null);
|
|
|
10521 |
return $assignment->get_submission_plugin_by_type($this->plugin);
|
|
|
10522 |
}
|
|
|
10523 |
|
|
|
10524 |
/**
|
|
|
10525 |
* Calculate a sha1 has of either a single file or a list
|
|
|
10526 |
* of files based on the data set by load_data.
|
|
|
10527 |
*
|
|
|
10528 |
* @return string
|
|
|
10529 |
*/
|
1254 |
ariadna |
10530 |
public function get_sha1()
|
|
|
10531 |
{
|
1 |
efrain |
10532 |
|
|
|
10533 |
if ($this->plugin && $this->editor) {
|
|
|
10534 |
$plugin = $this->get_submission_plugin();
|
|
|
10535 |
$options = portfolio_format_text_options();
|
|
|
10536 |
$options->context = context_module::instance($this->cmid);
|
|
|
10537 |
|
1254 |
ariadna |
10538 |
$text = format_text(
|
|
|
10539 |
$plugin->get_editor_text($this->editor, $this->sid),
|
|
|
10540 |
$plugin->get_editor_format($this->editor, $this->sid),
|
|
|
10541 |
$options
|
|
|
10542 |
);
|
1 |
efrain |
10543 |
$textsha1 = sha1($text);
|
|
|
10544 |
$filesha1 = '';
|
|
|
10545 |
try {
|
|
|
10546 |
$filesha1 = $this->get_sha1_file();
|
|
|
10547 |
} catch (portfolio_caller_exception $e) {
|
|
|
10548 |
// No files.
|
|
|
10549 |
}
|
|
|
10550 |
return sha1($textsha1 . $filesha1);
|
|
|
10551 |
}
|
|
|
10552 |
return $this->get_sha1_file();
|
|
|
10553 |
}
|
|
|
10554 |
|
|
|
10555 |
/**
|
|
|
10556 |
* Calculate the time to transfer either a single file or a list
|
|
|
10557 |
* of files based on the data set by load_data.
|
|
|
10558 |
*
|
|
|
10559 |
* @return int
|
|
|
10560 |
*/
|
1254 |
ariadna |
10561 |
public function expected_time()
|
|
|
10562 |
{
|
1 |
efrain |
10563 |
return $this->expected_time_file();
|
|
|
10564 |
}
|
|
|
10565 |
|
|
|
10566 |
/**
|
|
|
10567 |
* Checking the permissions.
|
|
|
10568 |
*
|
|
|
10569 |
* @return bool
|
|
|
10570 |
*/
|
1254 |
ariadna |
10571 |
public function check_permissions()
|
|
|
10572 |
{
|
1 |
efrain |
10573 |
$context = context_module::instance($this->cmid);
|
|
|
10574 |
return has_capability('mod/assign:exportownsubmission', $context);
|
|
|
10575 |
}
|
|
|
10576 |
|
|
|
10577 |
/**
|
|
|
10578 |
* Display a module name.
|
|
|
10579 |
*
|
|
|
10580 |
* @return string
|
|
|
10581 |
*/
|
1254 |
ariadna |
10582 |
public static function display_name()
|
|
|
10583 |
{
|
1 |
efrain |
10584 |
return get_string('modulename', 'assign');
|
|
|
10585 |
}
|
|
|
10586 |
|
|
|
10587 |
/**
|
|
|
10588 |
* Return array of formats supported by this portfolio call back.
|
|
|
10589 |
*
|
|
|
10590 |
* @return array
|
|
|
10591 |
*/
|
1254 |
ariadna |
10592 |
public static function base_supported_formats()
|
|
|
10593 |
{
|
1 |
efrain |
10594 |
return array(PORTFOLIO_FORMAT_FILE, PORTFOLIO_FORMAT_LEAP2A);
|
|
|
10595 |
}
|
|
|
10596 |
}
|
|
|
10597 |
|
|
|
10598 |
/**
|
|
|
10599 |
* Logic to happen when a/some group(s) has/have been deleted in a course.
|
|
|
10600 |
*
|
|
|
10601 |
* @param int $courseid The course ID.
|
|
|
10602 |
* @param int $groupid The group id if it is known
|
|
|
10603 |
* @return void
|
|
|
10604 |
*/
|
1254 |
ariadna |
10605 |
function assign_process_group_deleted_in_course($courseid, $groupid = null)
|
|
|
10606 |
{
|
1 |
efrain |
10607 |
global $DB;
|
|
|
10608 |
|
|
|
10609 |
$params = array('courseid' => $courseid);
|
|
|
10610 |
if ($groupid) {
|
|
|
10611 |
$params['groupid'] = $groupid;
|
|
|
10612 |
// We just update the group that was deleted.
|
|
|
10613 |
$sql = "SELECT o.id, o.assignid, o.groupid
|
|
|
10614 |
FROM {assign_overrides} o
|
|
|
10615 |
JOIN {assign} assign ON assign.id = o.assignid
|
|
|
10616 |
WHERE assign.course = :courseid
|
|
|
10617 |
AND o.groupid = :groupid";
|
|
|
10618 |
} else {
|
|
|
10619 |
// No groupid, we update all orphaned group overrides for all assign in course.
|
|
|
10620 |
$sql = "SELECT o.id, o.assignid, o.groupid
|
|
|
10621 |
FROM {assign_overrides} o
|
|
|
10622 |
JOIN {assign} assign ON assign.id = o.assignid
|
|
|
10623 |
LEFT JOIN {groups} grp ON grp.id = o.groupid
|
|
|
10624 |
WHERE assign.course = :courseid
|
|
|
10625 |
AND o.groupid IS NOT NULL
|
|
|
10626 |
AND grp.id IS NULL";
|
|
|
10627 |
}
|
|
|
10628 |
$records = $DB->get_records_sql($sql, $params);
|
|
|
10629 |
if (!$records) {
|
|
|
10630 |
return; // Nothing to do.
|
|
|
10631 |
}
|
|
|
10632 |
$DB->delete_records_list('assign_overrides', 'id', array_keys($records));
|
|
|
10633 |
$cache = cache::make('mod_assign', 'overrides');
|
|
|
10634 |
foreach ($records as $record) {
|
|
|
10635 |
$cache->delete("{$record->assignid}_g_{$record->groupid}");
|
|
|
10636 |
}
|
|
|
10637 |
}
|
|
|
10638 |
|
|
|
10639 |
/**
|
|
|
10640 |
* Change the sort order of an override
|
|
|
10641 |
*
|
|
|
10642 |
* @param int $id of the override
|
|
|
10643 |
* @param string $move direction of move
|
|
|
10644 |
* @param int $assignid of the assignment
|
|
|
10645 |
* @return bool success of operation
|
|
|
10646 |
*/
|
1254 |
ariadna |
10647 |
function move_group_override($id, $move, $assignid)
|
|
|
10648 |
{
|
1 |
efrain |
10649 |
global $DB;
|
|
|
10650 |
|
|
|
10651 |
// Get the override object.
|
|
|
10652 |
if (!$override = $DB->get_record('assign_overrides', ['id' => $id, 'assignid' => $assignid], 'id, sortorder, groupid')) {
|
|
|
10653 |
return false;
|
|
|
10654 |
}
|
|
|
10655 |
// Count the number of group overrides.
|
|
|
10656 |
$overridecountgroup = $DB->count_records('assign_overrides', array('userid' => null, 'assignid' => $assignid));
|
|
|
10657 |
|
|
|
10658 |
// Calculate the new sortorder.
|
1254 |
ariadna |
10659 |
if (($move == 'up') and ($override->sortorder > 1)) {
|
1 |
efrain |
10660 |
$neworder = $override->sortorder - 1;
|
|
|
10661 |
} else if (($move == 'down') and ($override->sortorder < $overridecountgroup)) {
|
|
|
10662 |
$neworder = $override->sortorder + 1;
|
|
|
10663 |
} else {
|
|
|
10664 |
return false;
|
|
|
10665 |
}
|
|
|
10666 |
|
|
|
10667 |
// Retrieve the override object that is currently residing in the new position.
|
|
|
10668 |
$params = ['sortorder' => $neworder, 'assignid' => $assignid];
|
|
|
10669 |
if ($swapoverride = $DB->get_record('assign_overrides', $params, 'id, sortorder, groupid')) {
|
|
|
10670 |
|
|
|
10671 |
// Swap the sortorders.
|
|
|
10672 |
$swapoverride->sortorder = $override->sortorder;
|
|
|
10673 |
$override->sortorder = $neworder;
|
|
|
10674 |
|
|
|
10675 |
// Update the override records.
|
|
|
10676 |
$DB->update_record('assign_overrides', $override);
|
|
|
10677 |
$DB->update_record('assign_overrides', $swapoverride);
|
|
|
10678 |
|
|
|
10679 |
// Delete cache for the 2 records we updated above.
|
|
|
10680 |
$cache = cache::make('mod_assign', 'overrides');
|
|
|
10681 |
$cache->delete("{$assignid}_g_{$override->groupid}");
|
|
|
10682 |
$cache->delete("{$assignid}_g_{$swapoverride->groupid}");
|
|
|
10683 |
}
|
|
|
10684 |
|
|
|
10685 |
reorder_group_overrides($assignid);
|
|
|
10686 |
return true;
|
|
|
10687 |
}
|
|
|
10688 |
|
|
|
10689 |
/**
|
|
|
10690 |
* Reorder the overrides starting at the override at the given startorder.
|
|
|
10691 |
*
|
|
|
10692 |
* @param int $assignid of the assigment
|
|
|
10693 |
*/
|
1254 |
ariadna |
10694 |
function reorder_group_overrides($assignid)
|
|
|
10695 |
{
|
1 |
efrain |
10696 |
global $DB;
|
|
|
10697 |
|
|
|
10698 |
$i = 1;
|
|
|
10699 |
if ($overrides = $DB->get_records('assign_overrides', array('userid' => null, 'assignid' => $assignid), 'sortorder ASC')) {
|
|
|
10700 |
$cache = cache::make('mod_assign', 'overrides');
|
|
|
10701 |
foreach ($overrides as $override) {
|
|
|
10702 |
$f = new stdClass();
|
|
|
10703 |
$f->id = $override->id;
|
|
|
10704 |
$f->sortorder = $i++;
|
|
|
10705 |
$DB->update_record('assign_overrides', $f);
|
|
|
10706 |
$cache->delete("{$assignid}_g_{$override->groupid}");
|
|
|
10707 |
|
|
|
10708 |
// Update priorities of group overrides.
|
|
|
10709 |
$params = [
|
|
|
10710 |
'modulename' => 'assign',
|
|
|
10711 |
'instance' => $override->assignid,
|
|
|
10712 |
'groupid' => $override->groupid
|
|
|
10713 |
];
|
|
|
10714 |
$DB->set_field('event', 'priority', $f->sortorder, $params);
|
|
|
10715 |
}
|
|
|
10716 |
}
|
|
|
10717 |
}
|
|
|
10718 |
|
|
|
10719 |
/**
|
|
|
10720 |
* Get the information about the standard assign JavaScript module.
|
|
|
10721 |
* @return array a standard jsmodule structure.
|
|
|
10722 |
*/
|
1254 |
ariadna |
10723 |
function assign_get_js_module()
|
|
|
10724 |
{
|
1 |
efrain |
10725 |
return array(
|
|
|
10726 |
'name' => 'mod_assign',
|
|
|
10727 |
'fullpath' => '/mod/assign/module.js',
|
|
|
10728 |
);
|
|
|
10729 |
}
|