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