MDL-44536 workshop: Fix missing phpDoc for user report callbacks
[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) {
1143 if ($gradeitem->categoryid != $workshop->gradecategory) {
1144 $gradeitem->set_parent($workshop->gradecategory);
1145 }
1146 } else if ($gradeitem->itemnumber == 1) {
1147 if ($gradeitem->categoryid != $workshop->gradinggradecategory) {
1148 $gradeitem->set_parent($workshop->gradinggradecategory);
1149 }
1150 }
82f336aa
SL
1151 if (!empty($workshop->add)) {
1152 $gradecategory = $gradeitem->get_parent_category();
1153 if (grade_category::aggregation_uses_aggregationcoef($gradecategory->aggregation)) {
1154 if ($gradecategory->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN) {
1155 $gradeitem->aggregationcoef = 1;
1156 } else {
1157 $gradeitem->aggregationcoef = 0;
1158 }
1159 $gradeitem->update();
1160 }
1161 }
a74cadfa
DM
1162 }
1163 }
1164}
1165
b8ead2e6
DM
1166////////////////////////////////////////////////////////////////////////////////
1167// File API //
1168////////////////////////////////////////////////////////////////////////////////
0dc47fb9 1169
a39d7d87
DM
1170/**
1171 * Returns the lists of all browsable file areas within the given module context
1172 *
1173 * The file area workshop_intro for the activity introduction field is added automatically
64f93798 1174 * by {@link file_browser::get_file_info_context_module()}
a39d7d87 1175 *
d2b7803e
DC
1176 * @package mod_workshop
1177 * @category files
1178 *
5924db72
PS
1179 * @param stdClass $course
1180 * @param stdClass $cm
1181 * @param stdClass $context
a39d7d87
DM
1182 * @return array of [(string)filearea] => (string)description
1183 */
1184function workshop_get_file_areas($course, $cm, $context) {
1185 $areas = array();
64f93798
PS
1186 $areas['instructauthors'] = get_string('areainstructauthors', 'workshop');
1187 $areas['instructreviewers'] = get_string('areainstructreviewers', 'workshop');
1188 $areas['submission_content'] = get_string('areasubmissioncontent', 'workshop');
1189 $areas['submission_attachment'] = get_string('areasubmissionattachment', 'workshop');
5a49f825 1190 $areas['conclusion'] = get_string('areaconclusion', 'workshop');
88e79d8a
DM
1191 $areas['overallfeedback_content'] = get_string('areaoverallfeedbackcontent', 'workshop');
1192 $areas['overallfeedback_attachment'] = get_string('areaoverallfeedbackattachment', 'workshop');
64f93798 1193
a39d7d87
DM
1194 return $areas;
1195}
1196
0dc47fb9 1197/**
b8ead2e6 1198 * Serves the files from the workshop file areas
0dc47fb9 1199 *
b8ead2e6
DM
1200 * Apart from module intro (handled by pluginfile.php automatically), workshop files may be
1201 * media inserted into submission content (like images) and submission attachments. For these two,
7528e238 1202 * the fileareas submission_content and submission_attachment are used.
5a49f825 1203 * Besides that, areas instructauthors, instructreviewers and conclusion contain the media
6516b9e9 1204 * embedded using the mod_form.php.
0dc47fb9 1205 *
d2b7803e
DC
1206 * @package mod_workshop
1207 * @category files
1208 *
261cbbac
DM
1209 * @param stdClass $course the course object
1210 * @param stdClass $cm the course module object
1211 * @param stdClass $context the workshop's context
1212 * @param string $filearea the name of the file area
1213 * @param array $args extra arguments (itemid, path)
1214 * @param bool $forcedownload whether or not force download
1215 * @param array $options additional options affecting the file serving
1216 * @return bool false if the file not found, just send the file otherwise and do not return anything
0dc47fb9 1217 */
261cbbac 1218function workshop_pluginfile($course, $cm, $context, $filearea, array $args, $forcedownload, array $options=array()) {
7528e238 1219 global $DB, $CFG, $USER;
0dc47fb9 1220
64f93798
PS
1221 if ($context->contextlevel != CONTEXT_MODULE) {
1222 return false;
6516b9e9 1223 }
64f93798 1224
6516b9e9
DM
1225 require_login($course, true, $cm);
1226
64f93798 1227 if ($filearea === 'instructauthors') {
92039f1c 1228 array_shift($args); // itemid is ignored here
64f93798 1229 $relativepath = implode('/', $args);
261cbbac 1230 $fullpath = "/$context->id/mod_workshop/$filearea/0/$relativepath";
6516b9e9
DM
1231
1232 $fs = get_file_storage();
1233 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
15d12b54
DM
1234 send_file_not_found();
1235 }
1236
15d12b54 1237 // finally send the file
0c431257 1238 send_stored_file($file, null, 0, $forcedownload, $options);
15d12b54 1239
5a49f825
DM
1240 } else if ($filearea === 'instructreviewers') {
1241 array_shift($args); // itemid is ignored here
1242 $relativepath = implode('/', $args);
1243 $fullpath = "/$context->id/mod_workshop/$filearea/0/$relativepath";
1244
1245 $fs = get_file_storage();
1246 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
1247 send_file_not_found();
1248 }
1249
5a49f825 1250 // finally send the file
0c431257 1251 send_stored_file($file, null, 0, $forcedownload, $options);
5a49f825
DM
1252
1253 } else if ($filearea === 'conclusion') {
92039f1c 1254 array_shift($args); // itemid is ignored here
64f93798 1255 $relativepath = implode('/', $args);
7a5f4be0 1256 $fullpath = "/$context->id/mod_workshop/$filearea/0/$relativepath";
15d12b54
DM
1257
1258 $fs = get_file_storage();
1259 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
6516b9e9
DM
1260 send_file_not_found();
1261 }
1262
6516b9e9 1263 // finally send the file
0c431257 1264 send_stored_file($file, null, 0, $forcedownload, $options);
0dc47fb9 1265
64f93798 1266 } else if ($filearea === 'submission_content' or $filearea === 'submission_attachment') {
18cbfe9b 1267 $itemid = (int)array_shift($args);
64f93798 1268 if (!$workshop = $DB->get_record('workshop', array('id' => $cm->instance))) {
18cbfe9b
DM
1269 return false;
1270 }
64f93798 1271 if (!$submission = $DB->get_record('workshop_submissions', array('id' => $itemid, 'workshopid' => $workshop->id))) {
18cbfe9b
DM
1272 return false;
1273 }
7528e238
DM
1274
1275 // make sure the user is allowed to see the file
1276 if (empty($submission->example)) {
1277 if ($USER->id != $submission->authorid) {
2d4a3e68
DM
1278 if ($submission->published == 1 and $workshop->phase == 50
1279 and has_capability('mod/workshop:viewpublishedsubmissions', $context)) {
1280 // Published submission, we can go (workshop does not take the group mode
1281 // into account in this case yet).
1282 } else if (!$DB->record_exists('workshop_assessments', array('submissionid' => $submission->id, 'reviewerid' => $USER->id))) {
7528e238
DM
1283 if (!has_capability('mod/workshop:viewallsubmissions', $context)) {
1284 send_file_not_found();
1285 } else {
1286 $gmode = groups_get_activity_groupmode($cm, $course);
1287 if ($gmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
1288 // check there is at least one common group with both the $USER
1289 // and the submission author
1290 $sql = "SELECT 'x'
1291 FROM {workshop_submissions} s
1292 JOIN {user} a ON (a.id = s.authorid)
1293 JOIN {groups_members} agm ON (a.id = agm.userid)
1294 JOIN {user} u ON (u.id = ?)
1295 JOIN {groups_members} ugm ON (u.id = ugm.userid)
1296 WHERE s.example = 0 AND s.workshopid = ? AND s.id = ? AND agm.groupid = ugm.groupid";
1297 $params = array($USER->id, $workshop->id, $submission->id);
1298 if (!$DB->record_exists_sql($sql, $params)) {
1299 send_file_not_found();
1300 }
1301 }
1302 }
1303 }
1304 }
1305 }
1306
05837ba3
DM
1307 $fs = get_file_storage();
1308 $relativepath = implode('/', $args);
1309 $fullpath = "/$context->id/mod_workshop/$filearea/$itemid/$relativepath";
1310 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
1311 return false;
1312 }
1313 // finally send the file
1314 // these files are uploaded by students - forcing download for security reasons
1315 send_stored_file($file, 0, 0, true, $options);
1316
1317 } else if ($filearea === 'overallfeedback_content' or $filearea === 'overallfeedback_attachment') {
1318 $itemid = (int)array_shift($args);
1319 if (!$workshop = $DB->get_record('workshop', array('id' => $cm->instance))) {
1320 return false;
1321 }
1322 if (!$assessment = $DB->get_record('workshop_assessments', array('id' => $itemid))) {
1323 return false;
1324 }
1325 if (!$submission = $DB->get_record('workshop_submissions', array('id' => $assessment->submissionid, 'workshopid' => $workshop->id))) {
1326 return false;
1327 }
1328
1329 if ($USER->id == $assessment->reviewerid) {
1330 // Reviewers can always see their own files.
1331 } else if ($USER->id == $submission->authorid and $workshop->phase == 50) {
1332 // Authors can see the feedback once the workshop is closed.
1333 } else if (!empty($submission->example) and $assessment->weight == 1) {
1334 // Reference assessments of example submissions can be displayed.
1335 } else if (!has_capability('mod/workshop:viewallassessments', $context)) {
1336 send_file_not_found();
1337 } else {
1338 $gmode = groups_get_activity_groupmode($cm, $course);
1339 if ($gmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
1340 // Check there is at least one common group with both the $USER
1341 // and the submission author.
1342 $sql = "SELECT 'x'
1343 FROM {workshop_submissions} s
1344 JOIN {user} a ON (a.id = s.authorid)
1345 JOIN {groups_members} agm ON (a.id = agm.userid)
1346 JOIN {user} u ON (u.id = ?)
1347 JOIN {groups_members} ugm ON (u.id = ugm.userid)
1348 WHERE s.example = 0 AND s.workshopid = ? AND s.id = ? AND agm.groupid = ugm.groupid";
1349 $params = array($USER->id, $workshop->id, $submission->id);
1350 if (!$DB->record_exists_sql($sql, $params)) {
1351 send_file_not_found();
1352 }
1353 }
1354 }
1355
18cbfe9b 1356 $fs = get_file_storage();
64f93798
PS
1357 $relativepath = implode('/', $args);
1358 $fullpath = "/$context->id/mod_workshop/$filearea/$itemid/$relativepath";
18cbfe9b
DM
1359 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
1360 return false;
1361 }
1362 // finally send the file
1363 // these files are uploaded by students - forcing download for security reasons
261cbbac 1364 send_stored_file($file, 0, 0, true, $options);
0dc47fb9 1365 }
18cbfe9b
DM
1366
1367 return false;
0dc47fb9
DM
1368}
1369
1370/**
1371 * File browsing support for workshop file areas
1372 *
d2b7803e
DC
1373 * @package mod_workshop
1374 * @category files
1375 *
1376 * @param file_browser $browser
1377 * @param array $areas
5924db72
PS
1378 * @param stdClass $course
1379 * @param stdClass $cm
1380 * @param stdClass $context
0dc47fb9
DM
1381 * @param string $filearea
1382 * @param int $itemid
1383 * @param string $filepath
1384 * @param string $filename
d2b7803e 1385 * @return file_info instance or null if not found
0dc47fb9
DM
1386 */
1387function workshop_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) {
b7a5e3d6 1388 global $CFG, $DB, $USER;
88e79d8a 1389
b7a5e3d6
DM
1390 /** @var array internal cache for author names */
1391 static $submissionauthors = array();
0dc47fb9 1392
0dc47fb9 1393 $fs = get_file_storage();
a39d7d87 1394
f08c1568 1395 if ($filearea === 'submission_content' or $filearea === 'submission_attachment') {
a39d7d87 1396
b7a5e3d6
DM
1397 if (!has_capability('mod/workshop:viewallsubmissions', $context)) {
1398 return null;
1399 }
1400
a39d7d87 1401 if (is_null($itemid)) {
b7a5e3d6 1402 // no itemid (submissionid) passed, display the list of all submissions
a39d7d87
DM
1403 require_once($CFG->dirroot . '/mod/workshop/fileinfolib.php');
1404 return new workshop_file_info_submissions_container($browser, $course, $cm, $context, $areas, $filearea);
1405 }
1406
b7a5e3d6
DM
1407 // make sure the user can see the particular submission in separate groups mode
1408 $gmode = groups_get_activity_groupmode($cm, $course);
1409
1410 if ($gmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
1411 // check there is at least one common group with both the $USER
1412 // and the submission author (this is not expected to be a frequent
1413 // usecase so we can live with pretty ineffective one query per submission here...)
1414 $sql = "SELECT 'x'
1415 FROM {workshop_submissions} s
1416 JOIN {user} a ON (a.id = s.authorid)
1417 JOIN {groups_members} agm ON (a.id = agm.userid)
1418 JOIN {user} u ON (u.id = ?)
1419 JOIN {groups_members} ugm ON (u.id = ugm.userid)
1420 WHERE s.example = 0 AND s.workshopid = ? AND s.id = ? AND agm.groupid = ugm.groupid";
1421 $params = array($USER->id, $cm->instance, $itemid);
1422 if (!$DB->record_exists_sql($sql, $params)) {
1423 return null;
1424 }
1425 }
1426
1427 // we are inside some particular submission container
a39d7d87
DM
1428
1429 $filepath = is_null($filepath) ? '/' : $filepath;
1430 $filename = is_null($filename) ? '.' : $filename;
1431
64f93798 1432 if (!$storedfile = $fs->get_file($context->id, 'mod_workshop', $filearea, $itemid, $filepath, $filename)) {
a39d7d87 1433 if ($filepath === '/' and $filename === '.') {
64f93798 1434 $storedfile = new virtual_root_file($context->id, 'mod_workshop', $filearea, $itemid);
a39d7d87
DM
1435 } else {
1436 // not found
1437 return null;
1438 }
1439 }
1440
35ca63c1
AG
1441 // Checks to see if the user can manage files or is the owner.
1442 // TODO MDL-33805 - Do not use userid here and move the capability check above.
1443 if (!has_capability('moodle/course:managefiles', $context) && $storedfile->get_userid() != $USER->id) {
1444 return null;
1445 }
1446
a39d7d87 1447 // let us display the author's name instead of itemid (submission id)
b7a5e3d6
DM
1448
1449 if (isset($submissionauthors[$itemid])) {
1450 $topvisiblename = $submissionauthors[$itemid];
1451
1452 } else {
1453
1454 $sql = "SELECT s.id, u.lastname, u.firstname
1455 FROM {workshop_submissions} s
1456 JOIN {user} u ON (s.authorid = u.id)
1457 WHERE s.example = 0 AND s.workshopid = ?";
1458 $params = array($cm->instance);
1459 $rs = $DB->get_recordset_sql($sql, $params);
1460
1461 foreach ($rs as $submissionauthor) {
1462 $title = s(fullname($submissionauthor)); // this is generally not unique...
1463 $submissionauthors[$submissionauthor->id] = $title;
1464 }
1465 $rs->close();
1466
1467 if (!isset($submissionauthors[$itemid])) {
1468 // should not happen
1469 return null;
1470 } else {
1471 $topvisiblename = $submissionauthors[$itemid];
1472 }
1473 }
1474
1475 $urlbase = $CFG->wwwroot . '/pluginfile.php';
a39d7d87
DM
1476 // do not allow manual modification of any files!
1477 return new file_info_stored($browser, $context, $storedfile, $urlbase, $topvisiblename, true, true, false, false);
0dc47fb9
DM
1478 }
1479
88e79d8a
DM
1480 if ($filearea === 'overallfeedback_content' or $filearea === 'overallfeedback_attachment') {
1481
1482 if (!has_capability('mod/workshop:viewallassessments', $context)) {
1483 return null;
1484 }
1485
1486 if (is_null($itemid)) {
1487 // No itemid (assessmentid) passed, display the list of all assessments.
1488 require_once($CFG->dirroot . '/mod/workshop/fileinfolib.php');
1489 return new workshop_file_info_overallfeedback_container($browser, $course, $cm, $context, $areas, $filearea);
1490 }
1491
1492 // Make sure the user can see the particular assessment in separate groups mode.
1493 $gmode = groups_get_activity_groupmode($cm, $course);
1494 if ($gmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
1495 // Check there is at least one common group with both the $USER
1496 // and the submission author.
1497 $sql = "SELECT 'x'
1498 FROM {workshop_submissions} s
1499 JOIN {user} a ON (a.id = s.authorid)
1500 JOIN {groups_members} agm ON (a.id = agm.userid)
1501 JOIN {user} u ON (u.id = ?)
1502 JOIN {groups_members} ugm ON (u.id = ugm.userid)
1503 WHERE s.example = 0 AND s.workshopid = ? AND s.id = ? AND agm.groupid = ugm.groupid";
1504 $params = array($USER->id, $cm->instance, $itemid);
1505 if (!$DB->record_exists_sql($sql, $params)) {
1506 return null;
1507 }
1508 }
1509
1510 // We are inside a particular assessment container.
1511 $filepath = is_null($filepath) ? '/' : $filepath;
1512 $filename = is_null($filename) ? '.' : $filename;
1513
1514 if (!$storedfile = $fs->get_file($context->id, 'mod_workshop', $filearea, $itemid, $filepath, $filename)) {
1515 if ($filepath === '/' and $filename === '.') {
1516 $storedfile = new virtual_root_file($context->id, 'mod_workshop', $filearea, $itemid);
1517 } else {
1518 // Not found
1519 return null;
1520 }
1521 }
1522
1523 // Check to see if the user can manage files or is the owner.
1524 if (!has_capability('moodle/course:managefiles', $context) and $storedfile->get_userid() != $USER->id) {
1525 return null;
1526 }
1527
1528 $urlbase = $CFG->wwwroot . '/pluginfile.php';
1529
1530 // Do not allow manual modification of any files.
1531 return new file_info_stored($browser, $context, $storedfile, $urlbase, $itemid, true, true, false, false);
1532 }
1533
5a49f825 1534 if ($filearea == 'instructauthors' or $filearea == 'instructreviewers' or $filearea == 'conclusion') {
a39d7d87
DM
1535 // always only itemid 0
1536
1537 $filepath = is_null($filepath) ? '/' : $filepath;
1538 $filename = is_null($filename) ? '.' : $filename;
1539
1540 $urlbase = $CFG->wwwroot.'/pluginfile.php';
64f93798 1541 if (!$storedfile = $fs->get_file($context->id, 'mod_workshop', $filearea, 0, $filepath, $filename)) {
a39d7d87 1542 if ($filepath === '/' and $filename === '.') {
64f93798 1543 $storedfile = new virtual_root_file($context->id, 'mod_workshop', $filearea, 0);
a39d7d87
DM
1544 } else {
1545 // not found
1546 return null;
1547 }
1548 }
1549 return new file_info_stored($browser, $context, $storedfile, $urlbase, $areas[$filearea], false, true, true, false);
0dc47fb9 1550 }
0dc47fb9
DM
1551}
1552
6516b9e9
DM
1553////////////////////////////////////////////////////////////////////////////////
1554// Navigation API //
1555////////////////////////////////////////////////////////////////////////////////
1556
39861053
DM
1557/**
1558 * Extends the global navigation tree by adding workshop nodes if there is a relevant content
1559 *
b761e6d9
DM
1560 * This can be called by an AJAX request so do not rely on $PAGE as it might not be set up properly.
1561 *
39861053 1562 * @param navigation_node $navref An object representing the navigation tree node of the workshop module instance
5924db72
PS
1563 * @param stdClass $course
1564 * @param stdClass $module
2addb4ba 1565 * @param cm_info $cm
39861053 1566 */
2addb4ba 1567function workshop_extend_navigation(navigation_node $navref, stdclass $course, stdclass $module, cm_info $cm) {
39861053
DM
1568 global $CFG;
1569
0bbe041b 1570 if (has_capability('mod/workshop:submit', context_module::instance($cm->id))) {
a6855934 1571 $url = new moodle_url('/mod/workshop/submission.php', array('cmid' => $cm->id));
3406acde
SH
1572 $mysubmission = $navref->add(get_string('mysubmission', 'workshop'), $url);
1573 $mysubmission->mainnavonly = true;
39861053
DM
1574 }
1575}
1576
1577/**
1578 * Extends the settings navigation with the Workshop settings
1579
b761e6d9
DM
1580 * This function is called when the context for the page is a workshop module. This is not called by AJAX
1581 * so it is safe to rely on the $PAGE.
39861053
DM
1582 *
1583 * @param settings_navigation $settingsnav {@link settings_navigation}
0b29477b 1584 * @param navigation_node $workshopnode {@link navigation_node}
39861053 1585 */
0b29477b
SH
1586function workshop_extend_settings_navigation(settings_navigation $settingsnav, navigation_node $workshopnode=null) {
1587 global $PAGE;
39861053 1588
39861053
DM
1589 //$workshopobject = $DB->get_record("workshop", array("id" => $PAGE->cm->instance));
1590
b761e6d9 1591 if (has_capability('mod/workshop:editdimensions', $PAGE->cm->context)) {
a6855934 1592 $url = new moodle_url('/mod/workshop/editform.php', array('cmid' => $PAGE->cm->id));
b761e6d9 1593 $workshopnode->add(get_string('editassessmentform', 'workshop'), $url, settings_navigation::TYPE_SETTING);
39861053 1594 }
428a28e1 1595 if (has_capability('mod/workshop:allocate', $PAGE->cm->context)) {
a6855934 1596 $url = new moodle_url('/mod/workshop/allocation.php', array('cmid' => $PAGE->cm->id));
b761e6d9 1597 $workshopnode->add(get_string('allocate', 'workshop'), $url, settings_navigation::TYPE_SETTING);
39861053
DM
1598 }
1599}
b1627a92
DC
1600
1601/**
1602 * Return a list of page types
1603 * @param string $pagetype current page type
1604 * @param stdClass $parentcontext Block's parent context
1605 * @param stdClass $currentcontext Current context of block
1606 */
b38e2e28 1607function workshop_page_type_list($pagetype, $parentcontext, $currentcontext) {
b1627a92
DC
1608 $module_pagetype = array('mod-workshop-*'=>get_string('page-mod-workshop-x', 'workshop'));
1609 return $module_pagetype;
1610}
ac069aeb
DM
1611
1612////////////////////////////////////////////////////////////////////////////////
1613// Calendar API //
1614////////////////////////////////////////////////////////////////////////////////
1615
1616/**
1617 * Updates the calendar events associated to the given workshop
1618 *
1619 * @param stdClass $workshop the workshop instance record
1620 * @param int $cmid course module id
1621 */
1622function workshop_calendar_update(stdClass $workshop, $cmid) {
1623 global $DB;
1624
1625 // get the currently registered events so that we can re-use their ids
1626 $currentevents = $DB->get_records('event', array('modulename' => 'workshop', 'instance' => $workshop->id));
1627
1628 // the common properties for all events
1629 $base = new stdClass();
1630 $base->description = format_module_intro('workshop', $workshop, $cmid, false);
1631 $base->courseid = $workshop->course;
1632 $base->groupid = 0;
1633 $base->userid = 0;
1634 $base->modulename = 'workshop';
1635 $base->eventtype = 'pluginname';
1636 $base->instance = $workshop->id;
1637 $base->visible = instance_is_visible('workshop', $workshop);
1638 $base->timeduration = 0;
1639
1640 if ($workshop->submissionstart) {
1641 $event = clone($base);
1642 $event->name = get_string('submissionstartevent', 'mod_workshop', $workshop->name);
1643 $event->timestart = $workshop->submissionstart;
1644 if ($reusedevent = array_shift($currentevents)) {
1645 $event->id = $reusedevent->id;
1646 } else {
1647 // should not be set but just in case
1648 unset($event->id);
1649 }
de7daa83
DM
1650 // update() will reuse a db record if the id field is set
1651 $eventobj = new calendar_event($event);
1652 $eventobj->update($event, false);
ac069aeb
DM
1653 }
1654
1655 if ($workshop->submissionend) {
1656 $event = clone($base);
1657 $event->name = get_string('submissionendevent', 'mod_workshop', $workshop->name);
1658 $event->timestart = $workshop->submissionend;
1659 if ($reusedevent = array_shift($currentevents)) {
1660 $event->id = $reusedevent->id;
1661 } else {
1662 // should not be set but just in case
1663 unset($event->id);
1664 }
de7daa83
DM
1665 // update() will reuse a db record if the id field is set
1666 $eventobj = new calendar_event($event);
1667 $eventobj->update($event, false);
ac069aeb
DM
1668 }
1669
1670 if ($workshop->assessmentstart) {
1671 $event = clone($base);
1672 $event->name = get_string('assessmentstartevent', 'mod_workshop', $workshop->name);
1673 $event->timestart = $workshop->assessmentstart;
1674 if ($reusedevent = array_shift($currentevents)) {
1675 $event->id = $reusedevent->id;
1676 } else {
1677 // should not be set but just in case
1678 unset($event->id);
1679 }
de7daa83
DM
1680 // update() will reuse a db record if the id field is set
1681 $eventobj = new calendar_event($event);
1682 $eventobj->update($event, false);
ac069aeb
DM
1683 }
1684
1685 if ($workshop->assessmentend) {
1686 $event = clone($base);
1687 $event->name = get_string('assessmentendevent', 'mod_workshop', $workshop->name);
1688 $event->timestart = $workshop->assessmentend;
1689 if ($reusedevent = array_shift($currentevents)) {
1690 $event->id = $reusedevent->id;
1691 } else {
1692 // should not be set but just in case
1693 unset($event->id);
1694 }
de7daa83
DM
1695 // update() will reuse a db record if the id field is set
1696 $eventobj = new calendar_event($event);
1697 $eventobj->update($event, false);
ac069aeb
DM
1698 }
1699
1700 // delete any leftover events
1701 foreach ($currentevents as $oldevent) {
1702 $oldevent = calendar_event::load($oldevent);
1703 $oldevent->delete();
1704 }
1705}