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