MDL-55360 workshop: Emptying grades to pass should set them to zero
[moodle.git] / mod / workshop / lib.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * Library of workshop module functions needed by Moodle core and other subsystems
20  *
21  * All the functions neeeded by Moodle core, gradebook, file subsystem etc
22  * are placed here.
23  *
24  * @package    mod_workshop
25  * @copyright  2009 David Mudrak <david.mudrak@gmail.com>
26  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27  */
29 defined('MOODLE_INTERNAL') || die();
31 require_once($CFG->dirroot . '/calendar/lib.php');
33 ////////////////////////////////////////////////////////////////////////////////
34 // Moodle core API                                                            //
35 ////////////////////////////////////////////////////////////////////////////////
37 /**
38  * Returns the information if the module supports a feature
39  *
40  * @see plugin_supports() in lib/moodlelib.php
41  * @param string $feature FEATURE_xx constant for requested feature
42  * @return mixed true if the feature is supported, null if unknown
43  */
44 function workshop_supports($feature) {
45     switch($feature) {
46         case FEATURE_GRADE_HAS_GRADE:   return true;
47         case FEATURE_GROUPS:            return true;
48         case FEATURE_GROUPINGS:         return true;
49         case FEATURE_MOD_INTRO:         return true;
50         case FEATURE_BACKUP_MOODLE2:    return true;
51         case FEATURE_COMPLETION_TRACKS_VIEWS:
52             return true;
53         case FEATURE_SHOW_DESCRIPTION:  return true;
54         case FEATURE_PLAGIARISM:        return true;
55         default:                        return null;
56     }
57 }
59 /**
60  * Saves a new instance of the workshop into the database
61  *
62  * Given an object containing all the necessary data,
63  * (defined by the form in mod_form.php) this function
64  * will save a new instance and return the id number
65  * of the new instance.
66  *
67  * @param stdClass $workshop An object from the form in mod_form.php
68  * @return int The id of the newly inserted workshop record
69  */
70 function workshop_add_instance(stdclass $workshop) {
71     global $CFG, $DB;
72     require_once(__DIR__ . '/locallib.php');
74     $workshop->phase                 = workshop::PHASE_SETUP;
75     $workshop->timecreated           = time();
76     $workshop->timemodified          = $workshop->timecreated;
77     $workshop->useexamples           = (int)!empty($workshop->useexamples);
78     $workshop->usepeerassessment     = 1;
79     $workshop->useselfassessment     = (int)!empty($workshop->useselfassessment);
80     $workshop->latesubmissions       = (int)!empty($workshop->latesubmissions);
81     $workshop->phaseswitchassessment = (int)!empty($workshop->phaseswitchassessment);
82     $workshop->evaluation            = 'best';
84     if (isset($workshop->gradinggradepass)) {
85         $workshop->gradinggradepass = (float)unformat_float($workshop->gradinggradepass);
86     }
88     if (isset($workshop->submissiongradepass)) {
89         $workshop->submissiongradepass = (float)unformat_float($workshop->submissiongradepass);
90     }
92     if (isset($workshop->submissionfiletypes)) {
93         $workshop->submissionfiletypes = workshop::clean_file_extensions($workshop->submissionfiletypes);
94     }
96     if (isset($workshop->overallfeedbackfiletypes)) {
97         $workshop->overallfeedbackfiletypes = workshop::clean_file_extensions($workshop->overallfeedbackfiletypes);
98     }
100     // insert the new record so we get the id
101     $workshop->id = $DB->insert_record('workshop', $workshop);
103     // we need to use context now, so we need to make sure all needed info is already in db
104     $cmid = $workshop->coursemodule;
105     $DB->set_field('course_modules', 'instance', $workshop->id, array('id' => $cmid));
106     $context = context_module::instance($cmid);
108     // process the custom wysiwyg editors
109     if ($draftitemid = $workshop->instructauthorseditor['itemid']) {
110         $workshop->instructauthors = file_save_draft_area_files($draftitemid, $context->id, 'mod_workshop', 'instructauthors',
111                 0, workshop::instruction_editors_options($context), $workshop->instructauthorseditor['text']);
112         $workshop->instructauthorsformat = $workshop->instructauthorseditor['format'];
113     }
115     if ($draftitemid = $workshop->instructreviewerseditor['itemid']) {
116         $workshop->instructreviewers = file_save_draft_area_files($draftitemid, $context->id, 'mod_workshop', 'instructreviewers',
117                 0, workshop::instruction_editors_options($context), $workshop->instructreviewerseditor['text']);
118         $workshop->instructreviewersformat = $workshop->instructreviewerseditor['format'];
119     }
121     if ($draftitemid = $workshop->conclusioneditor['itemid']) {
122         $workshop->conclusion = file_save_draft_area_files($draftitemid, $context->id, 'mod_workshop', 'conclusion',
123                 0, workshop::instruction_editors_options($context), $workshop->conclusioneditor['text']);
124         $workshop->conclusionformat = $workshop->conclusioneditor['format'];
125     }
127     // re-save the record with the replaced URLs in editor fields
128     $DB->update_record('workshop', $workshop);
130     // create gradebook items
131     workshop_grade_item_update($workshop);
132     workshop_grade_item_category_update($workshop);
134     // create calendar events
135     workshop_calendar_update($workshop, $workshop->coursemodule);
137     return $workshop->id;
140 /**
141  * Given an object containing all the necessary data,
142  * (defined by the form in mod_form.php) this function
143  * will update an existing instance with new data.
144  *
145  * @param stdClass $workshop An object from the form in mod_form.php
146  * @return bool success
147  */
148 function workshop_update_instance(stdclass $workshop) {
149     global $CFG, $DB;
150     require_once(__DIR__ . '/locallib.php');
152     $workshop->timemodified          = time();
153     $workshop->id                    = $workshop->instance;
154     $workshop->useexamples           = (int)!empty($workshop->useexamples);
155     $workshop->usepeerassessment     = 1;
156     $workshop->useselfassessment     = (int)!empty($workshop->useselfassessment);
157     $workshop->latesubmissions       = (int)!empty($workshop->latesubmissions);
158     $workshop->phaseswitchassessment = (int)!empty($workshop->phaseswitchassessment);
160     if (isset($workshop->gradinggradepass)) {
161         $workshop->gradinggradepass = (float)unformat_float($workshop->gradinggradepass);
162     }
164     if (isset($workshop->submissiongradepass)) {
165         $workshop->submissiongradepass = (float)unformat_float($workshop->submissiongradepass);
166     }
168     if (isset($workshop->submissionfiletypes)) {
169         $workshop->submissionfiletypes = workshop::clean_file_extensions($workshop->submissionfiletypes);
170     }
172     if (isset($workshop->overallfeedbackfiletypes)) {
173         $workshop->overallfeedbackfiletypes = workshop::clean_file_extensions($workshop->overallfeedbackfiletypes);
174     }
176     // todo - if the grading strategy is being changed, we may want to replace all aggregated peer grades with nulls
178     $DB->update_record('workshop', $workshop);
179     $context = context_module::instance($workshop->coursemodule);
181     // process the custom wysiwyg editors
182     if ($draftitemid = $workshop->instructauthorseditor['itemid']) {
183         $workshop->instructauthors = file_save_draft_area_files($draftitemid, $context->id, 'mod_workshop', 'instructauthors',
184                 0, workshop::instruction_editors_options($context), $workshop->instructauthorseditor['text']);
185         $workshop->instructauthorsformat = $workshop->instructauthorseditor['format'];
186     }
188     if ($draftitemid = $workshop->instructreviewerseditor['itemid']) {
189         $workshop->instructreviewers = file_save_draft_area_files($draftitemid, $context->id, 'mod_workshop', 'instructreviewers',
190                 0, workshop::instruction_editors_options($context), $workshop->instructreviewerseditor['text']);
191         $workshop->instructreviewersformat = $workshop->instructreviewerseditor['format'];
192     }
194     if ($draftitemid = $workshop->conclusioneditor['itemid']) {
195         $workshop->conclusion = file_save_draft_area_files($draftitemid, $context->id, 'mod_workshop', 'conclusion',
196                 0, workshop::instruction_editors_options($context), $workshop->conclusioneditor['text']);
197         $workshop->conclusionformat = $workshop->conclusioneditor['format'];
198     }
200     // re-save the record with the replaced URLs in editor fields
201     $DB->update_record('workshop', $workshop);
203     // update gradebook items
204     workshop_grade_item_update($workshop);
205     workshop_grade_item_category_update($workshop);
207     // update calendar events
208     workshop_calendar_update($workshop, $workshop->coursemodule);
210     return true;
213 /**
214  * Given an ID of an instance of this module,
215  * this function will permanently delete the instance
216  * and any data that depends on it.
217  *
218  * @param int $id Id of the module instance
219  * @return boolean Success/Failure
220  */
221 function workshop_delete_instance($id) {
222     global $CFG, $DB;
223     require_once($CFG->libdir.'/gradelib.php');
225     if (! $workshop = $DB->get_record('workshop', array('id' => $id))) {
226         return false;
227     }
229     // delete all associated aggregations
230     $DB->delete_records('workshop_aggregations', array('workshopid' => $workshop->id));
232     // get the list of ids of all submissions
233     $submissions = $DB->get_records('workshop_submissions', array('workshopid' => $workshop->id), '', 'id');
235     // get the list of all allocated assessments
236     $assessments = $DB->get_records_list('workshop_assessments', 'submissionid', array_keys($submissions), '', 'id');
238     // delete the associated records from the workshop core tables
239     $DB->delete_records_list('workshop_grades', 'assessmentid', array_keys($assessments));
240     $DB->delete_records_list('workshop_assessments', 'id', array_keys($assessments));
241     $DB->delete_records_list('workshop_submissions', 'id', array_keys($submissions));
243     // call the static clean-up methods of all available subplugins
244     $strategies = core_component::get_plugin_list('workshopform');
245     foreach ($strategies as $strategy => $path) {
246         require_once($path.'/lib.php');
247         $classname = 'workshop_'.$strategy.'_strategy';
248         call_user_func($classname.'::delete_instance', $workshop->id);
249     }
251     $allocators = core_component::get_plugin_list('workshopallocation');
252     foreach ($allocators as $allocator => $path) {
253         require_once($path.'/lib.php');
254         $classname = 'workshop_'.$allocator.'_allocator';
255         call_user_func($classname.'::delete_instance', $workshop->id);
256     }
258     $evaluators = core_component::get_plugin_list('workshopeval');
259     foreach ($evaluators as $evaluator => $path) {
260         require_once($path.'/lib.php');
261         $classname = 'workshop_'.$evaluator.'_evaluation';
262         call_user_func($classname.'::delete_instance', $workshop->id);
263     }
265     // delete the calendar events
266     $events = $DB->get_records('event', array('modulename' => 'workshop', 'instance' => $workshop->id));
267     foreach ($events as $event) {
268         $event = calendar_event::load($event);
269         $event->delete();
270     }
272     // finally remove the workshop record itself
273     $DB->delete_records('workshop', array('id' => $workshop->id));
275     // gradebook cleanup
276     grade_update('mod/workshop', $workshop->course, 'mod', 'workshop', $workshop->id, 0, null, array('deleted' => true));
277     grade_update('mod/workshop', $workshop->course, 'mod', 'workshop', $workshop->id, 1, null, array('deleted' => true));
279     return true;
282 /**
283  * List the actions that correspond to a view of this module.
284  * This is used by the participation report.
285  *
286  * Note: This is not used by new logging system. Event with
287  *       crud = 'r' and edulevel = LEVEL_PARTICIPATING will
288  *       be considered as view action.
289  *
290  * @return array
291  */
292 function workshop_get_view_actions() {
293     return array('view', 'view all', 'view submission', 'view example');
296 /**
297  * List the actions that correspond to a post of this module.
298  * This is used by the participation report.
299  *
300  * Note: This is not used by new logging system. Event with
301  *       crud = ('c' || 'u' || 'd') and edulevel = LEVEL_PARTICIPATING
302  *       will be considered as post action.
303  *
304  * @return array
305  */
306 function workshop_get_post_actions() {
307     return array('add', 'add assessment', 'add example', 'add submission',
308                  'update', 'update assessment', 'update example', 'update submission');
311 /**
312  * Return a small object with summary information about what a
313  * user has done with a given particular instance of this module
314  * Used for user activity reports.
315  * $return->time = the time they did it
316  * $return->info = a short text description
317  *
318  * @param stdClass $course The course record.
319  * @param stdClass $user The user record.
320  * @param cm_info|stdClass $mod The course module info object or record.
321  * @param stdClass $workshop The workshop instance record.
322  * @return stdclass|null
323  */
324 function workshop_user_outline($course, $user, $mod, $workshop) {
325     global $CFG, $DB;
326     require_once($CFG->libdir.'/gradelib.php');
328     $grades = grade_get_grades($course->id, 'mod', 'workshop', $workshop->id, $user->id);
330     $submissiongrade = null;
331     $assessmentgrade = null;
333     $info = '';
334     $time = 0;
336     if (!empty($grades->items[0]->grades)) {
337         $submissiongrade = reset($grades->items[0]->grades);
338         $info .= get_string('submissiongrade', 'workshop') . ': ' . $submissiongrade->str_long_grade . html_writer::empty_tag('br');
339         $time = max($time, $submissiongrade->dategraded);
340     }
341     if (!empty($grades->items[1]->grades)) {
342         $assessmentgrade = reset($grades->items[1]->grades);
343         $info .= get_string('gradinggrade', 'workshop') . ': ' . $assessmentgrade->str_long_grade;
344         $time = max($time, $assessmentgrade->dategraded);
345     }
347     if (!empty($info) and !empty($time)) {
348         $return = new stdclass();
349         $return->time = $time;
350         $return->info = $info;
351         return $return;
352     }
354     return null;
357 /**
358  * Print a detailed representation of what a user has done with
359  * a given particular instance of this module, for user activity reports.
360  *
361  * @param stdClass $course The course record.
362  * @param stdClass $user The user record.
363  * @param cm_info|stdClass $mod The course module info object or record.
364  * @param stdClass $workshop The workshop instance record.
365  * @return string HTML
366  */
367 function workshop_user_complete($course, $user, $mod, $workshop) {
368     global $CFG, $DB, $OUTPUT;
369     require_once(__DIR__.'/locallib.php');
370     require_once($CFG->libdir.'/gradelib.php');
372     $workshop   = new workshop($workshop, $mod, $course);
373     $grades     = grade_get_grades($course->id, 'mod', 'workshop', $workshop->id, $user->id);
375     if (!empty($grades->items[0]->grades)) {
376         $submissiongrade = reset($grades->items[0]->grades);
377         $info = get_string('submissiongrade', 'workshop') . ': ' . $submissiongrade->str_long_grade;
378         echo html_writer::tag('li', $info, array('class'=>'submissiongrade'));
379     }
380     if (!empty($grades->items[1]->grades)) {
381         $assessmentgrade = reset($grades->items[1]->grades);
382         $info = get_string('gradinggrade', 'workshop') . ': ' . $assessmentgrade->str_long_grade;
383         echo html_writer::tag('li', $info, array('class'=>'gradinggrade'));
384     }
386     if (has_capability('mod/workshop:viewallsubmissions', $workshop->context)) {
387         $canviewsubmission = true;
388         if (groups_get_activity_groupmode($workshop->cm) == SEPARATEGROUPS) {
389             // user must have accessallgroups or share at least one group with the submission author
390             if (!has_capability('moodle/site:accessallgroups', $workshop->context)) {
391                 $usersgroups = groups_get_activity_allowed_groups($workshop->cm);
392                 $authorsgroups = groups_get_all_groups($workshop->course->id, $user->id, $workshop->cm->groupingid, 'g.id');
393                 $sharedgroups = array_intersect_key($usersgroups, $authorsgroups);
394                 if (empty($sharedgroups)) {
395                     $canviewsubmission = false;
396                 }
397             }
398         }
399         if ($canviewsubmission and $submission = $workshop->get_submission_by_author($user->id)) {
400             $title      = format_string($submission->title);
401             $url        = $workshop->submission_url($submission->id);
402             $link       = html_writer::link($url, $title);
403             $info       = get_string('submission', 'workshop').': '.$link;
404             echo html_writer::tag('li', $info, array('class'=>'submission'));
405         }
406     }
408     if (has_capability('mod/workshop:viewallassessments', $workshop->context)) {
409         if ($assessments = $workshop->get_assessments_by_reviewer($user->id)) {
410             foreach ($assessments as $assessment) {
411                 $a = new stdclass();
412                 $a->submissionurl = $workshop->submission_url($assessment->submissionid)->out();
413                 $a->assessmenturl = $workshop->assess_url($assessment->id)->out();
414                 $a->submissiontitle = s($assessment->submissiontitle);
415                 echo html_writer::tag('li', get_string('assessmentofsubmission', 'workshop', $a));
416             }
417         }
418     }
421 /**
422  * Given a course and a time, this module should find recent activity
423  * that has occurred in workshop activities and print it out.
424  * Return true if there was output, or false is there was none.
425  *
426  * @param stdClass $course
427  * @param bool $viewfullnames
428  * @param int $timestart
429  * @return boolean
430  */
431 function workshop_print_recent_activity($course, $viewfullnames, $timestart) {
432     global $CFG, $USER, $DB, $OUTPUT;
434     $authoramefields = get_all_user_name_fields(true, 'author', null, 'author');
435     $reviewerfields = get_all_user_name_fields(true, 'reviewer', null, 'reviewer');
437     $sql = "SELECT s.id AS submissionid, s.title AS submissiontitle, s.timemodified AS submissionmodified,
438                    author.id AS authorid, $authoramefields, a.id AS assessmentid, a.timemodified AS assessmentmodified,
439                    reviewer.id AS reviewerid, $reviewerfields, cm.id AS cmid
440               FROM {workshop} w
441         INNER JOIN {course_modules} cm ON cm.instance = w.id
442         INNER JOIN {modules} md ON md.id = cm.module
443         INNER JOIN {workshop_submissions} s ON s.workshopid = w.id
444         INNER JOIN {user} author ON s.authorid = author.id
445          LEFT JOIN {workshop_assessments} a ON a.submissionid = s.id
446          LEFT JOIN {user} reviewer ON a.reviewerid = reviewer.id
447              WHERE cm.course = ?
448                    AND md.name = 'workshop'
449                    AND s.example = 0
450                    AND (s.timemodified > ? OR a.timemodified > ?)
451           ORDER BY s.timemodified";
453     $rs = $DB->get_recordset_sql($sql, array($course->id, $timestart, $timestart));
455     $modinfo = get_fast_modinfo($course); // reference needed because we might load the groups
457     $submissions = array(); // recent submissions indexed by submission id
458     $assessments = array(); // recent assessments indexed by assessment id
459     $users       = array();
461     foreach ($rs as $activity) {
462         if (!array_key_exists($activity->cmid, $modinfo->cms)) {
463             // this should not happen but just in case
464             continue;
465         }
467         $cm = $modinfo->cms[$activity->cmid];
468         if (!$cm->uservisible) {
469             continue;
470         }
472         // remember all user names we can use later
473         if (empty($users[$activity->authorid])) {
474             $u = new stdclass();
475             $users[$activity->authorid] = username_load_fields_from_object($u, $activity, 'author');
476         }
477         if ($activity->reviewerid and empty($users[$activity->reviewerid])) {
478             $u = new stdclass();
479             $users[$activity->reviewerid] = username_load_fields_from_object($u, $activity, 'reviewer');
480         }
482         $context = context_module::instance($cm->id);
483         $groupmode = groups_get_activity_groupmode($cm, $course);
485         if ($activity->submissionmodified > $timestart and empty($submissions[$activity->submissionid])) {
486             $s = new stdclass();
487             $s->title = $activity->submissiontitle;
488             $s->authorid = $activity->authorid;
489             $s->timemodified = $activity->submissionmodified;
490             $s->cmid = $activity->cmid;
491             if ($activity->authorid == $USER->id || has_capability('mod/workshop:viewauthornames', $context)) {
492                 $s->authornamevisible = true;
493             } else {
494                 $s->authornamevisible = false;
495             }
497             // the following do-while wrapper allows to break from deeply nested if-statements
498             do {
499                 if ($s->authorid === $USER->id) {
500                     // own submissions always visible
501                     $submissions[$activity->submissionid] = $s;
502                     break;
503                 }
505                 if (has_capability('mod/workshop:viewallsubmissions', $context)) {
506                     if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
507                         if (isguestuser()) {
508                             // shortcut - guest user does not belong into any group
509                             break;
510                         }
512                         // this might be slow - show only submissions by users who share group with me in this cm
513                         if (!$modinfo->get_groups($cm->groupingid)) {
514                             break;
515                         }
516                         $authorsgroups = groups_get_all_groups($course->id, $s->authorid, $cm->groupingid);
517                         if (is_array($authorsgroups)) {
518                             $authorsgroups = array_keys($authorsgroups);
519                             $intersect = array_intersect($authorsgroups, $modinfo->get_groups($cm->groupingid));
520                             if (empty($intersect)) {
521                                 break;
522                             } else {
523                                 // can see all submissions and shares a group with the author
524                                 $submissions[$activity->submissionid] = $s;
525                                 break;
526                             }
527                         }
529                     } else {
530                         // can see all submissions from all groups
531                         $submissions[$activity->submissionid] = $s;
532                     }
533                 }
534             } while (0);
535         }
537         if ($activity->assessmentmodified > $timestart and empty($assessments[$activity->assessmentid])) {
538             $a = new stdclass();
539             $a->submissionid = $activity->submissionid;
540             $a->submissiontitle = $activity->submissiontitle;
541             $a->reviewerid = $activity->reviewerid;
542             $a->timemodified = $activity->assessmentmodified;
543             $a->cmid = $activity->cmid;
544             if ($activity->reviewerid == $USER->id || has_capability('mod/workshop:viewreviewernames', $context)) {
545                 $a->reviewernamevisible = true;
546             } else {
547                 $a->reviewernamevisible = false;
548             }
550             // the following do-while wrapper allows to break from deeply nested if-statements
551             do {
552                 if ($a->reviewerid === $USER->id) {
553                     // own assessments always visible
554                     $assessments[$activity->assessmentid] = $a;
555                     break;
556                 }
558                 if (has_capability('mod/workshop:viewallassessments', $context)) {
559                     if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
560                         if (isguestuser()) {
561                             // shortcut - guest user does not belong into any group
562                             break;
563                         }
565                         // this might be slow - show only submissions by users who share group with me in this cm
566                         if (!$modinfo->get_groups($cm->groupingid)) {
567                             break;
568                         }
569                         $reviewersgroups = groups_get_all_groups($course->id, $a->reviewerid, $cm->groupingid);
570                         if (is_array($reviewersgroups)) {
571                             $reviewersgroups = array_keys($reviewersgroups);
572                             $intersect = array_intersect($reviewersgroups, $modinfo->get_groups($cm->groupingid));
573                             if (empty($intersect)) {
574                                 break;
575                             } else {
576                                 // can see all assessments and shares a group with the reviewer
577                                 $assessments[$activity->assessmentid] = $a;
578                                 break;
579                             }
580                         }
582                     } else {
583                         // can see all assessments from all groups
584                         $assessments[$activity->assessmentid] = $a;
585                     }
586                 }
587             } while (0);
588         }
589     }
590     $rs->close();
592     $shown = false;
594     if (!empty($submissions)) {
595         $shown = true;
596         echo $OUTPUT->heading(get_string('recentsubmissions', 'workshop'), 3);
597         foreach ($submissions as $id => $submission) {
598             $link = new moodle_url('/mod/workshop/submission.php', array('id'=>$id, 'cmid'=>$submission->cmid));
599             if ($submission->authornamevisible) {
600                 $author = $users[$submission->authorid];
601             } else {
602                 $author = null;
603             }
604             print_recent_activity_note($submission->timemodified, $author, $submission->title, $link->out(), false, $viewfullnames);
605         }
606     }
608     if (!empty($assessments)) {
609         $shown = true;
610         echo $OUTPUT->heading(get_string('recentassessments', 'workshop'), 3);
611         core_collator::asort_objects_by_property($assessments, 'timemodified');
612         foreach ($assessments as $id => $assessment) {
613             $link = new moodle_url('/mod/workshop/assessment.php', array('asid' => $id));
614             if ($assessment->reviewernamevisible) {
615                 $reviewer = $users[$assessment->reviewerid];
616             } else {
617                 $reviewer = null;
618             }
619             print_recent_activity_note($assessment->timemodified, $reviewer, $assessment->submissiontitle, $link->out(), false, $viewfullnames);
620         }
621     }
623     if ($shown) {
624         return true;
625     }
627     return false;
630 /**
631  * Returns all activity in course workshops since a given time
632  *
633  * @param array $activities sequentially indexed array of objects
634  * @param int $index
635  * @param int $timestart
636  * @param int $courseid
637  * @param int $cmid
638  * @param int $userid defaults to 0
639  * @param int $groupid defaults to 0
640  * @return void adds items into $activities and increases $index
641  */
642 function workshop_get_recent_mod_activity(&$activities, &$index, $timestart, $courseid, $cmid, $userid=0, $groupid=0) {
643     global $CFG, $COURSE, $USER, $DB;
645     if ($COURSE->id == $courseid) {
646         $course = $COURSE;
647     } else {
648         $course = $DB->get_record('course', array('id'=>$courseid));
649     }
651     $modinfo = get_fast_modinfo($course);
653     $cm = $modinfo->cms[$cmid];
655     $params = array();
656     if ($userid) {
657         $userselect = "AND (author.id = :authorid OR reviewer.id = :reviewerid)";
658         $params['authorid'] = $userid;
659         $params['reviewerid'] = $userid;
660     } else {
661         $userselect = "";
662     }
664     if ($groupid) {
665         $groupselect = "AND (authorgroupmembership.groupid = :authorgroupid OR reviewergroupmembership.groupid = :reviewergroupid)";
666         $groupjoin   = "LEFT JOIN {groups_members} authorgroupmembership ON authorgroupmembership.userid = author.id
667                         LEFT JOIN {groups_members} reviewergroupmembership ON reviewergroupmembership.userid = reviewer.id";
668         $params['authorgroupid'] = $groupid;
669         $params['reviewergroupid'] = $groupid;
670     } else {
671         $groupselect = "";
672         $groupjoin   = "";
673     }
675     $params['cminstance'] = $cm->instance;
676     $params['submissionmodified'] = $timestart;
677     $params['assessmentmodified'] = $timestart;
679     $authornamefields = get_all_user_name_fields(true, 'author', null, 'author');
680     $reviewerfields = get_all_user_name_fields(true, 'reviewer', null, 'reviewer');
682     $sql = "SELECT s.id AS submissionid, s.title AS submissiontitle, s.timemodified AS submissionmodified,
683                    author.id AS authorid, $authornamefields, author.picture AS authorpicture, author.imagealt AS authorimagealt,
684                    author.email AS authoremail, a.id AS assessmentid, a.timemodified AS assessmentmodified,
685                    reviewer.id AS reviewerid, $reviewerfields, reviewer.picture AS reviewerpicture,
686                    reviewer.imagealt AS reviewerimagealt, reviewer.email AS revieweremail
687               FROM {workshop_submissions} s
688         INNER JOIN {workshop} w ON s.workshopid = w.id
689         INNER JOIN {user} author ON s.authorid = author.id
690          LEFT JOIN {workshop_assessments} a ON a.submissionid = s.id
691          LEFT JOIN {user} reviewer ON a.reviewerid = reviewer.id
692         $groupjoin
693              WHERE w.id = :cminstance
694                    AND s.example = 0
695                    $userselect $groupselect
696                    AND (s.timemodified > :submissionmodified OR a.timemodified > :assessmentmodified)
697           ORDER BY s.timemodified ASC, a.timemodified ASC";
699     $rs = $DB->get_recordset_sql($sql, $params);
701     $groupmode       = groups_get_activity_groupmode($cm, $course);
702     $context         = context_module::instance($cm->id);
703     $grader          = has_capability('moodle/grade:viewall', $context);
704     $accessallgroups = has_capability('moodle/site:accessallgroups', $context);
705     $viewauthors     = has_capability('mod/workshop:viewauthornames', $context);
706     $viewreviewers   = has_capability('mod/workshop:viewreviewernames', $context);
708     $submissions = array(); // recent submissions indexed by submission id
709     $assessments = array(); // recent assessments indexed by assessment id
710     $users       = array();
712     foreach ($rs as $activity) {
714         // remember all user names we can use later
715         if (empty($users[$activity->authorid])) {
716             $u = new stdclass();
717             $additionalfields = explode(',', user_picture::fields());
718             $u = username_load_fields_from_object($u, $activity, 'author', $additionalfields);
719             $users[$activity->authorid] = $u;
720         }
721         if ($activity->reviewerid and empty($users[$activity->reviewerid])) {
722             $u = new stdclass();
723             $additionalfields = explode(',', user_picture::fields());
724             $u = username_load_fields_from_object($u, $activity, 'reviewer', $additionalfields);
725             $users[$activity->reviewerid] = $u;
726         }
728         if ($activity->submissionmodified > $timestart and empty($submissions[$activity->submissionid])) {
729             $s = new stdclass();
730             $s->id = $activity->submissionid;
731             $s->title = $activity->submissiontitle;
732             $s->authorid = $activity->authorid;
733             $s->timemodified = $activity->submissionmodified;
734             if ($activity->authorid == $USER->id || has_capability('mod/workshop:viewauthornames', $context)) {
735                 $s->authornamevisible = true;
736             } else {
737                 $s->authornamevisible = false;
738             }
740             // the following do-while wrapper allows to break from deeply nested if-statements
741             do {
742                 if ($s->authorid === $USER->id) {
743                     // own submissions always visible
744                     $submissions[$activity->submissionid] = $s;
745                     break;
746                 }
748                 if (has_capability('mod/workshop:viewallsubmissions', $context)) {
749                     if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
750                         if (isguestuser()) {
751                             // shortcut - guest user does not belong into any group
752                             break;
753                         }
755                         // this might be slow - show only submissions by users who share group with me in this cm
756                         if (!$modinfo->get_groups($cm->groupingid)) {
757                             break;
758                         }
759                         $authorsgroups = groups_get_all_groups($course->id, $s->authorid, $cm->groupingid);
760                         if (is_array($authorsgroups)) {
761                             $authorsgroups = array_keys($authorsgroups);
762                             $intersect = array_intersect($authorsgroups, $modinfo->get_groups($cm->groupingid));
763                             if (empty($intersect)) {
764                                 break;
765                             } else {
766                                 // can see all submissions and shares a group with the author
767                                 $submissions[$activity->submissionid] = $s;
768                                 break;
769                             }
770                         }
772                     } else {
773                         // can see all submissions from all groups
774                         $submissions[$activity->submissionid] = $s;
775                     }
776                 }
777             } while (0);
778         }
780         if ($activity->assessmentmodified > $timestart and empty($assessments[$activity->assessmentid])) {
781             $a = new stdclass();
782             $a->id = $activity->assessmentid;
783             $a->submissionid = $activity->submissionid;
784             $a->submissiontitle = $activity->submissiontitle;
785             $a->reviewerid = $activity->reviewerid;
786             $a->timemodified = $activity->assessmentmodified;
787             if ($activity->reviewerid == $USER->id || has_capability('mod/workshop:viewreviewernames', $context)) {
788                 $a->reviewernamevisible = true;
789             } else {
790                 $a->reviewernamevisible = false;
791             }
793             // the following do-while wrapper allows to break from deeply nested if-statements
794             do {
795                 if ($a->reviewerid === $USER->id) {
796                     // own assessments always visible
797                     $assessments[$activity->assessmentid] = $a;
798                     break;
799                 }
801                 if (has_capability('mod/workshop:viewallassessments', $context)) {
802                     if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
803                         if (isguestuser()) {
804                             // shortcut - guest user does not belong into any group
805                             break;
806                         }
808                         // this might be slow - show only submissions by users who share group with me in this cm
809                         if (!$modinfo->get_groups($cm->groupingid)) {
810                             break;
811                         }
812                         $reviewersgroups = groups_get_all_groups($course->id, $a->reviewerid, $cm->groupingid);
813                         if (is_array($reviewersgroups)) {
814                             $reviewersgroups = array_keys($reviewersgroups);
815                             $intersect = array_intersect($reviewersgroups, $modinfo->get_groups($cm->groupingid));
816                             if (empty($intersect)) {
817                                 break;
818                             } else {
819                                 // can see all assessments and shares a group with the reviewer
820                                 $assessments[$activity->assessmentid] = $a;
821                                 break;
822                             }
823                         }
825                     } else {
826                         // can see all assessments from all groups
827                         $assessments[$activity->assessmentid] = $a;
828                     }
829                 }
830             } while (0);
831         }
832     }
833     $rs->close();
835     $workshopname = format_string($cm->name, true);
837     if ($grader) {
838         require_once($CFG->libdir.'/gradelib.php');
839         $grades = grade_get_grades($courseid, 'mod', 'workshop', $cm->instance, array_keys($users));
840     }
842     foreach ($submissions as $submission) {
843         $tmpactivity                = new stdclass();
844         $tmpactivity->type          = 'workshop';
845         $tmpactivity->cmid          = $cm->id;
846         $tmpactivity->name          = $workshopname;
847         $tmpactivity->sectionnum    = $cm->sectionnum;
848         $tmpactivity->timestamp     = $submission->timemodified;
849         $tmpactivity->subtype       = 'submission';
850         $tmpactivity->content       = $submission;
851         if ($grader) {
852             $tmpactivity->grade     = $grades->items[0]->grades[$submission->authorid]->str_long_grade;
853         }
854         if ($submission->authornamevisible and !empty($users[$submission->authorid])) {
855             $tmpactivity->user      = $users[$submission->authorid];
856         }
857         $activities[$index++]       = $tmpactivity;
858     }
860     foreach ($assessments as $assessment) {
861         $tmpactivity                = new stdclass();
862         $tmpactivity->type          = 'workshop';
863         $tmpactivity->cmid          = $cm->id;
864         $tmpactivity->name          = $workshopname;
865         $tmpactivity->sectionnum    = $cm->sectionnum;
866         $tmpactivity->timestamp     = $assessment->timemodified;
867         $tmpactivity->subtype       = 'assessment';
868         $tmpactivity->content       = $assessment;
869         if ($grader) {
870             $tmpactivity->grade     = $grades->items[1]->grades[$assessment->reviewerid]->str_long_grade;
871         }
872         if ($assessment->reviewernamevisible and !empty($users[$assessment->reviewerid])) {
873             $tmpactivity->user      = $users[$assessment->reviewerid];
874         }
875         $activities[$index++]       = $tmpactivity;
876     }
879 /**
880  * Print single activity item prepared by {@see workshop_get_recent_mod_activity()}
881  */
882 function workshop_print_recent_mod_activity($activity, $courseid, $detail, $modnames, $viewfullnames) {
883     global $CFG, $OUTPUT;
885     if (!empty($activity->user)) {
886         echo html_writer::tag('div', $OUTPUT->user_picture($activity->user, array('courseid'=>$courseid)),
887                 array('style' => 'float: left; padding: 7px;'));
888     }
890     if ($activity->subtype == 'submission') {
891         echo html_writer::start_tag('div', array('class'=>'submission', 'style'=>'padding: 7px; float:left;'));
893         if ($detail) {
894             echo html_writer::start_tag('h4', array('class'=>'workshop'));
895             $url = new moodle_url('/mod/workshop/view.php', array('id'=>$activity->cmid));
896             $name = s($activity->name);
897             echo html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('icon', $activity->type), 'class'=>'icon', 'alt'=>$name));
898             echo ' ' . $modnames[$activity->type];
899             echo html_writer::link($url, $name, array('class'=>'name', 'style'=>'margin-left: 5px'));
900             echo html_writer::end_tag('h4');
901         }
903         echo html_writer::start_tag('div', array('class'=>'title'));
904         $url = new moodle_url('/mod/workshop/submission.php', array('cmid'=>$activity->cmid, 'id'=>$activity->content->id));
905         $name = s($activity->content->title);
906         echo html_writer::tag('strong', html_writer::link($url, $name));
907         echo html_writer::end_tag('div');
909         if (!empty($activity->user)) {
910             echo html_writer::start_tag('div', array('class'=>'user'));
911             $url = new moodle_url('/user/view.php', array('id'=>$activity->user->id, 'course'=>$courseid));
912             $name = fullname($activity->user);
913             $link = html_writer::link($url, $name);
914             echo get_string('submissionby', 'workshop', $link);
915             echo ' - '.userdate($activity->timestamp);
916             echo html_writer::end_tag('div');
917         } else {
918             echo html_writer::start_tag('div', array('class'=>'anonymous'));
919             echo get_string('submission', 'workshop');
920             echo ' - '.userdate($activity->timestamp);
921             echo html_writer::end_tag('div');
922         }
924         echo html_writer::end_tag('div');
925     }
927     if ($activity->subtype == 'assessment') {
928         echo html_writer::start_tag('div', array('class'=>'assessment', 'style'=>'padding: 7px; float:left;'));
930         if ($detail) {
931             echo html_writer::start_tag('h4', array('class'=>'workshop'));
932             $url = new moodle_url('/mod/workshop/view.php', array('id'=>$activity->cmid));
933             $name = s($activity->name);
934             echo html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('icon', $activity->type), 'class'=>'icon', 'alt'=>$name));
935             echo ' ' . $modnames[$activity->type];
936             echo html_writer::link($url, $name, array('class'=>'name', 'style'=>'margin-left: 5px'));
937             echo html_writer::end_tag('h4');
938         }
940         echo html_writer::start_tag('div', array('class'=>'title'));
941         $url = new moodle_url('/mod/workshop/assessment.php', array('asid'=>$activity->content->id));
942         $name = s($activity->content->submissiontitle);
943         echo html_writer::tag('em', html_writer::link($url, $name));
944         echo html_writer::end_tag('div');
946         if (!empty($activity->user)) {
947             echo html_writer::start_tag('div', array('class'=>'user'));
948             $url = new moodle_url('/user/view.php', array('id'=>$activity->user->id, 'course'=>$courseid));
949             $name = fullname($activity->user);
950             $link = html_writer::link($url, $name);
951             echo get_string('assessmentbyfullname', 'workshop', $link);
952             echo ' - '.userdate($activity->timestamp);
953             echo html_writer::end_tag('div');
954         } else {
955             echo html_writer::start_tag('div', array('class'=>'anonymous'));
956             echo get_string('assessment', 'workshop');
957             echo ' - '.userdate($activity->timestamp);
958             echo html_writer::end_tag('div');
959         }
961         echo html_writer::end_tag('div');
962     }
964     echo html_writer::empty_tag('br', array('style'=>'clear:both'));
967 /**
968  * Regular jobs to execute via cron
969  *
970  * @return boolean true on success, false otherwise
971  */
972 function workshop_cron() {
973     global $CFG, $DB;
975     $now = time();
977     mtrace(' processing workshop subplugins ...');
978     cron_execute_plugin_type('workshopallocation', 'workshop allocation methods');
980     // now when the scheduled allocator had a chance to do its job, check if there
981     // are some workshops to switch into the assessment phase
982     $workshops = $DB->get_records_select("workshop",
983         "phase = 20 AND phaseswitchassessment = 1 AND submissionend > 0 AND submissionend < ?", array($now));
985     if (!empty($workshops)) {
986         mtrace('Processing automatic assessment phase switch in '.count($workshops).' workshop(s) ... ', '');
987         require_once($CFG->dirroot.'/mod/workshop/locallib.php');
988         foreach ($workshops as $workshop) {
989             $cm = get_coursemodule_from_instance('workshop', $workshop->id, $workshop->course, false, MUST_EXIST);
990             $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
991             $workshop = new workshop($workshop, $cm, $course);
992             $workshop->switch_phase(workshop::PHASE_ASSESSMENT);
994             $params = array(
995                 'objectid' => $workshop->id,
996                 'context' => $workshop->context,
997                 'courseid' => $workshop->course->id,
998                 'other' => array(
999                     'workshopphase' => $workshop->phase
1000                 )
1001             );
1002             $event = \mod_workshop\event\phase_switched::create($params);
1003             $event->trigger();
1005             // disable the automatic switching now so that it is not executed again by accident
1006             // if the teacher changes the phase back to the submission one
1007             $DB->set_field('workshop', 'phaseswitchassessment', 0, array('id' => $workshop->id));
1009             // todo inform the teachers
1010         }
1011         mtrace('done');
1012     }
1014     return true;
1017 /**
1018  * Is a given scale used by the instance of workshop?
1019  *
1020  * The function asks all installed grading strategy subplugins. The workshop
1021  * core itself does not use scales. Both grade for submission and grade for
1022  * assessments do not use scales.
1023  *
1024  * @param int $workshopid id of workshop instance
1025  * @param int $scaleid id of the scale to check
1026  * @return bool
1027  */
1028 function workshop_scale_used($workshopid, $scaleid) {
1029     global $CFG; // other files included from here
1031     $strategies = core_component::get_plugin_list('workshopform');
1032     foreach ($strategies as $strategy => $strategypath) {
1033         $strategylib = $strategypath . '/lib.php';
1034         if (is_readable($strategylib)) {
1035             require_once($strategylib);
1036         } else {
1037             throw new coding_exception('the grading forms subplugin must contain library ' . $strategylib);
1038         }
1039         $classname = 'workshop_' . $strategy . '_strategy';
1040         if (method_exists($classname, 'scale_used')) {
1041             if (call_user_func_array(array($classname, 'scale_used'), array($scaleid, $workshopid))) {
1042                 // no need to include any other files - scale is used
1043                 return true;
1044             }
1045         }
1046     }
1048     return false;
1051 /**
1052  * Is a given scale used by any instance of workshop?
1053  *
1054  * The function asks all installed grading strategy subplugins. The workshop
1055  * core itself does not use scales. Both grade for submission and grade for
1056  * assessments do not use scales.
1057  *
1058  * @param int $scaleid id of the scale to check
1059  * @return bool
1060  */
1061 function workshop_scale_used_anywhere($scaleid) {
1062     global $CFG; // other files included from here
1064     $strategies = core_component::get_plugin_list('workshopform');
1065     foreach ($strategies as $strategy => $strategypath) {
1066         $strategylib = $strategypath . '/lib.php';
1067         if (is_readable($strategylib)) {
1068             require_once($strategylib);
1069         } else {
1070             throw new coding_exception('the grading forms subplugin must contain library ' . $strategylib);
1071         }
1072         $classname = 'workshop_' . $strategy . '_strategy';
1073         if (method_exists($classname, 'scale_used')) {
1074             if (call_user_func(array($classname, 'scale_used'), $scaleid)) {
1075                 // no need to include any other files - scale is used
1076                 return true;
1077             }
1078         }
1079     }
1081     return false;
1084 /**
1085  * Returns all other caps used in the module
1086  *
1087  * @return array
1088  */
1089 function workshop_get_extra_capabilities() {
1090     return array('moodle/site:accessallgroups');
1093 ////////////////////////////////////////////////////////////////////////////////
1094 // Gradebook API                                                              //
1095 ////////////////////////////////////////////////////////////////////////////////
1097 /**
1098  * Creates or updates grade items for the give workshop instance
1099  *
1100  * Needed by grade_update_mod_grades() in lib/gradelib.php. Also used by
1101  * {@link workshop_update_grades()}.
1102  *
1103  * @param stdClass $workshop instance object with extra cmidnumber property
1104  * @param stdClass $submissiongrades data for the first grade item
1105  * @param stdClass $assessmentgrades data for the second grade item
1106  * @return void
1107  */
1108 function workshop_grade_item_update(stdclass $workshop, $submissiongrades=null, $assessmentgrades=null) {
1109     global $CFG;
1110     require_once($CFG->libdir.'/gradelib.php');
1112     $a = new stdclass();
1113     $a->workshopname = clean_param($workshop->name, PARAM_NOTAGS);
1115     $item = array();
1116     $item['itemname'] = get_string('gradeitemsubmission', 'workshop', $a);
1117     $item['gradetype'] = GRADE_TYPE_VALUE;
1118     $item['grademax']  = $workshop->grade;
1119     $item['grademin']  = 0;
1120     grade_update('mod/workshop', $workshop->course, 'mod', 'workshop', $workshop->id, 0, $submissiongrades , $item);
1122     $item = array();
1123     $item['itemname'] = get_string('gradeitemassessment', 'workshop', $a);
1124     $item['gradetype'] = GRADE_TYPE_VALUE;
1125     $item['grademax']  = $workshop->gradinggrade;
1126     $item['grademin']  = 0;
1127     grade_update('mod/workshop', $workshop->course, 'mod', 'workshop', $workshop->id, 1, $assessmentgrades, $item);
1130 /**
1131  * Update workshop grades in the gradebook
1132  *
1133  * Needed by grade_update_mod_grades() in lib/gradelib.php
1134  *
1135  * @category grade
1136  * @param stdClass $workshop instance object with extra cmidnumber and modname property
1137  * @param int $userid        update grade of specific user only, 0 means all participants
1138  * @return void
1139  */
1140 function workshop_update_grades(stdclass $workshop, $userid=0) {
1141     global $CFG, $DB;
1142     require_once($CFG->libdir.'/gradelib.php');
1144     $whereuser = $userid ? ' AND authorid = :userid' : '';
1145     $params = array('workshopid' => $workshop->id, 'userid' => $userid);
1146     $sql = 'SELECT authorid, grade, gradeover, gradeoverby, feedbackauthor, feedbackauthorformat, timemodified, timegraded
1147               FROM {workshop_submissions}
1148              WHERE workshopid = :workshopid AND example=0' . $whereuser;
1149     $records = $DB->get_records_sql($sql, $params);
1150     $submissiongrades = array();
1151     foreach ($records as $record) {
1152         $grade = new stdclass();
1153         $grade->userid = $record->authorid;
1154         if (!is_null($record->gradeover)) {
1155             $grade->rawgrade = grade_floatval($workshop->grade * $record->gradeover / 100);
1156             $grade->usermodified = $record->gradeoverby;
1157         } else {
1158             $grade->rawgrade = grade_floatval($workshop->grade * $record->grade / 100);
1159         }
1160         $grade->feedback = $record->feedbackauthor;
1161         $grade->feedbackformat = $record->feedbackauthorformat;
1162         $grade->datesubmitted = $record->timemodified;
1163         $grade->dategraded = $record->timegraded;
1164         $submissiongrades[$record->authorid] = $grade;
1165     }
1167     $whereuser = $userid ? ' AND userid = :userid' : '';
1168     $params = array('workshopid' => $workshop->id, 'userid' => $userid);
1169     $sql = 'SELECT userid, gradinggrade, timegraded
1170               FROM {workshop_aggregations}
1171              WHERE workshopid = :workshopid' . $whereuser;
1172     $records = $DB->get_records_sql($sql, $params);
1173     $assessmentgrades = array();
1174     foreach ($records as $record) {
1175         $grade = new stdclass();
1176         $grade->userid = $record->userid;
1177         $grade->rawgrade = grade_floatval($workshop->gradinggrade * $record->gradinggrade / 100);
1178         $grade->dategraded = $record->timegraded;
1179         $assessmentgrades[$record->userid] = $grade;
1180     }
1182     workshop_grade_item_update($workshop, $submissiongrades, $assessmentgrades);
1185 /**
1186  * Update the grade items categories if they are changed via mod_form.php
1187  *
1188  * We must do it manually here in the workshop module because modedit supports only
1189  * single grade item while we use two.
1190  *
1191  * @param stdClass $workshop An object from the form in mod_form.php
1192  */
1193 function workshop_grade_item_category_update($workshop) {
1195     $gradeitems = grade_item::fetch_all(array(
1196         'itemtype'      => 'mod',
1197         'itemmodule'    => 'workshop',
1198         'iteminstance'  => $workshop->id,
1199         'courseid'      => $workshop->course));
1201     if (!empty($gradeitems)) {
1202         foreach ($gradeitems as $gradeitem) {
1203             if ($gradeitem->itemnumber == 0) {
1204                 if (isset($workshop->submissiongradepass) &&
1205                         $gradeitem->gradepass != $workshop->submissiongradepass) {
1206                     $gradeitem->gradepass = $workshop->submissiongradepass;
1207                     $gradeitem->update();
1208                 }
1209                 if ($gradeitem->categoryid != $workshop->gradecategory) {
1210                     $gradeitem->set_parent($workshop->gradecategory);
1211                 }
1212             } else if ($gradeitem->itemnumber == 1) {
1213                 if (isset($workshop->gradinggradepass) &&
1214                         $gradeitem->gradepass != $workshop->gradinggradepass) {
1215                     $gradeitem->gradepass = $workshop->gradinggradepass;
1216                     $gradeitem->update();
1217                 }
1218                 if ($gradeitem->categoryid != $workshop->gradinggradecategory) {
1219                     $gradeitem->set_parent($workshop->gradinggradecategory);
1220                 }
1221             }
1222         }
1223     }
1226 ////////////////////////////////////////////////////////////////////////////////
1227 // File API                                                                   //
1228 ////////////////////////////////////////////////////////////////////////////////
1230 /**
1231  * Returns the lists of all browsable file areas within the given module context
1232  *
1233  * The file area workshop_intro for the activity introduction field is added automatically
1234  * by {@link file_browser::get_file_info_context_module()}
1235  *
1236  * @package  mod_workshop
1237  * @category files
1238  *
1239  * @param stdClass $course
1240  * @param stdClass $cm
1241  * @param stdClass $context
1242  * @return array of [(string)filearea] => (string)description
1243  */
1244 function workshop_get_file_areas($course, $cm, $context) {
1245     $areas = array();
1246     $areas['instructauthors']          = get_string('areainstructauthors', 'workshop');
1247     $areas['instructreviewers']        = get_string('areainstructreviewers', 'workshop');
1248     $areas['submission_content']       = get_string('areasubmissioncontent', 'workshop');
1249     $areas['submission_attachment']    = get_string('areasubmissionattachment', 'workshop');
1250     $areas['conclusion']               = get_string('areaconclusion', 'workshop');
1251     $areas['overallfeedback_content']  = get_string('areaoverallfeedbackcontent', 'workshop');
1252     $areas['overallfeedback_attachment'] = get_string('areaoverallfeedbackattachment', 'workshop');
1254     return $areas;
1257 /**
1258  * Serves the files from the workshop file areas
1259  *
1260  * Apart from module intro (handled by pluginfile.php automatically), workshop files may be
1261  * media inserted into submission content (like images) and submission attachments. For these two,
1262  * the fileareas submission_content and submission_attachment are used.
1263  * Besides that, areas instructauthors, instructreviewers and conclusion contain the media
1264  * embedded using the mod_form.php.
1265  *
1266  * @package  mod_workshop
1267  * @category files
1268  *
1269  * @param stdClass $course the course object
1270  * @param stdClass $cm the course module object
1271  * @param stdClass $context the workshop's context
1272  * @param string $filearea the name of the file area
1273  * @param array $args extra arguments (itemid, path)
1274  * @param bool $forcedownload whether or not force download
1275  * @param array $options additional options affecting the file serving
1276  * @return bool false if the file not found, just send the file otherwise and do not return anything
1277  */
1278 function workshop_pluginfile($course, $cm, $context, $filearea, array $args, $forcedownload, array $options=array()) {
1279     global $DB, $CFG, $USER;
1281     if ($context->contextlevel != CONTEXT_MODULE) {
1282         return false;
1283     }
1285     require_login($course, true, $cm);
1287     if ($filearea === 'instructauthors') {
1288         array_shift($args); // itemid is ignored here
1289         $relativepath = implode('/', $args);
1290         $fullpath = "/$context->id/mod_workshop/$filearea/0/$relativepath";
1292         $fs = get_file_storage();
1293         if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
1294             send_file_not_found();
1295         }
1297         // finally send the file
1298         send_stored_file($file, null, 0, $forcedownload, $options);
1300     } else if ($filearea === 'instructreviewers') {
1301         array_shift($args); // itemid is ignored here
1302         $relativepath = implode('/', $args);
1303         $fullpath = "/$context->id/mod_workshop/$filearea/0/$relativepath";
1305         $fs = get_file_storage();
1306         if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
1307             send_file_not_found();
1308         }
1310         // finally send the file
1311         send_stored_file($file, null, 0, $forcedownload, $options);
1313     } else if ($filearea === 'conclusion') {
1314         array_shift($args); // itemid is ignored here
1315         $relativepath = implode('/', $args);
1316         $fullpath = "/$context->id/mod_workshop/$filearea/0/$relativepath";
1318         $fs = get_file_storage();
1319         if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
1320             send_file_not_found();
1321         }
1323         // finally send the file
1324         send_stored_file($file, null, 0, $forcedownload, $options);
1326     } else if ($filearea === 'submission_content' or $filearea === 'submission_attachment') {
1327         $itemid = (int)array_shift($args);
1328         if (!$workshop = $DB->get_record('workshop', array('id' => $cm->instance))) {
1329             return false;
1330         }
1331         if (!$submission = $DB->get_record('workshop_submissions', array('id' => $itemid, 'workshopid' => $workshop->id))) {
1332             return false;
1333         }
1335         // make sure the user is allowed to see the file
1336         if (empty($submission->example)) {
1337             if ($USER->id != $submission->authorid) {
1338                 if ($submission->published == 1 and $workshop->phase == 50
1339                         and has_capability('mod/workshop:viewpublishedsubmissions', $context)) {
1340                     // Published submission, we can go (workshop does not take the group mode
1341                     // into account in this case yet).
1342                 } else if (!$DB->record_exists('workshop_assessments', array('submissionid' => $submission->id, 'reviewerid' => $USER->id))) {
1343                     if (!has_capability('mod/workshop:viewallsubmissions', $context)) {
1344                         send_file_not_found();
1345                     } else {
1346                         $gmode = groups_get_activity_groupmode($cm, $course);
1347                         if ($gmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
1348                             // check there is at least one common group with both the $USER
1349                             // and the submission author
1350                             $sql = "SELECT 'x'
1351                                       FROM {workshop_submissions} s
1352                                       JOIN {user} a ON (a.id = s.authorid)
1353                                       JOIN {groups_members} agm ON (a.id = agm.userid)
1354                                       JOIN {user} u ON (u.id = ?)
1355                                       JOIN {groups_members} ugm ON (u.id = ugm.userid)
1356                                      WHERE s.example = 0 AND s.workshopid = ? AND s.id = ? AND agm.groupid = ugm.groupid";
1357                             $params = array($USER->id, $workshop->id, $submission->id);
1358                             if (!$DB->record_exists_sql($sql, $params)) {
1359                                 send_file_not_found();
1360                             }
1361                         }
1362                     }
1363                 }
1364             }
1365         }
1367         $fs = get_file_storage();
1368         $relativepath = implode('/', $args);
1369         $fullpath = "/$context->id/mod_workshop/$filearea/$itemid/$relativepath";
1370         if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
1371             return false;
1372         }
1373         // finally send the file
1374         // these files are uploaded by students - forcing download for security reasons
1375         send_stored_file($file, 0, 0, true, $options);
1377     } else if ($filearea === 'overallfeedback_content' or $filearea === 'overallfeedback_attachment') {
1378         $itemid = (int)array_shift($args);
1379         if (!$workshop = $DB->get_record('workshop', array('id' => $cm->instance))) {
1380             return false;
1381         }
1382         if (!$assessment = $DB->get_record('workshop_assessments', array('id' => $itemid))) {
1383             return false;
1384         }
1385         if (!$submission = $DB->get_record('workshop_submissions', array('id' => $assessment->submissionid, 'workshopid' => $workshop->id))) {
1386             return false;
1387         }
1389         if ($USER->id == $assessment->reviewerid) {
1390             // Reviewers can always see their own files.
1391         } else if ($USER->id == $submission->authorid and $workshop->phase == 50) {
1392             // Authors can see the feedback once the workshop is closed.
1393         } else if (!empty($submission->example) and $assessment->weight == 1) {
1394             // Reference assessments of example submissions can be displayed.
1395         } else if (!has_capability('mod/workshop:viewallassessments', $context)) {
1396             send_file_not_found();
1397         } else {
1398             $gmode = groups_get_activity_groupmode($cm, $course);
1399             if ($gmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
1400                 // Check there is at least one common group with both the $USER
1401                 // and the submission author.
1402                 $sql = "SELECT 'x'
1403                           FROM {workshop_submissions} s
1404                           JOIN {user} a ON (a.id = s.authorid)
1405                           JOIN {groups_members} agm ON (a.id = agm.userid)
1406                           JOIN {user} u ON (u.id = ?)
1407                           JOIN {groups_members} ugm ON (u.id = ugm.userid)
1408                          WHERE s.example = 0 AND s.workshopid = ? AND s.id = ? AND agm.groupid = ugm.groupid";
1409                 $params = array($USER->id, $workshop->id, $submission->id);
1410                 if (!$DB->record_exists_sql($sql, $params)) {
1411                     send_file_not_found();
1412                 }
1413             }
1414         }
1416         $fs = get_file_storage();
1417         $relativepath = implode('/', $args);
1418         $fullpath = "/$context->id/mod_workshop/$filearea/$itemid/$relativepath";
1419         if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
1420             return false;
1421         }
1422         // finally send the file
1423         // these files are uploaded by students - forcing download for security reasons
1424         send_stored_file($file, 0, 0, true, $options);
1425     }
1427     return false;
1430 /**
1431  * File browsing support for workshop file areas
1432  *
1433  * @package  mod_workshop
1434  * @category files
1435  *
1436  * @param file_browser $browser
1437  * @param array $areas
1438  * @param stdClass $course
1439  * @param stdClass $cm
1440  * @param stdClass $context
1441  * @param string $filearea
1442  * @param int $itemid
1443  * @param string $filepath
1444  * @param string $filename
1445  * @return file_info instance or null if not found
1446  */
1447 function workshop_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) {
1448     global $CFG, $DB, $USER;
1450     /** @var array internal cache for author names */
1451     static $submissionauthors = array();
1453     $fs = get_file_storage();
1455     if ($filearea === 'submission_content' or $filearea === 'submission_attachment') {
1457         if (!has_capability('mod/workshop:viewallsubmissions', $context)) {
1458             return null;
1459         }
1461         if (is_null($itemid)) {
1462             // no itemid (submissionid) passed, display the list of all submissions
1463             require_once($CFG->dirroot . '/mod/workshop/fileinfolib.php');
1464             return new workshop_file_info_submissions_container($browser, $course, $cm, $context, $areas, $filearea);
1465         }
1467         // make sure the user can see the particular submission in separate groups mode
1468         $gmode = groups_get_activity_groupmode($cm, $course);
1470         if ($gmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
1471             // check there is at least one common group with both the $USER
1472             // and the submission author (this is not expected to be a frequent
1473             // usecase so we can live with pretty ineffective one query per submission here...)
1474             $sql = "SELECT 'x'
1475                       FROM {workshop_submissions} s
1476                       JOIN {user} a ON (a.id = s.authorid)
1477                       JOIN {groups_members} agm ON (a.id = agm.userid)
1478                       JOIN {user} u ON (u.id = ?)
1479                       JOIN {groups_members} ugm ON (u.id = ugm.userid)
1480                      WHERE s.example = 0 AND s.workshopid = ? AND s.id = ? AND agm.groupid = ugm.groupid";
1481             $params = array($USER->id, $cm->instance, $itemid);
1482             if (!$DB->record_exists_sql($sql, $params)) {
1483                 return null;
1484             }
1485         }
1487         // we are inside some particular submission container
1489         $filepath = is_null($filepath) ? '/' : $filepath;
1490         $filename = is_null($filename) ? '.' : $filename;
1492         if (!$storedfile = $fs->get_file($context->id, 'mod_workshop', $filearea, $itemid, $filepath, $filename)) {
1493             if ($filepath === '/' and $filename === '.') {
1494                 $storedfile = new virtual_root_file($context->id, 'mod_workshop', $filearea, $itemid);
1495             } else {
1496                 // not found
1497                 return null;
1498             }
1499         }
1501         // Checks to see if the user can manage files or is the owner.
1502         // TODO MDL-33805 - Do not use userid here and move the capability check above.
1503         if (!has_capability('moodle/course:managefiles', $context) && $storedfile->get_userid() != $USER->id) {
1504             return null;
1505         }
1507         // let us display the author's name instead of itemid (submission id)
1509         if (isset($submissionauthors[$itemid])) {
1510             $topvisiblename = $submissionauthors[$itemid];
1512         } else {
1514             $sql = "SELECT s.id, u.lastname, u.firstname
1515                       FROM {workshop_submissions} s
1516                       JOIN {user} u ON (s.authorid = u.id)
1517                      WHERE s.example = 0 AND s.workshopid = ?";
1518             $params = array($cm->instance);
1519             $rs = $DB->get_recordset_sql($sql, $params);
1521             foreach ($rs as $submissionauthor) {
1522                 $title = s(fullname($submissionauthor)); // this is generally not unique...
1523                 $submissionauthors[$submissionauthor->id] = $title;
1524             }
1525             $rs->close();
1527             if (!isset($submissionauthors[$itemid])) {
1528                 // should not happen
1529                 return null;
1530             } else {
1531                 $topvisiblename = $submissionauthors[$itemid];
1532             }
1533         }
1535         $urlbase = $CFG->wwwroot . '/pluginfile.php';
1536         // do not allow manual modification of any files!
1537         return new file_info_stored($browser, $context, $storedfile, $urlbase, $topvisiblename, true, true, false, false);
1538     }
1540     if ($filearea === 'overallfeedback_content' or $filearea === 'overallfeedback_attachment') {
1542         if (!has_capability('mod/workshop:viewallassessments', $context)) {
1543             return null;
1544         }
1546         if (is_null($itemid)) {
1547             // No itemid (assessmentid) passed, display the list of all assessments.
1548             require_once($CFG->dirroot . '/mod/workshop/fileinfolib.php');
1549             return new workshop_file_info_overallfeedback_container($browser, $course, $cm, $context, $areas, $filearea);
1550         }
1552         // Make sure the user can see the particular assessment in separate groups mode.
1553         $gmode = groups_get_activity_groupmode($cm, $course);
1554         if ($gmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
1555             // Check there is at least one common group with both the $USER
1556             // and the submission author.
1557             $sql = "SELECT 'x'
1558                       FROM {workshop_submissions} s
1559                       JOIN {user} a ON (a.id = s.authorid)
1560                       JOIN {groups_members} agm ON (a.id = agm.userid)
1561                       JOIN {user} u ON (u.id = ?)
1562                       JOIN {groups_members} ugm ON (u.id = ugm.userid)
1563                      WHERE s.example = 0 AND s.workshopid = ? AND s.id = ? AND agm.groupid = ugm.groupid";
1564             $params = array($USER->id, $cm->instance, $itemid);
1565             if (!$DB->record_exists_sql($sql, $params)) {
1566                 return null;
1567             }
1568         }
1570         // We are inside a particular assessment container.
1571         $filepath = is_null($filepath) ? '/' : $filepath;
1572         $filename = is_null($filename) ? '.' : $filename;
1574         if (!$storedfile = $fs->get_file($context->id, 'mod_workshop', $filearea, $itemid, $filepath, $filename)) {
1575             if ($filepath === '/' and $filename === '.') {
1576                 $storedfile = new virtual_root_file($context->id, 'mod_workshop', $filearea, $itemid);
1577             } else {
1578                 // Not found
1579                 return null;
1580             }
1581         }
1583         // Check to see if the user can manage files or is the owner.
1584         if (!has_capability('moodle/course:managefiles', $context) and $storedfile->get_userid() != $USER->id) {
1585             return null;
1586         }
1588         $urlbase = $CFG->wwwroot . '/pluginfile.php';
1590         // Do not allow manual modification of any files.
1591         return new file_info_stored($browser, $context, $storedfile, $urlbase, $itemid, true, true, false, false);
1592     }
1594     if ($filearea == 'instructauthors' or $filearea == 'instructreviewers' or $filearea == 'conclusion') {
1595         // always only itemid 0
1597         $filepath = is_null($filepath) ? '/' : $filepath;
1598         $filename = is_null($filename) ? '.' : $filename;
1600         $urlbase = $CFG->wwwroot.'/pluginfile.php';
1601         if (!$storedfile = $fs->get_file($context->id, 'mod_workshop', $filearea, 0, $filepath, $filename)) {
1602             if ($filepath === '/' and $filename === '.') {
1603                 $storedfile = new virtual_root_file($context->id, 'mod_workshop', $filearea, 0);
1604             } else {
1605                 // not found
1606                 return null;
1607             }
1608         }
1609         return new file_info_stored($browser, $context, $storedfile, $urlbase, $areas[$filearea], false, true, true, false);
1610     }
1613 ////////////////////////////////////////////////////////////////////////////////
1614 // Navigation API                                                             //
1615 ////////////////////////////////////////////////////////////////////////////////
1617 /**
1618  * Extends the global navigation tree by adding workshop nodes if there is a relevant content
1619  *
1620  * This can be called by an AJAX request so do not rely on $PAGE as it might not be set up properly.
1621  *
1622  * @param navigation_node $navref An object representing the navigation tree node of the workshop module instance
1623  * @param stdClass $course
1624  * @param stdClass $module
1625  * @param cm_info $cm
1626  */
1627 function workshop_extend_navigation(navigation_node $navref, stdclass $course, stdclass $module, cm_info $cm) {
1628     global $CFG;
1630     if (has_capability('mod/workshop:submit', context_module::instance($cm->id))) {
1631         $url = new moodle_url('/mod/workshop/submission.php', array('cmid' => $cm->id));
1632         $mysubmission = $navref->add(get_string('mysubmission', 'workshop'), $url);
1633         $mysubmission->mainnavonly = true;
1634     }
1637 /**
1638  * Extends the settings navigation with the Workshop settings
1640  * This function is called when the context for the page is a workshop module. This is not called by AJAX
1641  * so it is safe to rely on the $PAGE.
1642  *
1643  * @param settings_navigation $settingsnav {@link settings_navigation}
1644  * @param navigation_node $workshopnode {@link navigation_node}
1645  */
1646 function workshop_extend_settings_navigation(settings_navigation $settingsnav, navigation_node $workshopnode=null) {
1647     global $PAGE;
1649     //$workshopobject = $DB->get_record("workshop", array("id" => $PAGE->cm->instance));
1651     if (has_capability('mod/workshop:editdimensions', $PAGE->cm->context)) {
1652         $url = new moodle_url('/mod/workshop/editform.php', array('cmid' => $PAGE->cm->id));
1653         $workshopnode->add(get_string('editassessmentform', 'workshop'), $url, settings_navigation::TYPE_SETTING);
1654     }
1655     if (has_capability('mod/workshop:allocate', $PAGE->cm->context)) {
1656         $url = new moodle_url('/mod/workshop/allocation.php', array('cmid' => $PAGE->cm->id));
1657         $workshopnode->add(get_string('allocate', 'workshop'), $url, settings_navigation::TYPE_SETTING);
1658     }
1661 /**
1662  * Return a list of page types
1663  * @param string $pagetype current page type
1664  * @param stdClass $parentcontext Block's parent context
1665  * @param stdClass $currentcontext Current context of block
1666  */
1667 function workshop_page_type_list($pagetype, $parentcontext, $currentcontext) {
1668     $module_pagetype = array('mod-workshop-*'=>get_string('page-mod-workshop-x', 'workshop'));
1669     return $module_pagetype;
1672 ////////////////////////////////////////////////////////////////////////////////
1673 // Calendar API                                                               //
1674 ////////////////////////////////////////////////////////////////////////////////
1676 /**
1677  * Updates the calendar events associated to the given workshop
1678  *
1679  * @param stdClass $workshop the workshop instance record
1680  * @param int $cmid course module id
1681  */
1682 function workshop_calendar_update(stdClass $workshop, $cmid) {
1683     global $DB;
1685     // get the currently registered events so that we can re-use their ids
1686     $currentevents = $DB->get_records('event', array('modulename' => 'workshop', 'instance' => $workshop->id));
1688     // the common properties for all events
1689     $base = new stdClass();
1690     $base->description  = format_module_intro('workshop', $workshop, $cmid, false);
1691     $base->courseid     = $workshop->course;
1692     $base->groupid      = 0;
1693     $base->userid       = 0;
1694     $base->modulename   = 'workshop';
1695     $base->eventtype    = 'pluginname';
1696     $base->instance     = $workshop->id;
1697     $base->visible      = instance_is_visible('workshop', $workshop);
1698     $base->timeduration = 0;
1700     if ($workshop->submissionstart) {
1701         $event = clone($base);
1702         $event->name = get_string('submissionstartevent', 'mod_workshop', $workshop->name);
1703         $event->timestart = $workshop->submissionstart;
1704         if ($reusedevent = array_shift($currentevents)) {
1705             $event->id = $reusedevent->id;
1706         } else {
1707             // should not be set but just in case
1708             unset($event->id);
1709         }
1710         // update() will reuse a db record if the id field is set
1711         $eventobj = new calendar_event($event);
1712         $eventobj->update($event, false);
1713     }
1715     if ($workshop->submissionend) {
1716         $event = clone($base);
1717         $event->name = get_string('submissionendevent', 'mod_workshop', $workshop->name);
1718         $event->timestart = $workshop->submissionend;
1719         if ($reusedevent = array_shift($currentevents)) {
1720             $event->id = $reusedevent->id;
1721         } else {
1722             // should not be set but just in case
1723             unset($event->id);
1724         }
1725         // update() will reuse a db record if the id field is set
1726         $eventobj = new calendar_event($event);
1727         $eventobj->update($event, false);
1728     }
1730     if ($workshop->assessmentstart) {
1731         $event = clone($base);
1732         $event->name = get_string('assessmentstartevent', 'mod_workshop', $workshop->name);
1733         $event->timestart = $workshop->assessmentstart;
1734         if ($reusedevent = array_shift($currentevents)) {
1735             $event->id = $reusedevent->id;
1736         } else {
1737             // should not be set but just in case
1738             unset($event->id);
1739         }
1740         // update() will reuse a db record if the id field is set
1741         $eventobj = new calendar_event($event);
1742         $eventobj->update($event, false);
1743     }
1745     if ($workshop->assessmentend) {
1746         $event = clone($base);
1747         $event->name = get_string('assessmentendevent', 'mod_workshop', $workshop->name);
1748         $event->timestart = $workshop->assessmentend;
1749         if ($reusedevent = array_shift($currentevents)) {
1750             $event->id = $reusedevent->id;
1751         } else {
1752             // should not be set but just in case
1753             unset($event->id);
1754         }
1755         // update() will reuse a db record if the id field is set
1756         $eventobj = new calendar_event($event);
1757         $eventobj->update($event, false);
1758     }
1760     // delete any leftover events
1761     foreach ($currentevents as $oldevent) {
1762         $oldevent = calendar_event::load($oldevent);
1763         $oldevent->delete();
1764     }
1767 ////////////////////////////////////////////////////////////////////////////////
1768 // Course reset API                                                           //
1769 ////////////////////////////////////////////////////////////////////////////////
1771 /**
1772  * Extends the course reset form with workshop specific settings.
1773  *
1774  * @param MoodleQuickForm $mform
1775  */
1776 function workshop_reset_course_form_definition($mform) {
1778     $mform->addElement('header', 'workshopheader', get_string('modulenameplural', 'mod_workshop'));
1780     $mform->addElement('advcheckbox', 'reset_workshop_submissions', get_string('resetsubmissions', 'mod_workshop'));
1781     $mform->addHelpButton('reset_workshop_submissions', 'resetsubmissions', 'mod_workshop');
1783     $mform->addElement('advcheckbox', 'reset_workshop_assessments', get_string('resetassessments', 'mod_workshop'));
1784     $mform->addHelpButton('reset_workshop_assessments', 'resetassessments', 'mod_workshop');
1785     $mform->disabledIf('reset_workshop_assessments', 'reset_workshop_submissions', 'checked');
1787     $mform->addElement('advcheckbox', 'reset_workshop_phase', get_string('resetphase', 'mod_workshop'));
1788     $mform->addHelpButton('reset_workshop_phase', 'resetphase', 'mod_workshop');
1791 /**
1792  * Provides default values for the workshop settings in the course reset form.
1793  *
1794  * @param stdClass $course The course to be reset.
1795  */
1796 function workshop_reset_course_form_defaults(stdClass $course) {
1798     $defaults = array(
1799         'reset_workshop_submissions'    => 1,
1800         'reset_workshop_assessments'    => 1,
1801         'reset_workshop_phase'          => 1,
1802     );
1804     return $defaults;
1807 /**
1808  * Performs the reset of all workshop instances in the course.
1809  *
1810  * @param stdClass $data The actual course reset settings.
1811  * @return array List of results, each being array[(string)component, (string)item, (string)error]
1812  */
1813 function workshop_reset_userdata(stdClass $data) {
1814     global $CFG, $DB;
1816     if (empty($data->reset_workshop_submissions)
1817             and empty($data->reset_workshop_assessments)
1818             and empty($data->reset_workshop_phase) ) {
1819         // Nothing to do here.
1820         return array();
1821     }
1823     $workshoprecords = $DB->get_records('workshop', array('course' => $data->courseid));
1825     if (empty($workshoprecords)) {
1826         // What a boring course - no workshops here!
1827         return array();
1828     }
1830     require_once($CFG->dirroot . '/mod/workshop/locallib.php');
1832     $course = $DB->get_record('course', array('id' => $data->courseid), '*', MUST_EXIST);
1833     $status = array();
1835     foreach ($workshoprecords as $workshoprecord) {
1836         $cm = get_coursemodule_from_instance('workshop', $workshoprecord->id, $course->id, false, MUST_EXIST);
1837         $workshop = new workshop($workshoprecord, $cm, $course);
1838         $status = array_merge($status, $workshop->reset_userdata($data));
1839     }
1841     return $status;