weekly release 2.3dev
[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 *
7a5f4be0
DM
24 * @package mod
25 * @subpackage workshop
26 * @copyright 2009 David Mudrak <david.mudrak@gmail.com>
27 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4eab2e7f
DM
28 */
29
6867e05d 30defined('MOODLE_INTERNAL') || die();
4eab2e7f 31
365c2cc2
DM
32////////////////////////////////////////////////////////////////////////////////
33// Moodle core API //
34////////////////////////////////////////////////////////////////////////////////
35
b761e6d9
DM
36/**
37 * Returns the information if the module supports a feature
38 *
39 * @see plugin_supports() in lib/moodlelib.php
40 * @param string $feature FEATURE_xx constant for requested feature
41 * @return mixed true if the feature is supported, null if unknown
42 */
43function workshop_supports($feature) {
44 switch($feature) {
45 case FEATURE_GRADE_HAS_GRADE: return true;
46 case FEATURE_GROUPS: return true;
47 case FEATURE_GROUPINGS: return true;
48 case FEATURE_GROUPMEMBERSONLY: return true;
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;
b761e6d9
DM
54 default: return null;
55 }
56}
57
6e309973 58/**
a39d7d87 59 * Saves a new instance of the workshop into the database
6e309973 60 *
4eab2e7f
DM
61 * Given an object containing all the necessary data,
62 * (defined by the form in mod_form.php) this function
a39d7d87 63 * will save a new instance and return the id number
4eab2e7f
DM
64 * of the new instance.
65 *
5924db72 66 * @param stdClass $workshop An object from the form in mod_form.php
4eab2e7f
DM
67 * @return int The id of the newly inserted workshop record
68 */
7a789aa8 69function workshop_add_instance(stdclass $workshop) {
454e8dd9
DM
70 global $CFG, $DB;
71 require_once(dirname(__FILE__) . '/locallib.php');
4eab2e7f 72
0d0b7d99
DM
73 $workshop->phase = workshop::PHASE_SETUP;
74 $workshop->timecreated = time();
75 $workshop->timemodified = $workshop->timecreated;
76 $workshop->useexamples = (int)!empty($workshop->useexamples); // unticked checkbox hack
77 $workshop->usepeerassessment = (int)!empty($workshop->usepeerassessment); // unticked checkbox hack
78 $workshop->useselfassessment = (int)!empty($workshop->useselfassessment); // unticked checkbox hack
79 $workshop->latesubmissions = (int)!empty($workshop->latesubmissions); // unticked checkbox hack
c2d2eb6e 80 $workshop->evaluation = 'best';
a39d7d87 81
6516b9e9 82 // insert the new record so we get the id
365c2cc2 83 $workshop->id = $DB->insert_record('workshop', $workshop);
6516b9e9
DM
84
85 // we need to use context now, so we need to make sure all needed info is already in db
365c2cc2
DM
86 $cmid = $workshop->coursemodule;
87 $DB->set_field('course_modules', 'instance', $workshop->id, array('id' => $cmid));
6516b9e9
DM
88 $context = get_context_instance(CONTEXT_MODULE, $cmid);
89
90 // process the custom wysiwyg editors
365c2cc2 91 if ($draftitemid = $workshop->instructauthorseditor['itemid']) {
64f93798 92 $workshop->instructauthors = file_save_draft_area_files($draftitemid, $context->id, 'mod_workshop', 'instructauthors',
365c2cc2
DM
93 0, workshop::instruction_editors_options($context), $workshop->instructauthorseditor['text']);
94 $workshop->instructauthorsformat = $workshop->instructauthorseditor['format'];
6516b9e9
DM
95 }
96
365c2cc2 97 if ($draftitemid = $workshop->instructreviewerseditor['itemid']) {
64f93798 98 $workshop->instructreviewers = file_save_draft_area_files($draftitemid, $context->id, 'mod_workshop', 'instructreviewers',
365c2cc2
DM
99 0, workshop::instruction_editors_options($context), $workshop->instructreviewerseditor['text']);
100 $workshop->instructreviewersformat = $workshop->instructreviewerseditor['format'];
15d12b54
DM
101 }
102
6516b9e9 103 // re-save the record with the replaced URLs in editor fields
365c2cc2 104 $DB->update_record('workshop', $workshop);
6516b9e9 105
a74cadfa 106 // create gradebook items
365c2cc2 107 workshop_grade_item_update($workshop);
a74cadfa 108 workshop_grade_item_category_update($workshop);
365c2cc2
DM
109
110 return $workshop->id;
a39d7d87 111}
a7c5b918 112
4eab2e7f
DM
113/**
114 * Given an object containing all the necessary data,
115 * (defined by the form in mod_form.php) this function
116 * will update an existing instance with new data.
117 *
5924db72 118 * @param stdClass $workshop An object from the form in mod_form.php
6516b9e9 119 * @return bool success
4eab2e7f 120 */
7a789aa8 121function workshop_update_instance(stdclass $workshop) {
6516b9e9
DM
122 global $CFG, $DB;
123 require_once(dirname(__FILE__) . '/locallib.php');
124
0d0b7d99
DM
125 $workshop->timemodified = time();
126 $workshop->id = $workshop->instance;
127 $workshop->useexamples = (int)!empty($workshop->useexamples); // unticked checkbox hack
128 $workshop->usepeerassessment = (int)!empty($workshop->usepeerassessment); // unticked checkbox hack
129 $workshop->useselfassessment = (int)!empty($workshop->useselfassessment); // unticked checkbox hack
130 $workshop->latesubmissions = (int)!empty($workshop->latesubmissions); // unticked checkbox hack
c2d2eb6e 131 $workshop->evaluation = 'best';
4eab2e7f 132
f05c168d
DM
133 // todo - if the grading strategy is being changed, we must replace all aggregated peer grades with nulls
134 // todo - if maximum grades are being changed, we should probably recalculate or invalidate them
135
365c2cc2
DM
136 $DB->update_record('workshop', $workshop);
137 $context = get_context_instance(CONTEXT_MODULE, $workshop->coursemodule);
4eab2e7f 138
6516b9e9 139 // process the custom wysiwyg editors
365c2cc2 140 if ($draftitemid = $workshop->instructauthorseditor['itemid']) {
64f93798 141 $workshop->instructauthors = file_save_draft_area_files($draftitemid, $context->id, 'mod_workshop', 'instructauthors',
365c2cc2
DM
142 0, workshop::instruction_editors_options($context), $workshop->instructauthorseditor['text']);
143 $workshop->instructauthorsformat = $workshop->instructauthorseditor['format'];
6516b9e9
DM
144 }
145
365c2cc2 146 if ($draftitemid = $workshop->instructreviewerseditor['itemid']) {
64f93798 147 $workshop->instructreviewers = file_save_draft_area_files($draftitemid, $context->id, 'mod_workshop', 'instructreviewers',
365c2cc2
DM
148 0, workshop::instruction_editors_options($context), $workshop->instructreviewerseditor['text']);
149 $workshop->instructreviewersformat = $workshop->instructreviewerseditor['format'];
15d12b54
DM
150 }
151
6516b9e9 152 // re-save the record with the replaced URLs in editor fields
365c2cc2
DM
153 $DB->update_record('workshop', $workshop);
154
a74cadfa 155 // update gradebook items
365c2cc2 156 workshop_grade_item_update($workshop);
a74cadfa 157 workshop_grade_item_category_update($workshop);
365c2cc2
DM
158
159 return true;
4eab2e7f
DM
160}
161
4eab2e7f
DM
162/**
163 * Given an ID of an instance of this module,
164 * this function will permanently delete the instance
165 * and any data that depends on it.
166 *
167 * @param int $id Id of the module instance
168 * @return boolean Success/Failure
169 */
170function workshop_delete_instance($id) {
f7d3a965
DM
171 global $CFG, $DB;
172 require_once($CFG->libdir.'/gradelib.php');
173
4eab2e7f
DM
174 if (! $workshop = $DB->get_record('workshop', array('id' => $id))) {
175 return false;
176 }
346af1a4 177
8a1ba8ac
DM
178 // delete all associated aggregations
179 $DB->delete_records('workshop_aggregations', array('workshopid' => $workshop->id));
346af1a4 180
8a1ba8ac
DM
181 // get the list of ids of all submissions
182 $submissions = $DB->get_records('workshop_submissions', array('workshopid' => $workshop->id), '', 'id');
346af1a4 183
8a1ba8ac
DM
184 // get the list of all allocated assessments
185 $assessments = $DB->get_records_list('workshop_assessments', 'submissionid', array_keys($submissions), '', 'id');
346af1a4 186
8a1ba8ac
DM
187 // delete the associated records from the workshop core tables
188 $DB->delete_records_list('workshop_grades', 'assessmentid', array_keys($assessments));
189 $DB->delete_records_list('workshop_assessments', 'id', array_keys($assessments));
190 $DB->delete_records_list('workshop_submissions', 'id', array_keys($submissions));
346af1a4
DM
191
192 // call the static clean-up methods of all available subplugins
193 $strategies = get_plugin_list('workshopform');
194 foreach ($strategies as $strategy => $path) {
195 require_once($path.'/lib.php');
196 $classname = 'workshop_'.$strategy.'_strategy';
197 call_user_func($classname.'::delete_instance', $workshop->id);
198 }
199
200 $allocators = get_plugin_list('workshopallocation');
201 foreach ($allocators as $allocator => $path) {
202 require_once($path.'/lib.php');
203 $classname = 'workshop_'.$allocator.'_allocator';
204 call_user_func($classname.'::delete_instance', $workshop->id);
205 }
206
207 $evaluators = get_plugin_list('workshopeval');
208 foreach ($evaluators as $evaluator => $path) {
209 require_once($path.'/lib.php');
210 $classname = 'workshop_'.$evaluator.'_evaluation';
211 call_user_func($classname.'::delete_instance', $workshop->id);
212 }
213
8a1ba8ac
DM
214 // finally remove the workshop record itself
215 $DB->delete_records('workshop', array('id' => $workshop->id));
4eab2e7f 216
365c2cc2
DM
217 // gradebook cleanup
218 grade_update('mod/workshop', $workshop->course, 'mod', 'workshop', $workshop->id, 0, null, array('deleted' => true));
219 grade_update('mod/workshop', $workshop->course, 'mod', 'workshop', $workshop->id, 1, null, array('deleted' => true));
220
8a1ba8ac 221 return true;
4eab2e7f
DM
222}
223
4eab2e7f
DM
224/**
225 * Return a small object with summary information about what a
226 * user has done with a given particular instance of this module
227 * Used for user activity reports.
228 * $return->time = the time they did it
229 * $return->info = a short text description
230 *
7a5f4be0 231 * @return stdclass|null
4eab2e7f
DM
232 */
233function workshop_user_outline($course, $user, $mod, $workshop) {
7a5f4be0
DM
234 global $CFG, $DB;
235 require_once($CFG->libdir.'/gradelib.php');
236
237 $grades = grade_get_grades($course->id, 'mod', 'workshop', $workshop->id, $user->id);
238
239 $submissiongrade = null;
240 $assessmentgrade = null;
241
242 $info = '';
243 $time = 0;
244
245 if (!empty($grades->items[0]->grades)) {
246 $submissiongrade = reset($grades->items[0]->grades);
247 $info .= get_string('submissiongrade', 'workshop') . ': ' . $submissiongrade->str_long_grade . html_writer::empty_tag('br');
248 $time = max($time, $submissiongrade->dategraded);
249 }
250 if (!empty($grades->items[1]->grades)) {
251 $assessmentgrade = reset($grades->items[1]->grades);
252 $info .= get_string('gradinggrade', 'workshop') . ': ' . $assessmentgrade->str_long_grade;
253 $time = max($time, $assessmentgrade->dategraded);
254 }
255
256 if (!empty($info) and !empty($time)) {
257 $return = new stdclass();
258 $return->time = $time;
259 $return->info = $info;
260 return $return;
261 }
262
263 return null;
4eab2e7f
DM
264}
265
4eab2e7f
DM
266/**
267 * Print a detailed representation of what a user has done with
268 * a given particular instance of this module, for user activity reports.
269 *
7a5f4be0 270 * @return string HTML
4eab2e7f
DM
271 */
272function workshop_user_complete($course, $user, $mod, $workshop) {
7a5f4be0
DM
273 global $CFG, $DB, $OUTPUT;
274 require_once(dirname(__FILE__).'/locallib.php');
275 require_once($CFG->libdir.'/gradelib.php');
276
277 $workshop = new workshop($workshop, $mod, $course);
278 $grades = grade_get_grades($course->id, 'mod', 'workshop', $workshop->id, $user->id);
279
280 if (!empty($grades->items[0]->grades)) {
281 $submissiongrade = reset($grades->items[0]->grades);
282 $info = get_string('submissiongrade', 'workshop') . ': ' . $submissiongrade->str_long_grade;
283 echo html_writer::tag('li', $info, array('class'=>'submissiongrade'));
284 }
285 if (!empty($grades->items[1]->grades)) {
286 $assessmentgrade = reset($grades->items[1]->grades);
287 $info = get_string('gradinggrade', 'workshop') . ': ' . $assessmentgrade->str_long_grade;
288 echo html_writer::tag('li', $info, array('class'=>'gradinggrade'));
289 }
290
291 if (has_capability('mod/workshop:viewallsubmissions', $workshop->context)) {
292 if ($submission = $workshop->get_submission_by_author($user->id)) {
293 $title = format_string($submission->title);
294 $url = $workshop->submission_url($submission->id);
295 $link = html_writer::link($url, $title);
296 $info = get_string('submission', 'workshop').': '.$link;
297 echo html_writer::tag('li', $info, array('class'=>'submission'));
298 }
299 }
300
301 if (has_capability('mod/workshop:viewallassessments', $workshop->context)) {
302 if ($assessments = $workshop->get_assessments_by_reviewer($user->id)) {
303 foreach ($assessments as $assessment) {
304 $a = new stdclass();
305 $a->submissionurl = $workshop->submission_url($assessment->submissionid)->out();
306 $a->assessmenturl = $workshop->assess_url($assessment->id)->out();
307 $a->submissiontitle = s($assessment->submissiontitle);
308 echo html_writer::tag('li', get_string('assessmentofsubmission', 'workshop', $a));
309 }
310 }
311 }
4eab2e7f
DM
312}
313
4eab2e7f
DM
314/**
315 * Given a course and a time, this module should find recent activity
316 * that has occurred in workshop activities and print it out.
317 * Return true if there was output, or false is there was none.
318 *
5924db72 319 * @param stdClass $course
7a5f4be0
DM
320 * @param bool $viewfullnames
321 * @param int $timestart
4eab2e7f 322 * @return boolean
4eab2e7f 323 */
7a5f4be0
DM
324function workshop_print_recent_activity($course, $viewfullnames, $timestart) {
325 global $CFG, $USER, $DB, $OUTPUT;
326
327 $sql = "SELECT s.id AS submissionid, s.title AS submissiontitle, s.timemodified AS submissionmodified,
328 author.id AS authorid, author.lastname AS authorlastname, author.firstname AS authorfirstname,
329 a.id AS assessmentid, a.timemodified AS assessmentmodified,
330 reviewer.id AS reviewerid, reviewer.lastname AS reviewerlastname, reviewer.firstname AS reviewerfirstname,
331 cm.id AS cmid
332 FROM {workshop} w
333 INNER JOIN {course_modules} cm ON cm.instance = w.id
334 INNER JOIN {modules} md ON md.id = cm.module
335 INNER JOIN {workshop_submissions} s ON s.workshopid = w.id
336 INNER JOIN {user} author ON s.authorid = author.id
337 LEFT JOIN {workshop_assessments} a ON a.submissionid = s.id
338 LEFT JOIN {user} reviewer ON a.reviewerid = reviewer.id
339 WHERE cm.course = ?
340 AND md.name = 'workshop'
341 AND s.example = 0
342 AND (s.timemodified > ? OR a.timemodified > ?)";
343
344 $rs = $DB->get_recordset_sql($sql, array($course->id, $timestart, $timestart));
345
f20edd52 346 $modinfo = get_fast_modinfo($course); // reference needed because we might load the groups
7a5f4be0
DM
347
348 $submissions = array(); // recent submissions indexed by submission id
349 $assessments = array(); // recent assessments indexed by assessment id
350 $users = array();
351
352 foreach ($rs as $activity) {
353 if (!array_key_exists($activity->cmid, $modinfo->cms)) {
354 // this should not happen but just in case
355 continue;
356 }
357
358 $cm = $modinfo->cms[$activity->cmid];
359 if (!$cm->uservisible) {
360 continue;
361 }
362
363 if ($viewfullnames) {
364 // remember all user names we can use later
365 if (empty($users[$activity->authorid])) {
366 $u = new stdclass();
367 $u->lastname = $activity->authorlastname;
368 $u->firstname = $activity->authorfirstname;
369 $users[$activity->authorid] = $u;
370 }
371 if ($activity->reviewerid and empty($users[$activity->reviewerid])) {
372 $u = new stdclass();
373 $u->lastname = $activity->reviewerlastname;
374 $u->firstname = $activity->reviewerfirstname;
375 $users[$activity->reviewerid] = $u;
376 }
377 }
378
379 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
380 $groupmode = groups_get_activity_groupmode($cm, $course);
381
382 if ($activity->submissionmodified > $timestart and empty($submissions[$activity->submissionid])) {
383 $s = new stdclass();
384 $s->title = $activity->submissiontitle;
385 $s->authorid = $activity->authorid;
386 $s->timemodified = $activity->submissionmodified;
387 $s->cmid = $activity->cmid;
388 if (has_capability('mod/workshop:viewauthornames', $context)) {
389 $s->authornamevisible = true;
390 } else {
391 $s->authornamevisible = false;
392 }
393
394 // the following do-while wrapper allows to break from deeply nested if-statements
395 do {
396 if ($s->authorid === $USER->id) {
397 // own submissions always visible
398 $submissions[$activity->submissionid] = $s;
399 break;
400 }
401
402 if (has_capability('mod/workshop:viewallsubmissions', $context)) {
403 if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
404 if (isguestuser()) {
405 // shortcut - guest user does not belong into any group
406 break;
407 }
408
409 if (is_null($modinfo->groups)) {
410 $modinfo->groups = groups_get_user_groups($course->id); // load all my groups and cache it in modinfo
411 }
412
413 // this might be slow - show only submissions by users who share group with me in this cm
414 if (empty($modinfo->groups[$cm->id])) {
415 break;
416 }
417 $authorsgroups = groups_get_all_groups($course->id, $s->authorid, $cm->groupingid);
418 if (is_array($authorsgroups)) {
419 $authorsgroups = array_keys($authorsgroups);
420 $intersect = array_intersect($authorsgroups, $modinfo->groups[$cm->id]);
421 if (empty($intersect)) {
422 break;
423 } else {
424 // can see all submissions and shares a group with the author
425 $submissions[$activity->submissionid] = $s;
426 break;
427 }
428 }
429
430 } else {
431 // can see all submissions from all groups
432 $submissions[$activity->submissionid] = $s;
433 }
434 }
435 } while (0);
436 }
437
438 if ($activity->assessmentmodified > $timestart and empty($assessments[$activity->assessmentid])) {
439 $a = new stdclass();
440 $a->submissionid = $activity->submissionid;
441 $a->submissiontitle = $activity->submissiontitle;
442 $a->reviewerid = $activity->reviewerid;
443 $a->timemodified = $activity->assessmentmodified;
444 $a->cmid = $activity->cmid;
445 if (has_capability('mod/workshop:viewreviewernames', $context)) {
446 $a->reviewernamevisible = true;
447 } else {
448 $a->reviewernamevisible = false;
449 }
450
451 // the following do-while wrapper allows to break from deeply nested if-statements
452 do {
453 if ($a->reviewerid === $USER->id) {
454 // own assessments always visible
455 $assessments[$activity->assessmentid] = $a;
456 break;
457 }
458
459 if (has_capability('mod/workshop:viewallassessments', $context)) {
460 if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
461 if (isguestuser()) {
462 // shortcut - guest user does not belong into any group
463 break;
464 }
465
466 if (is_null($modinfo->groups)) {
467 $modinfo->groups = groups_get_user_groups($course->id); // load all my groups and cache it in modinfo
468 }
469
470 // this might be slow - show only submissions by users who share group with me in this cm
471 if (empty($modinfo->groups[$cm->id])) {
472 break;
473 }
474 $reviewersgroups = groups_get_all_groups($course->id, $a->reviewerid, $cm->groupingid);
475 if (is_array($reviewersgroups)) {
476 $reviewersgroups = array_keys($reviewersgroups);
477 $intersect = array_intersect($reviewersgroups, $modinfo->groups[$cm->id]);
478 if (empty($intersect)) {
479 break;
480 } else {
481 // can see all assessments and shares a group with the reviewer
482 $assessments[$activity->assessmentid] = $a;
483 break;
484 }
485 }
486
487 } else {
488 // can see all assessments from all groups
489 $assessments[$activity->assessmentid] = $a;
490 }
491 }
492 } while (0);
493 }
494 }
495 $rs->close();
496
497 $shown = false;
498
499 if (!empty($submissions)) {
500 $shown = true;
501 echo $OUTPUT->heading(get_string('recentsubmissions', 'workshop'), 3);
502 foreach ($submissions as $id => $submission) {
503 $link = new moodle_url('/mod/workshop/submission.php', array('id'=>$id, 'cmid'=>$submission->cmid));
504 if ($viewfullnames and $submission->authornamevisible) {
505 $author = $users[$submission->authorid];
506 } else {
507 $author = null;
508 }
509 print_recent_activity_note($submission->timemodified, $author, $submission->title, $link->out(), false, $viewfullnames);
510 }
511 }
512
513 if (!empty($assessments)) {
514 $shown = true;
515 echo $OUTPUT->heading(get_string('recentassessments', 'workshop'), 3);
516 foreach ($assessments as $id => $assessment) {
517 $link = new moodle_url('/mod/workshop/assessment.php', array('asid' => $id));
518 if ($viewfullnames and $assessment->reviewernamevisible) {
519 $reviewer = $users[$assessment->reviewerid];
520 } else {
521 $reviewer = null;
522 }
523 print_recent_activity_note($assessment->timemodified, $reviewer, $assessment->submissiontitle, $link->out(), false, $viewfullnames);
524 }
525 }
526
527 if ($shown) {
528 return true;
529 }
530
531 return false;
532}
533
534/**
535 * Returns all activity in course workshops since a given time
536 *
537 * @param array $activities sequentially indexed array of objects
538 * @param int $index
539 * @param int $timestart
540 * @param int $courseid
541 * @param int $cmid
542 * @param int $userid defaults to 0
543 * @param int $groupid defaults to 0
544 * @return void adds items into $activities and increases $index
545 */
546function workshop_get_recent_mod_activity(&$activities, &$index, $timestart, $courseid, $cmid, $userid=0, $groupid=0) {
547 global $CFG, $COURSE, $USER, $DB;
548
549 if ($COURSE->id == $courseid) {
550 $course = $COURSE;
551 } else {
552 $course = $DB->get_record('course', array('id'=>$courseid));
553 }
554
f20edd52 555 $modinfo = get_fast_modinfo($course);
7a5f4be0
DM
556
557 $cm = $modinfo->cms[$cmid];
558
559 $params = array();
560 if ($userid) {
561 $userselect = "AND (author.id = :authorid OR reviewer.id = :reviewerid)";
562 $params['authorid'] = $userid;
563 $params['reviewerid'] = $userid;
564 } else {
565 $userselect = "";
566 }
567
568 if ($groupid) {
569 $groupselect = "AND (authorgroupmembership.groupid = :authorgroupid OR reviewergroupmembership.groupid = :reviewergroupid)";
570 $groupjoin = "LEFT JOIN {groups_members} authorgroupmembership ON authorgroumembership.userid = author.id
571 LEFT JOIN {groups_members} reviewergroupmembership ON reviewergroumembership.userid = reviewer.id";
572 $params['authorgroupid'] = $groupid;
573 $params['reviewergroupid'] = $groupid;
574 } else {
575 $groupselect = "";
576 $groupjoin = "";
577 }
578
579 $params['cminstance'] = $cm->instance;
580 $params['submissionmodified'] = $timestart;
581 $params['assessmentmodified'] = $timestart;
582
583 $sql = "SELECT s.id AS submissionid, s.title AS submissiontitle, s.timemodified AS submissionmodified,
584 author.id AS authorid, author.lastname AS authorlastname, author.firstname AS authorfirstname,
585 author.picture AS authorpicture, author.imagealt AS authorimagealt, author.email AS authoremail,
586 a.id AS assessmentid, a.timemodified AS assessmentmodified,
587 reviewer.id AS reviewerid, reviewer.lastname AS reviewerlastname, reviewer.firstname AS reviewerfirstname,
588 reviewer.picture AS reviewerpicture, reviewer.imagealt AS reviewerimagealt, reviewer.email AS revieweremail
589 FROM {workshop_submissions} s
590 INNER JOIN {workshop} w ON s.workshopid = w.id
591 INNER JOIN {user} author ON s.authorid = author.id
592 LEFT JOIN {workshop_assessments} a ON a.submissionid = s.id
593 LEFT JOIN {user} reviewer ON a.reviewerid = reviewer.id
594 $groupjoin
595 WHERE w.id = :cminstance
596 AND s.example = 0
597 $userselect $groupselect
598 AND (s.timemodified > :submissionmodified OR a.timemodified > :assessmentmodified)
599 ORDER BY s.timemodified ASC, a.timemodified ASC";
600
601 $rs = $DB->get_recordset_sql($sql, $params);
602
603 $groupmode = groups_get_activity_groupmode($cm, $course);
604 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
605 $grader = has_capability('moodle/grade:viewall', $context);
606 $accessallgroups = has_capability('moodle/site:accessallgroups', $context);
607 $viewfullnames = has_capability('moodle/site:viewfullnames', $context);
608 $viewauthors = has_capability('mod/workshop:viewauthornames', $context);
609 $viewreviewers = has_capability('mod/workshop:viewreviewernames', $context);
610
611 if (is_null($modinfo->groups)) {
612 $modinfo->groups = groups_get_user_groups($course->id); // load all my groups and cache it in modinfo
613 }
614
615 $submissions = array(); // recent submissions indexed by submission id
616 $assessments = array(); // recent assessments indexed by assessment id
617 $users = array();
618
619 foreach ($rs as $activity) {
620
621 if ($viewfullnames) {
622 // remember all user names we can use later
623 if (empty($users[$activity->authorid])) {
624 $u = new stdclass();
625 $u->id = $activity->authorid;
626 $u->lastname = $activity->authorlastname;
627 $u->firstname = $activity->authorfirstname;
628 $u->picture = $activity->authorpicture;
629 $u->imagealt = $activity->authorimagealt;
630 $u->email = $activity->authoremail;
631 $users[$activity->authorid] = $u;
632 }
633 if ($activity->reviewerid and empty($users[$activity->reviewerid])) {
634 $u = new stdclass();
635 $u->id = $activity->reviewerid;
636 $u->lastname = $activity->reviewerlastname;
637 $u->firstname = $activity->reviewerfirstname;
638 $u->picture = $activity->reviewerpicture;
639 $u->imagealt = $activity->reviewerimagealt;
640 $u->email = $activity->revieweremail;
641 $users[$activity->reviewerid] = $u;
642 }
643 }
644
645 if ($activity->submissionmodified > $timestart and empty($submissions[$activity->submissionid])) {
646 $s = new stdclass();
647 $s->id = $activity->submissionid;
648 $s->title = $activity->submissiontitle;
649 $s->authorid = $activity->authorid;
650 $s->timemodified = $activity->submissionmodified;
651 if (has_capability('mod/workshop:viewauthornames', $context)) {
652 $s->authornamevisible = true;
653 } else {
654 $s->authornamevisible = false;
655 }
656
657 // the following do-while wrapper allows to break from deeply nested if-statements
658 do {
659 if ($s->authorid === $USER->id) {
660 // own submissions always visible
661 $submissions[$activity->submissionid] = $s;
662 break;
663 }
664
665 if (has_capability('mod/workshop:viewallsubmissions', $context)) {
666 if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
667 if (isguestuser()) {
668 // shortcut - guest user does not belong into any group
669 break;
670 }
671
672 // this might be slow - show only submissions by users who share group with me in this cm
673 if (empty($modinfo->groups[$cm->id])) {
674 break;
675 }
676 $authorsgroups = groups_get_all_groups($course->id, $s->authorid, $cm->groupingid);
677 if (is_array($authorsgroups)) {
678 $authorsgroups = array_keys($authorsgroups);
679 $intersect = array_intersect($authorsgroups, $modinfo->groups[$cm->id]);
680 if (empty($intersect)) {
681 break;
682 } else {
683 // can see all submissions and shares a group with the author
684 $submissions[$activity->submissionid] = $s;
685 break;
686 }
687 }
688
689 } else {
690 // can see all submissions from all groups
691 $submissions[$activity->submissionid] = $s;
692 }
693 }
694 } while (0);
695 }
696
697 if ($activity->assessmentmodified > $timestart and empty($assessments[$activity->assessmentid])) {
698 $a = new stdclass();
699 $a->id = $activity->assessmentid;
700 $a->submissionid = $activity->submissionid;
701 $a->submissiontitle = $activity->submissiontitle;
702 $a->reviewerid = $activity->reviewerid;
703 $a->timemodified = $activity->assessmentmodified;
704 if (has_capability('mod/workshop:viewreviewernames', $context)) {
705 $a->reviewernamevisible = true;
706 } else {
707 $a->reviewernamevisible = false;
708 }
709
710 // the following do-while wrapper allows to break from deeply nested if-statements
711 do {
712 if ($a->reviewerid === $USER->id) {
713 // own assessments always visible
714 $assessments[$activity->assessmentid] = $a;
715 break;
716 }
717
718 if (has_capability('mod/workshop:viewallassessments', $context)) {
719 if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
720 if (isguestuser()) {
721 // shortcut - guest user does not belong into any group
722 break;
723 }
724
725 // this might be slow - show only submissions by users who share group with me in this cm
726 if (empty($modinfo->groups[$cm->id])) {
727 break;
728 }
729 $reviewersgroups = groups_get_all_groups($course->id, $a->reviewerid, $cm->groupingid);
730 if (is_array($reviewersgroups)) {
731 $reviewersgroups = array_keys($reviewersgroups);
732 $intersect = array_intersect($reviewersgroups, $modinfo->groups[$cm->id]);
733 if (empty($intersect)) {
734 break;
735 } else {
736 // can see all assessments and shares a group with the reviewer
737 $assessments[$activity->assessmentid] = $a;
738 break;
739 }
740 }
741
742 } else {
743 // can see all assessments from all groups
744 $assessments[$activity->assessmentid] = $a;
745 }
746 }
747 } while (0);
748 }
749 }
750 $rs->close();
751
752 $workshopname = format_string($cm->name, true);
753
754 if ($grader) {
755 require_once($CFG->libdir.'/gradelib.php');
756 $grades = grade_get_grades($courseid, 'mod', 'workshop', $cm->instance, array_keys($users));
757 }
758
759 foreach ($submissions as $submission) {
760 $tmpactivity = new stdclass();
761 $tmpactivity->type = 'workshop';
762 $tmpactivity->cmid = $cm->id;
763 $tmpactivity->name = $workshopname;
764 $tmpactivity->sectionnum = $cm->sectionnum;
765 $tmpactivity->timestamp = $submission->timemodified;
766 $tmpactivity->subtype = 'submission';
767 $tmpactivity->content = $submission;
768 if ($grader) {
769 $tmpactivity->grade = $grades->items[0]->grades[$submission->authorid]->str_long_grade;
770 }
771 if ($submission->authornamevisible and !empty($users[$submission->authorid])) {
772 $tmpactivity->user = $users[$submission->authorid];
773 }
774 $activities[$index++] = $tmpactivity;
775 }
776
777 foreach ($assessments as $assessment) {
778 $tmpactivity = new stdclass();
779 $tmpactivity->type = 'workshop';
780 $tmpactivity->cmid = $cm->id;
781 $tmpactivity->name = $workshopname;
782 $tmpactivity->sectionnum = $cm->sectionnum;
783 $tmpactivity->timestamp = $assessment->timemodified;
784 $tmpactivity->subtype = 'assessment';
785 $tmpactivity->content = $assessment;
786 if ($grader) {
787 $tmpactivity->grade = $grades->items[1]->grades[$assessment->reviewerid]->str_long_grade;
788 }
789 if ($assessment->reviewernamevisible and !empty($users[$assessment->reviewerid])) {
790 $tmpactivity->user = $users[$assessment->reviewerid];
791 }
792 $activities[$index++] = $tmpactivity;
793 }
794}
795
796/**
797 * Print single activity item prepared by {@see workshop_get_recent_mod_activity()}
798 */
799function workshop_print_recent_mod_activity($activity, $courseid, $detail, $modnames, $viewfullnames) {
800 global $CFG, $OUTPUT;
801
802 if (!empty($activity->user)) {
803 echo html_writer::tag('div', $OUTPUT->user_picture($activity->user, array('courseid'=>$courseid)),
804 array('style' => 'float: left; padding: 7px;'));
805 }
806
807 if ($activity->subtype == 'submission') {
808 echo html_writer::start_tag('div', array('class'=>'submission', 'style'=>'padding: 7px; float:left;'));
809
810 if ($detail) {
811 echo html_writer::start_tag('h4', array('class'=>'workshop'));
812 $url = new moodle_url('/mod/workshop/view.php', array('id'=>$activity->cmid));
813 $name = s($activity->name);
814 echo html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('icon', $activity->type), 'class'=>'icon', 'alt'=>$name));
815 echo ' ' . $modnames[$activity->type];
816 echo html_writer::link($url, $name, array('class'=>'name', 'style'=>'margin-left: 5px'));
817 echo html_writer::end_tag('h4');
818 }
819
820 echo html_writer::start_tag('div', array('class'=>'title'));
54fe8470 821 $url = new moodle_url('/mod/workshop/submission.php', array('cmid'=>$activity->cmid, 'id'=>$activity->content->id));
7a5f4be0
DM
822 $name = s($activity->content->title);
823 echo html_writer::tag('strong', html_writer::link($url, $name));
824 echo html_writer::end_tag('div');
825
826 if (!empty($activity->user)) {
827 echo html_writer::start_tag('div', array('class'=>'user'));
828 $url = new moodle_url('/user/view.php', array('id'=>$activity->user->id, 'course'=>$courseid));
829 $name = fullname($activity->user);
830 $link = html_writer::link($url, $name);
831 echo get_string('submissionby', 'workshop', $link);
832 echo ' - '.userdate($activity->timestamp);
833 echo html_writer::end_tag('div');
834 } else {
835 echo html_writer::start_tag('div', array('class'=>'anonymous'));
836 echo get_string('submission', 'workshop');
837 echo ' - '.userdate($activity->timestamp);
838 echo html_writer::end_tag('div');
839 }
840
841 echo html_writer::end_tag('div');
842 }
843
844 if ($activity->subtype == 'assessment') {
845 echo html_writer::start_tag('div', array('class'=>'assessment', 'style'=>'padding: 7px; float:left;'));
846
847 if ($detail) {
848 echo html_writer::start_tag('h4', array('class'=>'workshop'));
849 $url = new moodle_url('/mod/workshop/view.php', array('id'=>$activity->cmid));
850 $name = s($activity->name);
851 echo html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('icon', $activity->type), 'class'=>'icon', 'alt'=>$name));
852 echo ' ' . $modnames[$activity->type];
853 echo html_writer::link($url, $name, array('class'=>'name', 'style'=>'margin-left: 5px'));
854 echo html_writer::end_tag('h4');
855 }
856
857 echo html_writer::start_tag('div', array('class'=>'title'));
54fe8470 858 $url = new moodle_url('/mod/workshop/assessment.php', array('asid'=>$activity->content->id));
7a5f4be0
DM
859 $name = s($activity->content->submissiontitle);
860 echo html_writer::tag('em', html_writer::link($url, $name));
861 echo html_writer::end_tag('div');
862
863 if (!empty($activity->user)) {
864 echo html_writer::start_tag('div', array('class'=>'user'));
865 $url = new moodle_url('/user/view.php', array('id'=>$activity->user->id, 'course'=>$courseid));
866 $name = fullname($activity->user);
867 $link = html_writer::link($url, $name);
38504a44 868 echo get_string('assessmentbyfullname', 'workshop', $link);
7a5f4be0
DM
869 echo ' - '.userdate($activity->timestamp);
870 echo html_writer::end_tag('div');
871 } else {
872 echo html_writer::start_tag('div', array('class'=>'anonymous'));
38504a44 873 echo get_string('assessment', 'workshop');
7a5f4be0
DM
874 echo ' - '.userdate($activity->timestamp);
875 echo html_writer::end_tag('div');
876 }
877
878 echo html_writer::end_tag('div');
879 }
880
881 echo html_writer::empty_tag('br', array('style'=>'clear:both'));
4eab2e7f
DM
882}
883
4eab2e7f
DM
884/**
885 * Function to be run periodically according to the moodle cron
886 * This function searches for things that need to be done, such
887 * as sending out mail, toggling flags etc ...
888 *
889 * @return boolean
890 * @todo Finish documenting this function
891 **/
892function workshop_cron () {
893 return true;
894}
895
4eab2e7f 896/**
34860fc1
DM
897 * Returns an array of user ids who are participanting in this workshop
898 *
899 * Participants are either submission authors or reviewers or both.
900 * Authors of the example submissions and their referential assessments
901 * are not returned as the example submission is considered non-user
902 * data for the purpose of workshop backup.
4eab2e7f 903 *
2b04c41c
SH
904 * @todo: deprecated - to be deleted in 2.2
905 *
4eab2e7f 906 * @param int $workshopid ID of an instance of this module
34860fc1 907 * @return array of user ids, empty if there are no participants
4eab2e7f
DM
908 */
909function workshop_get_participants($workshopid) {
34860fc1
DM
910 global $DB;
911
912 $sql = "SELECT u.id
913 FROM {workshop} w
914 JOIN {workshop_submissions} s ON s.workshopid = w.id
915 JOIN {user} u ON s.authorid = u.id
916 WHERE w.id = ? AND s.example = 0
917
918 UNION
919
920 SELECT u.id
921 FROM {workshop} w
922 JOIN {workshop_submissions} s ON s.workshopid = w.id
923 JOIN {workshop_assessments} a ON a.submissionid = s.id
924 JOIN {user} u ON a.reviewerid = u.id
925 WHERE w.id = ? AND (s.example = 0 OR a.weight = 0)";
926
927 $users = array();
928 $rs = $DB->get_recordset_sql($sql, array($workshopid, $workshopid));
929 foreach ($rs as $user) {
930 if (empty($users[$user->id])) {
931 $users[$user->id] = $user;
932 }
933 }
934 $rs->close();
935
936 return $users;
4eab2e7f
DM
937}
938
4eab2e7f 939/**
7a2d0f61 940 * Is a given scale used by the instance of workshop?
4eab2e7f 941 *
7a2d0f61
DM
942 * The function asks all installed grading strategy subplugins. The workshop
943 * core itself does not use scales. Both grade for submission and grade for
944 * assessments do not use scales.
945 *
946 * @param int $workshopid id of workshop instance
947 * @param int $scaleid id of the scale to check
948 * @return bool
4eab2e7f
DM
949 */
950function workshop_scale_used($workshopid, $scaleid) {
7a2d0f61
DM
951 global $CFG; // other files included from here
952
953 $strategies = get_plugin_list('workshopform');
954 foreach ($strategies as $strategy => $strategypath) {
955 $strategylib = $strategypath . '/lib.php';
956 if (is_readable($strategylib)) {
957 require_once($strategylib);
958 } else {
959 throw new coding_exception('the grading forms subplugin must contain library ' . $strategylib);
960 }
961 $classname = 'workshop_' . $strategy . '_strategy';
962 if (method_exists($classname, 'scale_used')) {
963 if (call_user_func_array(array($classname, 'scale_used'), array($scaleid, $workshopid))) {
964 // no need to include any other files - scale is used
965 return true;
966 }
967 }
968 }
4eab2e7f 969
7a2d0f61 970 return false;
4eab2e7f
DM
971}
972
4eab2e7f 973/**
7a2d0f61
DM
974 * Is a given scale used by any instance of workshop?
975 *
976 * The function asks all installed grading strategy subplugins. The workshop
977 * core itself does not use scales. Both grade for submission and grade for
978 * assessments do not use scales.
4eab2e7f 979 *
7a2d0f61
DM
980 * @param int $scaleid id of the scale to check
981 * @return bool
4eab2e7f
DM
982 */
983function workshop_scale_used_anywhere($scaleid) {
7a2d0f61
DM
984 global $CFG; // other files included from here
985
986 $strategies = get_plugin_list('workshopform');
987 foreach ($strategies as $strategy => $strategypath) {
988 $strategylib = $strategypath . '/lib.php';
989 if (is_readable($strategylib)) {
990 require_once($strategylib);
991 } else {
992 throw new coding_exception('the grading forms subplugin must contain library ' . $strategylib);
993 }
994 $classname = 'workshop_' . $strategy . '_strategy';
995 if (method_exists($classname, 'scale_used')) {
996 if (call_user_func(array($classname, 'scale_used'), $scaleid)) {
997 // no need to include any other files - scale is used
998 return true;
999 }
1000 }
4eab2e7f 1001 }
7a2d0f61
DM
1002
1003 return false;
4eab2e7f
DM
1004}
1005
0dc47fb9
DM
1006/**
1007 * Returns all other caps used in the module
1008 *
1009 * @return array
1010 */
1011function workshop_get_extra_capabilities() {
1012 return array('moodle/site:accessallgroups');
1013}
1014
365c2cc2
DM
1015////////////////////////////////////////////////////////////////////////////////
1016// Gradebook API //
1017////////////////////////////////////////////////////////////////////////////////
1018
1019/**
1020 * Creates or updates grade items for the give workshop instance
1021 *
1022 * Needed by grade_update_mod_grades() in lib/gradelib.php. Also used by
1023 * {@link workshop_update_grades()}.
1024 *
5924db72
PS
1025 * @param stdClass $workshop instance object with extra cmidnumber and modname property
1026 * @param stdClass $submissiongrades data for the first grade item
1027 * @param stdClass $assessmentgrades data for the second grade item
365c2cc2
DM
1028 * @return void
1029 */
7a789aa8 1030function workshop_grade_item_update(stdclass $workshop, $submissiongrades=null, $assessmentgrades=null) {
365c2cc2
DM
1031 global $CFG;
1032 require_once($CFG->libdir.'/gradelib.php');
1033
7a789aa8 1034 $a = new stdclass();
365c2cc2
DM
1035 $a->workshopname = clean_param($workshop->name, PARAM_NOTAGS);
1036
1037 $item = array();
1038 $item['itemname'] = get_string('gradeitemsubmission', 'workshop', $a);
365c2cc2
DM
1039 $item['gradetype'] = GRADE_TYPE_VALUE;
1040 $item['grademax'] = $workshop->grade;
1041 $item['grademin'] = 0;
1042 grade_update('mod/workshop', $workshop->course, 'mod', 'workshop', $workshop->id, 0, $submissiongrades , $item);
1043
1044 $item = array();
1045 $item['itemname'] = get_string('gradeitemassessment', 'workshop', $a);
365c2cc2
DM
1046 $item['gradetype'] = GRADE_TYPE_VALUE;
1047 $item['grademax'] = $workshop->gradinggrade;
1048 $item['grademin'] = 0;
1049 grade_update('mod/workshop', $workshop->course, 'mod', 'workshop', $workshop->id, 1, $assessmentgrades, $item);
1050}
1051
1052/**
1053 * Update workshop grades in the gradebook
1054 *
1055 * Needed by grade_update_mod_grades() in lib/gradelib.php
1056 *
a153c9f2 1057 * @category grade
5924db72 1058 * @param stdClass $workshop instance object with extra cmidnumber and modname property
365c2cc2
DM
1059 * @param int $userid update grade of specific user only, 0 means all participants
1060 * @return void
1061 */
7a789aa8 1062function workshop_update_grades(stdclass $workshop, $userid=0) {
365c2cc2
DM
1063 global $CFG, $DB;
1064 require_once($CFG->libdir.'/gradelib.php');
1065
1066 $whereuser = $userid ? ' AND authorid = :userid' : '';
10bc4bce
DM
1067 $params = array('workshopid' => $workshop->id, 'userid' => $userid);
1068 $sql = 'SELECT authorid, grade, gradeover, gradeoverby, feedbackauthor, feedbackauthorformat, timemodified, timegraded
365c2cc2
DM
1069 FROM {workshop_submissions}
1070 WHERE workshopid = :workshopid AND example=0' . $whereuser;
10bc4bce
DM
1071 $records = $DB->get_records_sql($sql, $params);
1072 $submissiongrades = array();
1073 foreach ($records as $record) {
7a789aa8 1074 $grade = new stdclass();
10bc4bce
DM
1075 $grade->userid = $record->authorid;
1076 if (!is_null($record->gradeover)) {
1077 $grade->rawgrade = grade_floatval($workshop->grade * $record->gradeover / 100);
1078 $grade->usermodified = $record->gradeoverby;
1079 } else {
1080 $grade->rawgrade = grade_floatval($workshop->grade * $record->grade / 100);
1081 }
1082 $grade->feedback = $record->feedbackauthor;
1083 $grade->feedbackformat = $record->feedbackauthorformat;
1084 $grade->datesubmitted = $record->timemodified;
1085 $grade->dategraded = $record->timegraded;
1086 $submissiongrades[$record->authorid] = $grade;
1087 }
365c2cc2
DM
1088
1089 $whereuser = $userid ? ' AND userid = :userid' : '';
10bc4bce
DM
1090 $params = array('workshopid' => $workshop->id, 'userid' => $userid);
1091 $sql = 'SELECT userid, gradinggrade, timegraded
365c2cc2
DM
1092 FROM {workshop_aggregations}
1093 WHERE workshopid = :workshopid' . $whereuser;
10bc4bce
DM
1094 $records = $DB->get_records_sql($sql, $params);
1095 $assessmentgrades = array();
1096 foreach ($records as $record) {
7a789aa8 1097 $grade = new stdclass();
10bc4bce
DM
1098 $grade->userid = $record->userid;
1099 $grade->rawgrade = grade_floatval($workshop->gradinggrade * $record->gradinggrade / 100);
1100 $grade->dategraded = $record->timegraded;
1101 $assessmentgrades[$record->userid] = $grade;
1102 }
365c2cc2
DM
1103
1104 workshop_grade_item_update($workshop, $submissiongrades, $assessmentgrades);
1105}
1106
a74cadfa
DM
1107/**
1108 * Update the grade items categories if they are changed via mod_form.php
1109 *
1110 * We must do it manually here in the workshop module because modedit supports only
1111 * single grade item while we use two.
1112 *
1113 * @param stdClass $workshop An object from the form in mod_form.php
1114 */
1115function workshop_grade_item_category_update($workshop) {
1116
1117 $gradeitems = grade_item::fetch_all(array(
1118 'itemtype' => 'mod',
1119 'itemmodule' => 'workshop',
1120 'iteminstance' => $workshop->id,
1121 'courseid' => $workshop->course));
1122
1123 if (!empty($gradeitems)) {
1124 foreach ($gradeitems as $gradeitem) {
1125 if ($gradeitem->itemnumber == 0) {
1126 if ($gradeitem->categoryid != $workshop->gradecategory) {
1127 $gradeitem->set_parent($workshop->gradecategory);
1128 }
1129 } else if ($gradeitem->itemnumber == 1) {
1130 if ($gradeitem->categoryid != $workshop->gradinggradecategory) {
1131 $gradeitem->set_parent($workshop->gradinggradecategory);
1132 }
1133 }
1134 }
1135 }
1136}
1137
b8ead2e6
DM
1138////////////////////////////////////////////////////////////////////////////////
1139// File API //
1140////////////////////////////////////////////////////////////////////////////////
0dc47fb9 1141
a39d7d87
DM
1142/**
1143 * Returns the lists of all browsable file areas within the given module context
1144 *
1145 * The file area workshop_intro for the activity introduction field is added automatically
64f93798 1146 * by {@link file_browser::get_file_info_context_module()}
a39d7d87 1147 *
d2b7803e
DC
1148 * @package mod_workshop
1149 * @category files
1150 *
5924db72
PS
1151 * @param stdClass $course
1152 * @param stdClass $cm
1153 * @param stdClass $context
a39d7d87
DM
1154 * @return array of [(string)filearea] => (string)description
1155 */
1156function workshop_get_file_areas($course, $cm, $context) {
1157 $areas = array();
64f93798
PS
1158 $areas['instructauthors'] = get_string('areainstructauthors', 'workshop');
1159 $areas['instructreviewers'] = get_string('areainstructreviewers', 'workshop');
1160 $areas['submission_content'] = get_string('areasubmissioncontent', 'workshop');
1161 $areas['submission_attachment'] = get_string('areasubmissionattachment', 'workshop');
1162
a39d7d87
DM
1163 return $areas;
1164}
1165
0dc47fb9 1166/**
b8ead2e6 1167 * Serves the files from the workshop file areas
0dc47fb9 1168 *
b8ead2e6
DM
1169 * Apart from module intro (handled by pluginfile.php automatically), workshop files may be
1170 * media inserted into submission content (like images) and submission attachments. For these two,
1171 * the fileareas workshop_submission_content and workshop_submission_attachment are used.
1172 * The access rights to the files are checked here. The user must be either a peer-reviewer
1173 * of the submission or have capability ... (todo) to access the submission files.
64f93798 1174 * Besides that, areas workshop_instructauthors and mod_workshop instructreviewers contain the media
6516b9e9 1175 * embedded using the mod_form.php.
0dc47fb9 1176 *
d2b7803e
DC
1177 * @package mod_workshop
1178 * @category files
1179 *
5924db72
PS
1180 * @param stdClass $course
1181 * @param stdClass $cm
1182 * @param stdClass $context
0dc47fb9
DM
1183 * @param string $filearea
1184 * @param array $args
1185 * @param bool $forcedownload
6516b9e9 1186 * @return void this should never return to the caller
0dc47fb9 1187 */
64f93798 1188function workshop_pluginfile($course, $cm, $context, $filearea, array $args, $forcedownload) {
1c5765dd 1189 global $DB, $CFG;
0dc47fb9 1190
64f93798
PS
1191 if ($context->contextlevel != CONTEXT_MODULE) {
1192 return false;
6516b9e9 1193 }
64f93798 1194
6516b9e9
DM
1195 require_login($course, true, $cm);
1196
64f93798 1197 if ($filearea === 'instructauthors') {
6516b9e9
DM
1198 // submission instructions may contain sensitive data
1199 if (!has_any_capability(array('moodle/course:manageactivities', 'mod/workshop:submit'), $context)) {
1200 send_file_not_found();
1201 }
1202
1203 array_shift($args); // we do not use itemids here
64f93798
PS
1204 $relativepath = implode('/', $args);
1205 $fullpath = "/$context->id/mod_workshop/$filearea/0/$relativepath"; // beware, slashes are not used here!
6516b9e9
DM
1206
1207 $fs = get_file_storage();
1208 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
15d12b54
DM
1209 send_file_not_found();
1210 }
1211
1212 $lifetime = isset($CFG->filelifetime) ? $CFG->filelifetime : 86400;
1213
1214 // finally send the file
1215 send_stored_file($file, $lifetime, 0);
1216 }
1217
64f93798 1218 if ($filearea === 'instructreviewers') {
15d12b54
DM
1219 // submission instructions may contain sensitive data
1220 if (!has_any_capability(array('moodle/course:manageactivities', 'mod/workshop:peerassess'), $context)) {
1221 send_file_not_found();
1222 }
1223
1224 array_shift($args); // we do not use itemids here
64f93798 1225 $relativepath = implode('/', $args);
7a5f4be0 1226 $fullpath = "/$context->id/mod_workshop/$filearea/0/$relativepath";
15d12b54
DM
1227
1228 $fs = get_file_storage();
1229 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
6516b9e9
DM
1230 send_file_not_found();
1231 }
1232
1233 $lifetime = isset($CFG->filelifetime) ? $CFG->filelifetime : 86400;
1234
1235 // finally send the file
1236 send_stored_file($file, $lifetime, 0);
0dc47fb9 1237
64f93798 1238 } else if ($filearea === 'submission_content' or $filearea === 'submission_attachment') {
18cbfe9b 1239 $itemid = (int)array_shift($args);
64f93798 1240 if (!$workshop = $DB->get_record('workshop', array('id' => $cm->instance))) {
18cbfe9b
DM
1241 return false;
1242 }
64f93798 1243 if (!$submission = $DB->get_record('workshop_submissions', array('id' => $itemid, 'workshopid' => $workshop->id))) {
18cbfe9b
DM
1244 return false;
1245 }
1246 // TODO now make sure the user is allowed to see the file
1247 $fs = get_file_storage();
64f93798
PS
1248 $relativepath = implode('/', $args);
1249 $fullpath = "/$context->id/mod_workshop/$filearea/$itemid/$relativepath";
18cbfe9b
DM
1250 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
1251 return false;
1252 }
1253 // finally send the file
1254 // these files are uploaded by students - forcing download for security reasons
a39d7d87 1255 send_stored_file($file, 0, 0, true);
0dc47fb9 1256 }
18cbfe9b
DM
1257
1258 return false;
0dc47fb9
DM
1259}
1260
1261/**
1262 * File browsing support for workshop file areas
1263 *
d2b7803e
DC
1264 * @package mod_workshop
1265 * @category files
1266 *
1267 * @param file_browser $browser
1268 * @param array $areas
5924db72
PS
1269 * @param stdClass $course
1270 * @param stdClass $cm
1271 * @param stdClass $context
0dc47fb9
DM
1272 * @param string $filearea
1273 * @param int $itemid
1274 * @param string $filepath
1275 * @param string $filename
d2b7803e 1276 * @return file_info instance or null if not found
0dc47fb9
DM
1277 */
1278function workshop_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) {
1279 global $CFG, $DB;
0dc47fb9 1280
a39d7d87 1281 if (!has_capability('moodle/course:managefiles', $context)) {
0dc47fb9
DM
1282 return null;
1283 }
0dc47fb9
DM
1284
1285 $fs = get_file_storage();
a39d7d87 1286
64f93798 1287 if ($filearea === 'content' or $filearea === 'attachment') {
a39d7d87
DM
1288
1289 if (is_null($itemid)) {
1290 require_once($CFG->dirroot . '/mod/workshop/fileinfolib.php');
1291 return new workshop_file_info_submissions_container($browser, $course, $cm, $context, $areas, $filearea);
1292 }
1293
1294 // we are inside the submission container
1295
1296 $filepath = is_null($filepath) ? '/' : $filepath;
1297 $filename = is_null($filename) ? '.' : $filename;
1298
64f93798 1299 if (!$storedfile = $fs->get_file($context->id, 'mod_workshop', $filearea, $itemid, $filepath, $filename)) {
a39d7d87 1300 if ($filepath === '/' and $filename === '.') {
64f93798 1301 $storedfile = new virtual_root_file($context->id, 'mod_workshop', $filearea, $itemid);
a39d7d87
DM
1302 } else {
1303 // not found
1304 return null;
1305 }
1306 }
1307
1308 // let us display the author's name instead of itemid (submission id)
fa159f43
DM
1309 // todo some sort of caching should happen here
1310
7a5f4be0 1311 $sql = "SELECT s.id, u.lastname, u.firstname
fa159f43 1312 FROM {workshop_submissions} s
00aca3c1 1313 INNER JOIN {user} u ON (s.authorid = u.id)
7a5f4be0 1314 WHERE s.workshopid = ?";
fa159f43
DM
1315 $params = array($cm->instance);
1316 $authors = $DB->get_records_sql($sql, $params);
a39d7d87
DM
1317 $urlbase = $CFG->wwwroot . '/pluginfile.php';
1318 $topvisiblename = fullname($authors[$itemid]);
1319 // do not allow manual modification of any files!
1320 return new file_info_stored($browser, $context, $storedfile, $urlbase, $topvisiblename, true, true, false, false);
0dc47fb9
DM
1321 }
1322
64f93798 1323 if ($filearea == 'instructauthors' or $filearea == 'instructreviewers') {
a39d7d87
DM
1324 // always only itemid 0
1325
1326 $filepath = is_null($filepath) ? '/' : $filepath;
1327 $filename = is_null($filename) ? '.' : $filename;
1328
1329 $urlbase = $CFG->wwwroot.'/pluginfile.php';
64f93798 1330 if (!$storedfile = $fs->get_file($context->id, 'mod_workshop', $filearea, 0, $filepath, $filename)) {
a39d7d87 1331 if ($filepath === '/' and $filename === '.') {
64f93798 1332 $storedfile = new virtual_root_file($context->id, 'mod_workshop', $filearea, 0);
a39d7d87
DM
1333 } else {
1334 // not found
1335 return null;
1336 }
1337 }
1338 return new file_info_stored($browser, $context, $storedfile, $urlbase, $areas[$filearea], false, true, true, false);
0dc47fb9 1339 }
0dc47fb9
DM
1340}
1341
6516b9e9
DM
1342////////////////////////////////////////////////////////////////////////////////
1343// Navigation API //
1344////////////////////////////////////////////////////////////////////////////////
1345
39861053
DM
1346/**
1347 * Extends the global navigation tree by adding workshop nodes if there is a relevant content
1348 *
b761e6d9
DM
1349 * This can be called by an AJAX request so do not rely on $PAGE as it might not be set up properly.
1350 *
39861053 1351 * @param navigation_node $navref An object representing the navigation tree node of the workshop module instance
5924db72
PS
1352 * @param stdClass $course
1353 * @param stdClass $module
2addb4ba 1354 * @param cm_info $cm
39861053 1355 */
2addb4ba 1356function workshop_extend_navigation(navigation_node $navref, stdclass $course, stdclass $module, cm_info $cm) {
39861053
DM
1357 global $CFG;
1358
4f0c2d00 1359 if (has_capability('mod/workshop:submit', get_context_instance(CONTEXT_MODULE, $cm->id))) {
a6855934 1360 $url = new moodle_url('/mod/workshop/submission.php', array('cmid' => $cm->id));
3406acde
SH
1361 $mysubmission = $navref->add(get_string('mysubmission', 'workshop'), $url);
1362 $mysubmission->mainnavonly = true;
39861053
DM
1363 }
1364}
1365
1366/**
1367 * Extends the settings navigation with the Workshop settings
1368
b761e6d9
DM
1369 * This function is called when the context for the page is a workshop module. This is not called by AJAX
1370 * so it is safe to rely on the $PAGE.
39861053
DM
1371 *
1372 * @param settings_navigation $settingsnav {@link settings_navigation}
0b29477b 1373 * @param navigation_node $workshopnode {@link navigation_node}
39861053 1374 */
0b29477b
SH
1375function workshop_extend_settings_navigation(settings_navigation $settingsnav, navigation_node $workshopnode=null) {
1376 global $PAGE;
39861053 1377
39861053
DM
1378 //$workshopobject = $DB->get_record("workshop", array("id" => $PAGE->cm->instance));
1379
b761e6d9 1380 if (has_capability('mod/workshop:editdimensions', $PAGE->cm->context)) {
a6855934 1381 $url = new moodle_url('/mod/workshop/editform.php', array('cmid' => $PAGE->cm->id));
b761e6d9 1382 $workshopnode->add(get_string('editassessmentform', 'workshop'), $url, settings_navigation::TYPE_SETTING);
39861053 1383 }
428a28e1 1384 if (has_capability('mod/workshop:allocate', $PAGE->cm->context)) {
a6855934 1385 $url = new moodle_url('/mod/workshop/allocation.php', array('cmid' => $PAGE->cm->id));
b761e6d9 1386 $workshopnode->add(get_string('allocate', 'workshop'), $url, settings_navigation::TYPE_SETTING);
39861053
DM
1387 }
1388}
b1627a92
DC
1389
1390/**
1391 * Return a list of page types
1392 * @param string $pagetype current page type
1393 * @param stdClass $parentcontext Block's parent context
1394 * @param stdClass $currentcontext Current context of block
1395 */
b38e2e28 1396function workshop_page_type_list($pagetype, $parentcontext, $currentcontext) {
b1627a92
DC
1397 $module_pagetype = array('mod-workshop-*'=>get_string('page-mod-workshop-x', 'workshop'));
1398 return $module_pagetype;
1399}