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