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