| Línea 143... |
Línea 143... |
| 143 |
$quizdetails['completionpass'] = $quizobj->get_cm()->completionpassgrade;
|
143 |
$quizdetails['completionpass'] = $quizobj->get_cm()->completionpassgrade;
|
| 144 |
}
|
144 |
}
|
| Línea 145... |
Línea 145... |
| 145 |
|
145 |
|
| 146 |
// Fields only for managers.
|
146 |
// Fields only for managers.
|
| 147 |
if (has_capability('moodle/course:manageactivities', $context)) {
|
147 |
if (has_capability('moodle/course:manageactivities', $context)) {
|
| - |
|
148 |
$additionalfields = [
|
| - |
|
149 |
'shuffleanswers',
|
| - |
|
150 |
'timecreated',
|
| - |
|
151 |
'timemodified',
|
| - |
|
152 |
'password',
|
| - |
|
153 |
'subnet',
|
| - |
|
154 |
'precreateattempts',
|
| 148 |
$additionalfields = ['shuffleanswers', 'timecreated', 'timemodified', 'password', 'subnet'];
|
155 |
];
|
| 149 |
$viewablefields = array_merge($viewablefields, $additionalfields);
|
156 |
$viewablefields = array_merge($viewablefields, $additionalfields);
|
| Línea 150... |
Línea 157... |
| 150 |
}
|
157 |
}
|
| 151 |
|
158 |
|
| Línea 265... |
Línea 272... |
| 265 |
offline in the mobile app', VALUE_OPTIONAL),
|
272 |
offline in the mobile app', VALUE_OPTIONAL),
|
| 266 |
'autosaveperiod' => new external_value(PARAM_INT, 'Auto-save delay', VALUE_OPTIONAL),
|
273 |
'autosaveperiod' => new external_value(PARAM_INT, 'Auto-save delay', VALUE_OPTIONAL),
|
| 267 |
'hasfeedback' => new external_value(PARAM_INT, 'Whether the quiz has any non-blank feedback text',
|
274 |
'hasfeedback' => new external_value(PARAM_INT, 'Whether the quiz has any non-blank feedback text',
|
| 268 |
VALUE_OPTIONAL),
|
275 |
VALUE_OPTIONAL),
|
| 269 |
'hasquestions' => new external_value(PARAM_INT, 'Whether the quiz has questions', VALUE_OPTIONAL),
|
276 |
'hasquestions' => new external_value(PARAM_INT, 'Whether the quiz has questions', VALUE_OPTIONAL),
|
| - |
|
277 |
'precreateattempts' => new external_value(PARAM_INT, 'Whether attempt pre-creation is enabled',
|
| - |
|
278 |
VALUE_OPTIONAL),
|
| 270 |
]
|
279 |
]
|
| 271 |
))
|
280 |
))
|
| 272 |
),
|
281 |
),
|
| 273 |
'warnings' => new external_warnings(),
|
282 |
'warnings' => new external_warnings(),
|
| 274 |
]
|
283 |
]
|
| Línea 352... |
Línea 361... |
| 352 |
/**
|
361 |
/**
|
| 353 |
* Describes the parameters for get_user_attempts.
|
362 |
* Describes the parameters for get_user_attempts.
|
| 354 |
*
|
363 |
*
|
| 355 |
* @return external_function_parameters
|
364 |
* @return external_function_parameters
|
| 356 |
* @since Moodle 3.1
|
365 |
* @since Moodle 3.1
|
| - |
|
366 |
* @deprecated Since Moodle 5.0 MDL-68806.
|
| - |
|
367 |
* @todo Final deprecation in Moodle 6.0 (MDL-80956)
|
| 357 |
*/
|
368 |
*/
|
| - |
|
369 |
#[\core\attribute\deprecated(
|
| - |
|
370 |
'mod_quiz_external::get_user_quiz_attempts_parameters',
|
| - |
|
371 |
since: '5.0',
|
| - |
|
372 |
reason: 'The old API for fetching attempts doesn\'t return true states for NOT_STARTED and SUBMITTED attempts',
|
| - |
|
373 |
mdl: 'MDL-68806'
|
| - |
|
374 |
)]
|
| 358 |
public static function get_user_attempts_parameters() {
|
375 |
public static function get_user_attempts_parameters() {
|
| 359 |
return new external_function_parameters (
|
376 |
return new external_function_parameters (
|
| 360 |
[
|
377 |
[
|
| 361 |
'quizid' => new external_value(PARAM_INT, 'quiz instance id'),
|
378 |
'quizid' => new external_value(PARAM_INT, 'quiz instance id'),
|
| 362 |
'userid' => new external_value(PARAM_INT, 'user id, empty for current user', VALUE_DEFAULT, 0),
|
379 |
'userid' => new external_value(PARAM_INT, 'user id, empty for current user', VALUE_DEFAULT, 0),
|
| Línea 368... |
Línea 385... |
| 368 |
}
|
385 |
}
|
| Línea 369... |
Línea 386... |
| 369 |
|
386 |
|
| 370 |
/**
|
387 |
/**
|
| 371 |
* Return a list of attempts for the given quiz and user.
|
388 |
* Return a list of attempts for the given quiz and user.
|
| - |
|
389 |
*
|
| - |
|
390 |
* For backwards compatibility, SUBMITTED attempts will be treated as FINISHED with marks hidden, and NOT_STARTED will not
|
| - |
|
391 |
* be returned. To return all real states, call get_user_quiz_attempts instead.
|
| 372 |
*
|
392 |
*
|
| 373 |
* @param int $quizid quiz instance id
|
393 |
* @param int $quizid quiz instance id
|
| 374 |
* @param int $userid user id
|
394 |
* @param int $userid user id
|
| 375 |
* @param string $status quiz status: all, finished or unfinished
|
395 |
* @param string $status quiz status: all, finished or unfinished
|
| 376 |
* @param bool $includepreviews whether to include previews or not
|
396 |
* @param bool $includepreviews whether to include previews or not
|
| 377 |
* @return array of warnings and the list of attempts
|
397 |
* @return array of warnings and the list of attempts
|
| - |
|
398 |
* @since Moodle 3.1
|
| - |
|
399 |
* @deprecated Since Moodle 5.0 MDL-68806.
|
| 378 |
* @since Moodle 3.1
|
400 |
* @todo Final deprecation in Moodle 6.0 (MDL-80956)
|
| - |
|
401 |
*/
|
| - |
|
402 |
#[\core\attribute\deprecated(
|
| - |
|
403 |
'mod_quiz_external::get_user_quiz_attempts',
|
| - |
|
404 |
since: '5.0',
|
| - |
|
405 |
reason: 'The old API for fetching attempts doesn\'t return true states for NOT_STARTED and SUBMITTED attempts',
|
| - |
|
406 |
mdl: 'MDL-68806'
|
| 379 |
*/
|
407 |
)]
|
| 380 |
public static function get_user_attempts($quizid, $userid = 0, $status = 'finished', $includepreviews = false) {
|
408 |
public static function get_user_attempts($quizid, $userid = 0, $status = 'finished', $includepreviews = false) {
|
| - |
|
409 |
global $USER;
|
| Línea 381... |
Línea 410... |
| 381 |
global $USER;
|
410 |
\core\deprecation::emit_deprecation(__METHOD__);
|
| Línea 382... |
Línea 411... |
| 382 |
|
411 |
|
| 383 |
$warnings = [];
|
412 |
$warnings = [];
|
| Línea 415... |
Línea 444... |
| 415 |
$quizobj = new quiz_settings($quiz, $cm, $course);
|
444 |
$quizobj = new quiz_settings($quiz, $cm, $course);
|
| 416 |
$gradeitemmarks = $quizobj->get_grade_calculator()->compute_grade_item_totals_for_attempts(
|
445 |
$gradeitemmarks = $quizobj->get_grade_calculator()->compute_grade_item_totals_for_attempts(
|
| 417 |
array_column($attempts, 'uniqueid'));
|
446 |
array_column($attempts, 'uniqueid'));
|
| 418 |
$attemptresponse = [];
|
447 |
$attemptresponse = [];
|
| 419 |
foreach ($attempts as $attempt) {
|
448 |
foreach ($attempts as $attempt) {
|
| - |
|
449 |
if ($attempt->state == quiz_attempt::NOT_STARTED) {
|
| - |
|
450 |
continue; // For backwards compatibility, do not return Not Started attempts.
|
| - |
|
451 |
}
|
| 420 |
$reviewoptions = quiz_get_review_options($quiz, $attempt, $context);
|
452 |
$reviewoptions = quiz_get_review_options($quiz, $attempt, $context);
|
| - |
|
453 |
if (
|
| - |
|
454 |
$attempt->state == quiz_attempt::SUBMITTED ||
|
| - |
|
455 |
(
|
| 421 |
if (!has_capability('mod/quiz:viewreports', $context) &&
|
456 |
!has_capability('mod/quiz:viewreports', $context) &&
|
| - |
|
457 |
(
|
| 422 |
($reviewoptions->marks < question_display_options::MARK_AND_MAX || $attempt->state != quiz_attempt::FINISHED)) {
|
458 |
$reviewoptions->marks < question_display_options::MARK_AND_MAX ||
|
| - |
|
459 |
$attempt->state != quiz_attempt::FINISHED
|
| - |
|
460 |
)
|
| - |
|
461 |
)
|
| - |
|
462 |
) {
|
| 423 |
// Blank the mark if the teacher does not allow it.
|
463 |
// Blank the mark if the teacher does not allow it.
|
| 424 |
$attempt->sumgrades = null;
|
464 |
$attempt->sumgrades = null;
|
| 425 |
} else if (isset($gradeitemmarks[$attempt->uniqueid])) {
|
465 |
} else if (isset($gradeitemmarks[$attempt->uniqueid])) {
|
| 426 |
$attempt->gradeitemmarks = [];
|
466 |
$attempt->gradeitemmarks = [];
|
| 427 |
foreach ($gradeitemmarks[$attempt->uniqueid] as $gradeitem) {
|
467 |
foreach ($gradeitemmarks[$attempt->uniqueid] as $gradeitem) {
|
| Línea 430... |
Línea 470... |
| 430 |
'grade' => $gradeitem->grade,
|
470 |
'grade' => $gradeitem->grade,
|
| 431 |
'maxgrade' => $gradeitem->maxgrade,
|
471 |
'maxgrade' => $gradeitem->maxgrade,
|
| 432 |
];
|
472 |
];
|
| 433 |
}
|
473 |
}
|
| 434 |
}
|
474 |
}
|
| - |
|
475 |
if ($attempt->state == quiz_attempt::SUBMITTED) {
|
| - |
|
476 |
$attempt->state = quiz_attempt::FINISHED; // For backwards-compatibility.
|
| - |
|
477 |
}
|
| 435 |
$attemptresponse[] = $attempt;
|
478 |
$attemptresponse[] = $attempt;
|
| 436 |
}
|
479 |
}
|
| 437 |
$result = [];
|
480 |
$result = [];
|
| 438 |
$result['attempts'] = $attemptresponse;
|
481 |
$result['attempts'] = $attemptresponse;
|
| 439 |
$result['warnings'] = $warnings;
|
482 |
$result['warnings'] = $warnings;
|
| Línea 487... |
Línea 530... |
| 487 |
/**
|
530 |
/**
|
| 488 |
* Describes the get_user_attempts return value.
|
531 |
* Describes the get_user_attempts return value.
|
| 489 |
*
|
532 |
*
|
| 490 |
* @return external_single_structure
|
533 |
* @return external_single_structure
|
| 491 |
* @since Moodle 3.1
|
534 |
* @since Moodle 3.1
|
| - |
|
535 |
* @deprecated Since Moodle 5.0 MDL-68806.
|
| - |
|
536 |
* @todo Final deprecation in Moodle 6.0 (MDL-80956)
|
| 492 |
*/
|
537 |
*/
|
| - |
|
538 |
#[\core\attribute\deprecated(
|
| - |
|
539 |
'mod_quiz_external::get_user_quiz_attempts_returns',
|
| - |
|
540 |
since: '5.0',
|
| - |
|
541 |
reason: 'The old API for fetching attempts doesn\'t return true states for NOT_STARTED and SUBMITTED attempts',
|
| - |
|
542 |
mdl: 'MDL-68806'
|
| - |
|
543 |
)]
|
| 493 |
public static function get_user_attempts_returns() {
|
544 |
public static function get_user_attempts_returns() {
|
| - |
|
545 |
$attemptstructure = self::attempt_structure();
|
| - |
|
546 |
$attemptstructure->keys['state']->desc .= " For backwards compatibility, attempts in 'submitted' state will return " .
|
| - |
|
547 |
"'finished' and attempts in 'notstarted' state will return 'inprogress'. To get attempts with all real states, call " .
|
| - |
|
548 |
"get_user_quiz_attempts() instead.";
|
| 494 |
return new external_single_structure(
|
549 |
return new external_single_structure(
|
| 495 |
[
|
550 |
[
|
| 496 |
'attempts' => new external_multiple_structure(self::attempt_structure()),
|
551 |
'attempts' => new external_multiple_structure($attemptstructure),
|
| 497 |
'warnings' => new external_warnings(),
|
552 |
'warnings' => new external_warnings(),
|
| 498 |
]
|
553 |
]
|
| 499 |
);
|
554 |
);
|
| 500 |
}
|
555 |
}
|
| Línea 501... |
Línea 556... |
| 501 |
|
556 |
|
| - |
|
557 |
/**
|
| - |
|
558 |
* Mark get_user_attempts as deprecated.
|
| - |
|
559 |
*
|
| - |
|
560 |
* @return bool
|
| - |
|
561 |
*/
|
| - |
|
562 |
public static function get_user_attempts_is_deprecated(): bool {
|
| - |
|
563 |
return true;
|
| - |
|
564 |
}
|
| - |
|
565 |
|
| - |
|
566 |
/**
|
| - |
|
567 |
* Describes the parameters for get_user_quiz_attempts.
|
| - |
|
568 |
*
|
| - |
|
569 |
* @return external_function_parameters
|
| - |
|
570 |
* @since Moodle 4.5
|
| - |
|
571 |
*/
|
| - |
|
572 |
public static function get_user_quiz_attempts_parameters(): external_function_parameters {
|
| - |
|
573 |
return new external_function_parameters (
|
| - |
|
574 |
[
|
| - |
|
575 |
'quizid' => new external_value(PARAM_INT, 'quiz instance id'),
|
| - |
|
576 |
'userid' => new external_value(PARAM_INT, 'user id, empty for current user', VALUE_DEFAULT, 0),
|
| - |
|
577 |
'status' => new external_value(PARAM_ALPHA, 'quiz status: all, finished or unfinished', VALUE_DEFAULT, 'finished'),
|
| - |
|
578 |
'includepreviews' => new external_value(PARAM_BOOL, 'whether to include previews or not', VALUE_DEFAULT, false),
|
| - |
|
579 |
],
|
| - |
|
580 |
);
|
| - |
|
581 |
}
|
| - |
|
582 |
|
| - |
|
583 |
/**
|
| - |
|
584 |
* Return a list of attempts for the given quiz and user.
|
| - |
|
585 |
*
|
| - |
|
586 |
* @param int $quizid quiz instance id
|
| - |
|
587 |
* @param int $userid user id
|
| - |
|
588 |
* @param string $status quiz status: all, finished or unfinished
|
| - |
|
589 |
* @param bool $includepreviews whether to include previews or not
|
| - |
|
590 |
* @return array of warnings and the list of attempts
|
| - |
|
591 |
* @since Moodle 4.5
|
| - |
|
592 |
*/
|
| - |
|
593 |
public static function get_user_quiz_attempts(
|
| - |
|
594 |
int $quizid,
|
| - |
|
595 |
int $userid = 0,
|
| - |
|
596 |
string $status = 'finished',
|
| - |
|
597 |
bool $includepreviews = false
|
| - |
|
598 |
): array {
|
| - |
|
599 |
global $USER;
|
| - |
|
600 |
|
| - |
|
601 |
$warnings = [];
|
| - |
|
602 |
|
| - |
|
603 |
$params = [
|
| - |
|
604 |
'quizid' => $quizid,
|
| - |
|
605 |
'userid' => $userid,
|
| - |
|
606 |
'status' => $status,
|
| - |
|
607 |
'includepreviews' => $includepreviews,
|
| - |
|
608 |
];
|
| - |
|
609 |
$params = self::validate_parameters(self::get_user_quiz_attempts_parameters(), $params);
|
| - |
|
610 |
|
| - |
|
611 |
[$quiz, $course, $cm, $context] = self::validate_quiz($params['quizid']);
|
| - |
|
612 |
|
| - |
|
613 |
if (!in_array($params['status'], ['all', 'finished', 'unfinished'])) {
|
| - |
|
614 |
throw new invalid_parameter_exception('Invalid status value');
|
| - |
|
615 |
}
|
| - |
|
616 |
|
| - |
|
617 |
// Default value for userid.
|
| - |
|
618 |
if (empty($params['userid'])) {
|
| - |
|
619 |
$params['userid'] = $USER->id;
|
| - |
|
620 |
}
|
| - |
|
621 |
|
| - |
|
622 |
$user = core_user::get_user($params['userid'], '*', MUST_EXIST);
|
| - |
|
623 |
core_user::require_active_user($user);
|
| - |
|
624 |
|
| - |
|
625 |
// Extra checks so only users with permissions can view other users attempts.
|
| - |
|
626 |
if ($USER->id != $user->id) {
|
| - |
|
627 |
require_capability('mod/quiz:viewreports', $context);
|
| - |
|
628 |
}
|
| - |
|
629 |
|
| - |
|
630 |
// Update quiz with override information.
|
| - |
|
631 |
$quiz = quiz_update_effective_access($quiz, $params['userid']);
|
| - |
|
632 |
$attempts = quiz_get_user_attempts($quiz->id, $user->id, $params['status'], $params['includepreviews']);
|
| - |
|
633 |
$quizobj = new quiz_settings($quiz, $cm, $course);
|
| - |
|
634 |
$gradeitemmarks = $quizobj->get_grade_calculator()->compute_grade_item_totals_for_attempts(
|
| - |
|
635 |
array_column($attempts, 'uniqueid'));
|
| - |
|
636 |
$attemptresponse = [];
|
| - |
|
637 |
foreach ($attempts as $attempt) {
|
| - |
|
638 |
$reviewoptions = quiz_get_review_options($quiz, $attempt, $context);
|
| - |
|
639 |
if (!has_capability('mod/quiz:viewreports', $context) &&
|
| - |
|
640 |
($reviewoptions->marks < question_display_options::MARK_AND_MAX || $attempt->state != quiz_attempt::FINISHED)) {
|
| - |
|
641 |
// Blank the mark if the teacher does not allow it.
|
| - |
|
642 |
$attempt->sumgrades = null;
|
| - |
|
643 |
} else if (isset($gradeitemmarks[$attempt->uniqueid])) {
|
| - |
|
644 |
$attempt->gradeitemmarks = [];
|
| - |
|
645 |
foreach ($gradeitemmarks[$attempt->uniqueid] as $gradeitem) {
|
| - |
|
646 |
$attempt->gradeitemmarks[] = [
|
| - |
|
647 |
'name' => \core_external\util::format_string($gradeitem->name, $context),
|
| - |
|
648 |
'grade' => $gradeitem->grade,
|
| - |
|
649 |
'maxgrade' => $gradeitem->maxgrade,
|
| - |
|
650 |
];
|
| - |
|
651 |
}
|
| - |
|
652 |
}
|
| - |
|
653 |
$attemptresponse[] = $attempt;
|
| - |
|
654 |
}
|
| - |
|
655 |
$result = [];
|
| - |
|
656 |
$result['attempts'] = $attemptresponse;
|
| - |
|
657 |
$result['warnings'] = $warnings;
|
| - |
|
658 |
return $result;
|
| - |
|
659 |
}
|
| - |
|
660 |
|
| - |
|
661 |
/**
|
| - |
|
662 |
* Describes the get_user_attempts return value.
|
| - |
|
663 |
*
|
| - |
|
664 |
* @return external_single_structure
|
| - |
|
665 |
* @since Moodle 4.5
|
| - |
|
666 |
*/
|
| - |
|
667 |
public static function get_user_quiz_attempts_returns(): external_single_structure {
|
| - |
|
668 |
return new external_single_structure(
|
| - |
|
669 |
[
|
| - |
|
670 |
'attempts' => new external_multiple_structure(self::attempt_structure()),
|
| - |
|
671 |
'warnings' => new external_warnings(),
|
| - |
|
672 |
],
|
| - |
|
673 |
);
|
| - |
|
674 |
}
|
| - |
|
675 |
|
| 502 |
/**
|
676 |
/**
|
| 503 |
* Describes the parameters for get_user_best_grade.
|
677 |
* Describes the parameters for get_user_best_grade.
|
| 504 |
*
|
678 |
*
|
| 505 |
* @return external_function_parameters
|
679 |
* @return external_function_parameters
|
| 506 |
* @since Moodle 3.1
|
680 |
* @since Moodle 3.1
|
| Línea 801... |
Línea 975... |
| 801 |
|
975 |
|
| 802 |
// Pre-flight check passed.
|
976 |
// Pre-flight check passed.
|
| 803 |
$accessmanager->notify_preflight_check_passed($currentattemptid);
|
977 |
$accessmanager->notify_preflight_check_passed($currentattemptid);
|
| Línea 804... |
Línea 978... |
| 804 |
}
|
978 |
}
|
| 805 |
|
979 |
|
| 806 |
if ($currentattemptid) {
|
980 |
if ($currentattemptid && $lastattempt->state !== quiz_attempt::NOT_STARTED) {
|
| 807 |
if ($lastattempt->state == quiz_attempt::OVERDUE) {
|
981 |
if ($lastattempt->state == quiz_attempt::OVERDUE) {
|
| 808 |
throw new moodle_exception('stateoverdue', 'quiz', $quizobj->view_url());
|
982 |
throw new moodle_exception('stateoverdue', 'quiz', $quizobj->view_url());
|
| 809 |
} else {
|
983 |
} else {
|