Merge branch 'MDL-59248-master' of git://github.com/jleyva/moodle

This commit is contained in:
Eloy Lafuente (stronk7)
2017-10-03 12:35:59 +02:00
8 changed files with 313 additions and 35 deletions
+5 -21
View File
@@ -69,31 +69,15 @@ if ($isreviewer and $workshop->assessing_allowed($USER->id)) {
}
// check that all required examples have been assessed by the user
if ($assessmenteditable and $workshop->useexamples and $workshop->examplesmode == workshop::EXAMPLES_BEFORE_ASSESSMENT
and !has_capability('mod/workshop:manageexamples', $workshop->context)) {
// the reviewer must have submitted their own submission
$reviewersubmission = $workshop->get_submission_by_author($assessment->reviewerid);
$output = $PAGE->get_renderer('mod_workshop');
if (!$reviewersubmission) {
// no money, no love
$assessmenteditable = false;
if ($assessmenteditable) {
list($assessed, $notice) = $workshop->check_examples_assessed_before_assessment($assessment->reviewerid);
if (!$assessed) {
echo $output->header();
echo $output->heading(format_string($workshop->name));
notice(get_string('exampleneedsubmission', 'workshop'), new moodle_url('/mod/workshop/view.php', array('id' => $cm->id)));
notice(get_string($notice, 'workshop'), new moodle_url('/mod/workshop/view.php', array('id' => $cm->id)));
echo $output->footer();
exit;
} else {
$examples = $workshop->get_examples_for_reviewer($assessment->reviewerid);
foreach ($examples as $exampleid => $example) {
if (is_null($example->grade)) {
$assessmenteditable = false;
echo $output->header();
echo $output->heading(format_string($workshop->name));
notice(get_string('exampleneedassessed', 'workshop'), new moodle_url('/mod/workshop/view.php', array('id' => $cm->id)));
echo $output->footer();
exit;
}
}
}
}
+143 -5
View File
@@ -199,7 +199,8 @@ class mod_workshop_external extends external_api {
if (is_null($result['assessingexamplesallowed'])) {
$result['assessingexamplesallowed'] = false;
}
$result['examplesassessed'] = $workshop->check_examples_assessed($USER->id);
$result['examplesassessedbeforesubmission'] = $workshop->check_examples_assessed_before_submission($USER->id);
list($result['examplesassessedbeforeassessment'], $code) = $workshop->check_examples_assessed_before_assessment($USER->id);
$result['warnings'] = array();
return $result;
@@ -222,8 +223,12 @@ class mod_workshop_external extends external_api {
'Is the user allowed to create/edit his assessments?'),
'assessingexamplesallowed' => new external_value(PARAM_BOOL,
'Are reviewers allowed to create/edit their assessments of the example submissions?.'),
'examplesassessed' => new external_value(PARAM_BOOL,
'Whether the given user has assessed all his required examples (always true if there are no examples to assess).'),
'examplesassessedbeforesubmission' => new external_value(PARAM_BOOL,
'Whether the given user has assessed all his required examples before submission
(always true if there are not examples to assess or not configured to check before submission).'),
'examplesassessedbeforeassessment' => new external_value(PARAM_BOOL,
'Whether the given user has assessed all his required examples before assessment
(always true if there are not examples to assessor not configured to check before assessment).'),
'warnings' => new external_warnings()
);
@@ -476,7 +481,7 @@ class mod_workshop_external extends external_api {
// Check if we can submit now.
$canaddsubmission = $workshop->creating_submission_allowed($USER->id);
$canaddsubmission = $canaddsubmission && $workshop->check_examples_assessed($USER->id);
$canaddsubmission = $canaddsubmission && $workshop->check_examples_assessed_before_submission($USER->id);
if (!$canaddsubmission) {
throw new moodle_exception('nopermissions', 'error', '', 'add submission');
}
@@ -592,7 +597,7 @@ class mod_workshop_external extends external_api {
// Check if we can update the submission.
$canupdatesubmission = $submission->authorid == $USER->id;
$canupdatesubmission = $canupdatesubmission && $workshop->modifying_submission_allowed($USER->id);
$canupdatesubmission = $canupdatesubmission && $workshop->check_examples_assessed($USER->id);
$canupdatesubmission = $canupdatesubmission && $workshop->check_examples_assessed_before_submission($USER->id);
if (!$canupdatesubmission) {
throw new moodle_exception('nopermissions', 'error', '', 'update submission');
}
@@ -1182,4 +1187,137 @@ class mod_workshop_external extends external_api {
)
);
}
/**
* Returns the description of the external function parameters.
*
* @return external_function_parameters
* @since Moodle 3.4
*/
public static function get_assessment_form_definition_parameters() {
return new external_function_parameters(
array(
'assessmentid' => new external_value(PARAM_INT, 'Assessment id'),
'mode' => new external_value(PARAM_ALPHA, 'The form mode (assessment or preview)', VALUE_DEFAULT, 'assessment'),
)
);
}
/**
* Retrieves the assessment form definition (data required to be able to display the assessment form).
*
* @param int $assessmentid the assessment id
* @param string $mode the form mode (assessment or preview)
* @return array containing the assessment and warnings.
* @since Moodle 3.4
* @throws moodle_exception
*/
public static function get_assessment_form_definition($assessmentid, $mode = 'assessment') {
global $DB, $USER;
$params = self::validate_parameters(
self::get_assessment_form_definition_parameters(), array('assessmentid' => $assessmentid, 'mode' => $mode)
);
$warnings = $pending = array();
if ($params['mode'] != 'assessment' && $params['mode'] != 'preview') {
throw new invalid_parameter_exception('Invalid value for mode parameter (value: ' . $params['mode'] . ')');
}
// Get and validate the assessment, submission and workshop.
$assessment = $DB->get_record('workshop_assessments', array('id' => $params['assessmentid']), '*', MUST_EXIST);
$submission = $DB->get_record('workshop_submissions', array('id' => $assessment->submissionid), '*', MUST_EXIST);
list($workshop, $course, $cm, $context) = self::validate_workshop($submission->workshopid);
// Check we can edit the assessment (so we can get the form data).
$workshop->check_edit_assessment($assessment, $submission);
$cansetassessmentweight = has_capability('mod/workshop:allocate', $context);
$pending = $workshop->get_pending_assessments_by_reviewer($assessment->reviewerid, $assessment->id);
// Retrieve the data from the strategy plugin.
$strategy = $workshop->grading_strategy_instance();
$strategyname = str_replace('_strategy', '', get_class($strategy)); // Get strategy name.
$mform = $strategy->get_assessment_form(null, $params['mode'], $assessment, true,
array('editableweight' => $cansetassessmentweight, 'pending' => !empty($pending)));
$formdata = $mform->get_customdata();
$result = array(
'dimenssionscount' => $formdata['nodims'],
'descriptionfiles' => external_util::get_area_files($context->id, $strategyname, 'description'),
'warnings' => $warnings
);
// Include missing dimension fields.
for ($i = 0; $i < $formdata['nodims']; $i++) {
$formdata['fields']->{'gradeid__idx_' . $i} = 0;
$formdata['fields']->{'peercomment__idx_' . $i} = '';
}
// Convert all the form data for external.
foreach (array('options', 'fields', 'current') as $typeofdata) {
$result[$typeofdata] = array();
if (!empty($formdata[$typeofdata])) {
$alldata = (array) $formdata[$typeofdata];
foreach ($alldata as $key => $val) {
if (strpos($key, 'peercomment__idx_') === 0) {
// Format reviewer comment.
list($val, $format) = external_format_text($val, FORMAT_MOODLE, $context->id);
} else if (strpos($key, 'description__idx_')) {
// Format dimension description.
$id = str_replace('description__idx_', '', $key);
list($val, $format) = external_format_text($val, $alldata['dimensionid__idx_' . $id . 'format'],
$context->id, $strategyname, 'description', $alldata['dimensionid__idx_' . $id]);
}
$result[$typeofdata][] = array(
'name' => $key,
'value' => $val
);
}
}
}
return $result;
}
/**
* Returns description of method result value
*
* @return external_description
* @since Moodle 3.4
*/
public static function get_assessment_form_definition_returns() {
return new external_single_structure(
array(
'dimenssionscount' => new external_value(PARAM_INT, 'The number of dimenssions used by the form.'),
'descriptionfiles' => new external_files('Files in the description text'),
'options' => new external_multiple_structure(
new external_single_structure(
array(
'name' => new external_value(PARAM_ALPHANUMEXT, 'Option name.'),
'value' => new external_value(PARAM_NOTAGS, 'Option value.')
)
), 'The form options.'
),
'fields' => new external_multiple_structure(
new external_single_structure(
array(
'name' => new external_value(PARAM_ALPHANUMEXT, 'Field name.'),
'value' => new external_value(PARAM_RAW, 'Field default value.')
)
), 'The form fields.'
),
'current' => new external_multiple_structure(
new external_single_structure(
array(
'name' => new external_value(PARAM_ALPHANUMEXT, 'Field name.'),
'value' => new external_value(PARAM_RAW, 'Current field value.')
)
), 'The current field values.'
),
'warnings' => new external_warnings()
)
);
}
}
+7
View File
@@ -113,4 +113,11 @@ $functions = array(
'type' => 'read',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_workshop_get_assessment_form_definition' => array(
'classname' => 'mod_workshop_external',
'methodname' => 'get_assessment_form_definition',
'description' => 'Retrieves the assessment form definition.',
'type' => 'read',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
);
+10
View File
@@ -120,4 +120,14 @@ class workshop_assessment_form extends moodleform {
public function is_editable() {
return !$this->_form->isFrozen();
}
/**
* Return the form custom data.
*
* @return array an array containing the custom data
* @since Moodle 3.4
*/
public function get_customdata() {
return $this->_customdata;
}
}
+58 -2
View File
@@ -2767,13 +2767,13 @@ class workshop {
}
/**
* Check whether the given user has assessed all his required examples.
* Check whether the given user has assessed all his required examples before submission.
*
* @param int $userid the user to check
* @return bool false if there are examples missing assessment, true otherwise.
* @since Moodle 3.4
*/
public function check_examples_assessed($userid) {
public function check_examples_assessed_before_submission($userid) {
if ($this->useexamples and $this->examplesmode == self::EXAMPLES_BEFORE_SUBMISSION
and !has_capability('mod/workshop:manageexamples', $this->context)) {
@@ -2792,6 +2792,35 @@ class workshop {
return true;
}
/**
* Check that all required examples have been assessed by the given user.
*
* @param stdClass $userid the user (reviewer) to check
* @return mixed bool|state false and notice code if there are examples missing assessment, true otherwise.
* @since Moodle 3.4
*/
public function check_examples_assessed_before_assessment($userid) {
if ($this->useexamples and $this->examplesmode == self::EXAMPLES_BEFORE_ASSESSMENT
and !has_capability('mod/workshop:manageexamples', $this->context)) {
// The reviewer must have submitted their own submission.
$reviewersubmission = $this->get_submission_by_author($userid);
if (!$reviewersubmission) {
// No money, no love.
return array(false, 'exampleneedsubmission');
} else {
$examples = $this->get_examples_for_reviewer($userid);
foreach ($examples as $exampleid => $example) {
if (is_null($example->grade)) {
return array(false, 'exampleneedassessed');
}
}
}
}
return array(true, null);
}
/**
* Trigger module viewed event and set the module viewed for completion.
*
@@ -2969,6 +2998,33 @@ class workshop {
}
}
/**
* Helper method for validating if the current user can edit the given assessment.
*
* @param stdClass $assessment assessment object
* @param stdClass $submission submission object
* @return void
* @throws moodle_exception
* @since Moodle 3.4
*/
public function check_edit_assessment($assessment, $submission) {
global $USER;
$this->check_view_assessment($assessment, $submission);
// Further checks.
$isreviewer = ($USER->id == $assessment->reviewerid);
$assessmenteditable = $isreviewer && $this->assessing_allowed($USER->id);
if (!$assessmenteditable) {
throw new moodle_exception('nopermissions', 'error', '', 'edit assessments');
}
list($assessed, $notice) = $this->check_examples_assessed_before_assessment($assessment->reviewerid);
if (!$assessed) {
throw new moodle_exception($notice, 'mod_workshop');
}
}
////////////////////////////////////////////////////////////////////////////////
// Internal methods (implementation details) //
////////////////////////////////////////////////////////////////////////////////
+1 -1
View File
@@ -103,7 +103,7 @@ if ($submission->id and !$workshop->modifying_submission_allowed($USER->id)) {
$canviewall = $canviewall && $workshop->check_group_membership($submission->authorid);
$editable = $editable && $workshop->check_examples_assessed($USER->id);
$editable = ($editable && $workshop->check_examples_assessed_before_submission($USER->id));
$edit = ($editable and $edit);
if (!$candeleteall and $ownsubmission and $editable) {
+88 -5
View File
@@ -79,6 +79,20 @@ class mod_workshop_external_testcase extends externallib_advanced_testcase {
$this->context = context_module::instance($this->workshop->cmid);
$this->cm = get_coursemodule_from_instance('workshop', $this->workshop->id);
// Add grading strategy data (accumulative is the default).
$workshop = new workshop($this->workshop, $this->cm, $this->course);
$strategy = $workshop->grading_strategy_instance();
$data = array();
for ($i = 0; $i < 4; $i++) {
$data['dimensionid__idx_'.$i] = 0;
$data['description__idx_'.$i.'_editor'] = array('text' => "Content $i", 'format' => FORMAT_MOODLE);
$data['grade__idx_'.$i] = 25;
$data['weight__idx_'.$i] = 25;
}
$data['workshopid'] = $workshop->id;
$data['norepeats'] = 4;
$strategy->save_edit_strategy_form((object) $data);
// Create users.
$this->student = self::getDataGenerator()->create_user();
$this->anotherstudentg1 = self::getDataGenerator()->create_user();
@@ -242,7 +256,8 @@ class mod_workshop_external_testcase extends externallib_advanced_testcase {
$this->assertFalse($result['modifyingsubmissionallowed']);
$this->assertFalse($result['assessingallowed']);
$this->assertFalse($result['assessingexamplesallowed']);
$this->assertTrue($result['examplesassessed']);
$this->assertTrue($result['examplesassessedbeforesubmission']);
$this->assertTrue($result['examplesassessedbeforeassessment']);
// Switch phase.
$workshop = new workshop($this->workshop, $this->cm, $this->course);
@@ -254,7 +269,8 @@ class mod_workshop_external_testcase extends externallib_advanced_testcase {
$this->assertTrue($result['modifyingsubmissionallowed']);
$this->assertFalse($result['assessingallowed']);
$this->assertFalse($result['assessingexamplesallowed']);
$this->assertTrue($result['examplesassessed']);
$this->assertTrue($result['examplesassessedbeforesubmission']);
$this->assertTrue($result['examplesassessedbeforeassessment']);
// Switch to next (to assessment).
$workshop = new workshop($this->workshop, $this->cm, $this->course);
@@ -266,7 +282,8 @@ class mod_workshop_external_testcase extends externallib_advanced_testcase {
$this->assertFalse($result['modifyingsubmissionallowed']);
$this->assertTrue($result['assessingallowed']);
$this->assertFalse($result['assessingexamplesallowed']);
$this->assertTrue($result['examplesassessed']);
$this->assertTrue($result['examplesassessedbeforesubmission']);
$this->assertTrue($result['examplesassessedbeforeassessment']);
}
/**
@@ -340,7 +357,7 @@ class mod_workshop_external_testcase extends externallib_advanced_testcase {
$this->assertCount(4, $result['userplan']['phases'][0]['tasks']); // For new empty workshops, always 4 tasks.
foreach ($result['userplan']['phases'][0]['tasks'] as $task) {
if ($task['code'] == 'intro' || $task['code'] == 'instructauthors') {
if ($task['code'] == 'intro' || $task['code'] == 'instructauthors' || $task['code'] == 'editform') {
$this->assertEquals(1, $task['completed']);
} else {
$this->assertEmpty($task['completed']);
@@ -354,7 +371,8 @@ class mod_workshop_external_testcase extends externallib_advanced_testcase {
$result = mod_workshop_external::get_user_plan($this->workshop->id);
$result = external_api::clean_returnvalue(mod_workshop_external::get_user_plan_returns(), $result);
foreach ($result['userplan']['phases'][0]['tasks'] as $task) {
if ($task['code'] == 'intro' || $task['code'] == 'instructauthors' || $task['code'] == 'switchtonextphase') {
if ($task['code'] == 'intro' || $task['code'] == 'instructauthors' || $task['code'] == 'editform' ||
$task['code'] == 'switchtonextphase') {
$this->assertEquals(1, $task['completed']);
} else {
$this->assertEmpty($task['completed']);
@@ -1263,4 +1281,69 @@ class mod_workshop_external_testcase extends externallib_advanced_testcase {
$this->setExpectedException('moodle_exception');
mod_workshop_external::get_assessment($assessmentid);
}
/**
* Test get_assessment_form_definition_reviewer_new_assessment.
*/
public function test_get_assessment_form_definition_reviewer_new_assessment() {
global $DB;
// Create the submission.
$submissionid = $this->create_test_submission($this->anotherstudentg1);
$workshop = new workshop($this->workshop, $this->cm, $this->course);
$submission = $workshop->get_submission_by_id($submissionid);
$assessmentid = $workshop->add_allocation($submission, $this->student->id);
// Switch to assessment phase.
$DB->set_field('workshop', 'phase', workshop::PHASE_ASSESSMENT, array('id' => $this->workshop->id));
$this->setUser($this->student);
$result = mod_workshop_external::get_assessment_form_definition($assessmentid);
$result = external_api::clean_returnvalue(mod_workshop_external::get_assessment_form_definition_returns(), $result);
$this->assertEquals(4, $result['dimenssionscount']); // We receive the expected 4 dimensions.
$this->assertEmpty($result['current']); // Assessment not yet done.
foreach ($result['fields'] as $field) {
if (strpos($field['name'], 'grade__idx_') === 0) {
$this->assertEquals(25, $field['value']); // Check one of the dimension fields attributes.
}
}
}
/**
* Test get_assessment_form_definition_teacher_new_assessment.
*/
public function test_get_assessment_form_definition_teacher_new_assessment() {
global $DB;
// Create the submission.
$submissionid = $this->create_test_submission($this->anotherstudentg1);
$workshop = new workshop($this->workshop, $this->cm, $this->course);
$submission = $workshop->get_submission_by_id($submissionid);
$assessmentid = $workshop->add_allocation($submission, $this->student->id);
// Switch to assessment phase.
$DB->set_field('workshop', 'phase', workshop::PHASE_ASSESSMENT, array('id' => $this->workshop->id));
$this->setUser($this->teacher);
$this->setExpectedException('moodle_exception');
mod_workshop_external::get_assessment_form_definition($assessmentid); // Teachers can't add/edit assessments.
}
/**
* Test get_assessment_form_definition_invalid_phase.
*/
public function test_get_assessment_form_definition_invalid_phase() {
global $DB;
// Create the submission.
$submissionid = $this->create_test_submission($this->anotherstudentg1);
$workshop = new workshop($this->workshop, $this->cm, $this->course);
$submission = $workshop->get_submission_by_id($submissionid);
$assessmentid = $workshop->add_allocation($submission, $this->student->id);
$this->setUser($this->student);
$this->setExpectedException('moodle_exception');
mod_workshop_external::get_assessment_form_definition($assessmentid);
}
}
+1 -1
View File
@@ -24,7 +24,7 @@
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2017051511; // The current module version (YYYYMMDDXX)
$plugin->version = 2017051512; // The current module version (YYYYMMDDXX)
$plugin->requires = 2017050500; // Requires this Moodle version.
$plugin->component = 'mod_workshop';
$plugin->cron = 60; // Give as a chance every minute.