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