NOMDL workshop_get_participants() though it does not seem to be used by core any...
[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/**
365c2cc2 19 * Library of 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
DM
23 *
24 * @package mod-workshop
25 * @copyright 2009 David Mudrak <david.mudrak@gmail.com>
26 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27 */
28
6867e05d 29defined('MOODLE_INTERNAL') || die();
4eab2e7f 30
365c2cc2
DM
31////////////////////////////////////////////////////////////////////////////////
32// Moodle core API //
33////////////////////////////////////////////////////////////////////////////////
34
b761e6d9
DM
35/**
36 * Returns the information if the module supports a feature
37 *
38 * @see plugin_supports() in lib/moodlelib.php
39 * @param string $feature FEATURE_xx constant for requested feature
40 * @return mixed true if the feature is supported, null if unknown
41 */
42function workshop_supports($feature) {
43 switch($feature) {
44 case FEATURE_GRADE_HAS_GRADE: return true;
45 case FEATURE_GROUPS: return true;
46 case FEATURE_GROUPINGS: return true;
47 case FEATURE_GROUPMEMBERSONLY: return true;
48 case FEATURE_MOD_INTRO: return true;
0fc5a5fa 49 case FEATURE_BACKUP_MOODLE2: return true;
b761e6d9
DM
50 default: return null;
51 }
52}
53
6e309973 54/**
a39d7d87 55 * Saves a new instance of the workshop into the database
6e309973 56 *
4eab2e7f
DM
57 * Given an object containing all the necessary data,
58 * (defined by the form in mod_form.php) this function
a39d7d87 59 * will save a new instance and return the id number
4eab2e7f
DM
60 * of the new instance.
61 *
7a789aa8 62 * @param stdclass $workshop An object from the form in mod_form.php
4eab2e7f
DM
63 * @return int The id of the newly inserted workshop record
64 */
7a789aa8 65function workshop_add_instance(stdclass $workshop) {
454e8dd9
DM
66 global $CFG, $DB;
67 require_once(dirname(__FILE__) . '/locallib.php');
4eab2e7f 68
0d0b7d99
DM
69 $workshop->phase = workshop::PHASE_SETUP;
70 $workshop->timecreated = time();
71 $workshop->timemodified = $workshop->timecreated;
72 $workshop->useexamples = (int)!empty($workshop->useexamples); // unticked checkbox hack
73 $workshop->usepeerassessment = (int)!empty($workshop->usepeerassessment); // unticked checkbox hack
74 $workshop->useselfassessment = (int)!empty($workshop->useselfassessment); // unticked checkbox hack
75 $workshop->latesubmissions = (int)!empty($workshop->latesubmissions); // unticked checkbox hack
c2d2eb6e 76 $workshop->evaluation = 'best';
a39d7d87 77
6516b9e9 78 // insert the new record so we get the id
365c2cc2 79 $workshop->id = $DB->insert_record('workshop', $workshop);
6516b9e9
DM
80
81 // we need to use context now, so we need to make sure all needed info is already in db
365c2cc2
DM
82 $cmid = $workshop->coursemodule;
83 $DB->set_field('course_modules', 'instance', $workshop->id, array('id' => $cmid));
6516b9e9
DM
84 $context = get_context_instance(CONTEXT_MODULE, $cmid);
85
86 // process the custom wysiwyg editors
365c2cc2 87 if ($draftitemid = $workshop->instructauthorseditor['itemid']) {
64f93798 88 $workshop->instructauthors = file_save_draft_area_files($draftitemid, $context->id, 'mod_workshop', 'instructauthors',
365c2cc2
DM
89 0, workshop::instruction_editors_options($context), $workshop->instructauthorseditor['text']);
90 $workshop->instructauthorsformat = $workshop->instructauthorseditor['format'];
6516b9e9
DM
91 }
92
365c2cc2 93 if ($draftitemid = $workshop->instructreviewerseditor['itemid']) {
64f93798 94 $workshop->instructreviewers = file_save_draft_area_files($draftitemid, $context->id, 'mod_workshop', 'instructreviewers',
365c2cc2
DM
95 0, workshop::instruction_editors_options($context), $workshop->instructreviewerseditor['text']);
96 $workshop->instructreviewersformat = $workshop->instructreviewerseditor['format'];
15d12b54
DM
97 }
98
6516b9e9 99 // re-save the record with the replaced URLs in editor fields
365c2cc2 100 $DB->update_record('workshop', $workshop);
6516b9e9 101
365c2cc2
DM
102 // update gradebook item
103 workshop_grade_item_update($workshop);
104
105 return $workshop->id;
a39d7d87 106}
a7c5b918 107
4eab2e7f
DM
108/**
109 * Given an object containing all the necessary data,
110 * (defined by the form in mod_form.php) this function
111 * will update an existing instance with new data.
112 *
7a789aa8 113 * @param stdclass $workshop An object from the form in mod_form.php
6516b9e9 114 * @return bool success
4eab2e7f 115 */
7a789aa8 116function workshop_update_instance(stdclass $workshop) {
6516b9e9
DM
117 global $CFG, $DB;
118 require_once(dirname(__FILE__) . '/locallib.php');
119
0d0b7d99
DM
120 $workshop->timemodified = time();
121 $workshop->id = $workshop->instance;
122 $workshop->useexamples = (int)!empty($workshop->useexamples); // unticked checkbox hack
123 $workshop->usepeerassessment = (int)!empty($workshop->usepeerassessment); // unticked checkbox hack
124 $workshop->useselfassessment = (int)!empty($workshop->useselfassessment); // unticked checkbox hack
125 $workshop->latesubmissions = (int)!empty($workshop->latesubmissions); // unticked checkbox hack
c2d2eb6e 126 $workshop->evaluation = 'best';
4eab2e7f 127
f05c168d
DM
128 // todo - if the grading strategy is being changed, we must replace all aggregated peer grades with nulls
129 // todo - if maximum grades are being changed, we should probably recalculate or invalidate them
130
365c2cc2
DM
131 $DB->update_record('workshop', $workshop);
132 $context = get_context_instance(CONTEXT_MODULE, $workshop->coursemodule);
4eab2e7f 133
6516b9e9 134 // process the custom wysiwyg editors
365c2cc2 135 if ($draftitemid = $workshop->instructauthorseditor['itemid']) {
64f93798 136 $workshop->instructauthors = file_save_draft_area_files($draftitemid, $context->id, 'mod_workshop', 'instructauthors',
365c2cc2
DM
137 0, workshop::instruction_editors_options($context), $workshop->instructauthorseditor['text']);
138 $workshop->instructauthorsformat = $workshop->instructauthorseditor['format'];
6516b9e9
DM
139 }
140
365c2cc2 141 if ($draftitemid = $workshop->instructreviewerseditor['itemid']) {
64f93798 142 $workshop->instructreviewers = file_save_draft_area_files($draftitemid, $context->id, 'mod_workshop', 'instructreviewers',
365c2cc2
DM
143 0, workshop::instruction_editors_options($context), $workshop->instructreviewerseditor['text']);
144 $workshop->instructreviewersformat = $workshop->instructreviewerseditor['format'];
15d12b54
DM
145 }
146
6516b9e9 147 // re-save the record with the replaced URLs in editor fields
365c2cc2
DM
148 $DB->update_record('workshop', $workshop);
149
150 // update gradebook item
151 workshop_grade_item_update($workshop);
152
153 return true;
4eab2e7f
DM
154}
155
4eab2e7f
DM
156/**
157 * Given an ID of an instance of this module,
158 * this function will permanently delete the instance
159 * and any data that depends on it.
160 *
161 * @param int $id Id of the module instance
162 * @return boolean Success/Failure
163 */
164function workshop_delete_instance($id) {
f7d3a965
DM
165 global $CFG, $DB;
166 require_once($CFG->libdir.'/gradelib.php');
167
4eab2e7f
DM
168
169 if (! $workshop = $DB->get_record('workshop', array('id' => $id))) {
170 return false;
171 }
8a1ba8ac
DM
172 // delete all associated aggregations
173 $DB->delete_records('workshop_aggregations', array('workshopid' => $workshop->id));
174 // get the list of ids of all submissions
175 $submissions = $DB->get_records('workshop_submissions', array('workshopid' => $workshop->id), '', 'id');
176 // get the list of all allocated assessments
177 $assessments = $DB->get_records_list('workshop_assessments', 'submissionid', array_keys($submissions), '', 'id');
178 // delete the associated records from the workshop core tables
179 $DB->delete_records_list('workshop_grades', 'assessmentid', array_keys($assessments));
180 $DB->delete_records_list('workshop_assessments', 'id', array_keys($assessments));
181 $DB->delete_records_list('workshop_submissions', 'id', array_keys($submissions));
182 // todo call the static clean-up methods of all available subplugins
183 // ...
184 // finally remove the workshop record itself
185 $DB->delete_records('workshop', array('id' => $workshop->id));
4eab2e7f 186
365c2cc2
DM
187 // gradebook cleanup
188 grade_update('mod/workshop', $workshop->course, 'mod', 'workshop', $workshop->id, 0, null, array('deleted' => true));
189 grade_update('mod/workshop', $workshop->course, 'mod', 'workshop', $workshop->id, 1, null, array('deleted' => true));
190
8a1ba8ac 191 return true;
4eab2e7f
DM
192}
193
4eab2e7f
DM
194/**
195 * Return a small object with summary information about what a
196 * user has done with a given particular instance of this module
197 * Used for user activity reports.
198 * $return->time = the time they did it
199 * $return->info = a short text description
200 *
201 * @return null
202 * @todo Finish documenting this function
203 */
204function workshop_user_outline($course, $user, $mod, $workshop) {
7a789aa8 205 $return = new stdclass();
4eab2e7f
DM
206 $return->time = 0;
207 $return->info = '';
208 return $return;
209}
210
4eab2e7f
DM
211/**
212 * Print a detailed representation of what a user has done with
213 * a given particular instance of this module, for user activity reports.
214 *
215 * @return boolean
216 * @todo Finish documenting this function
217 */
218function workshop_user_complete($course, $user, $mod, $workshop) {
219 return true;
220}
221
4eab2e7f
DM
222/**
223 * Given a course and a time, this module should find recent activity
224 * that has occurred in workshop activities and print it out.
225 * Return true if there was output, or false is there was none.
226 *
227 * @return boolean
228 * @todo Finish documenting this function
229 */
230function workshop_print_recent_activity($course, $isteacher, $timestart) {
231 return false; // True if anything was printed, otherwise false
232}
233
4eab2e7f
DM
234/**
235 * Function to be run periodically according to the moodle cron
236 * This function searches for things that need to be done, such
237 * as sending out mail, toggling flags etc ...
238 *
239 * @return boolean
240 * @todo Finish documenting this function
241 **/
242function workshop_cron () {
243 return true;
244}
245
4eab2e7f 246/**
34860fc1
DM
247 * Returns an array of user ids who are participanting in this workshop
248 *
249 * Participants are either submission authors or reviewers or both.
250 * Authors of the example submissions and their referential assessments
251 * are not returned as the example submission is considered non-user
252 * data for the purpose of workshop backup.
4eab2e7f
DM
253 *
254 * @param int $workshopid ID of an instance of this module
34860fc1 255 * @return array of user ids, empty if there are no participants
4eab2e7f
DM
256 */
257function workshop_get_participants($workshopid) {
34860fc1
DM
258 global $DB;
259
260 $sql = "SELECT u.id
261 FROM {workshop} w
262 JOIN {workshop_submissions} s ON s.workshopid = w.id
263 JOIN {user} u ON s.authorid = u.id
264 WHERE w.id = ? AND s.example = 0
265
266 UNION
267
268 SELECT u.id
269 FROM {workshop} w
270 JOIN {workshop_submissions} s ON s.workshopid = w.id
271 JOIN {workshop_assessments} a ON a.submissionid = s.id
272 JOIN {user} u ON a.reviewerid = u.id
273 WHERE w.id = ? AND (s.example = 0 OR a.weight = 0)";
274
275 $users = array();
276 $rs = $DB->get_recordset_sql($sql, array($workshopid, $workshopid));
277 foreach ($rs as $user) {
278 if (empty($users[$user->id])) {
279 $users[$user->id] = $user;
280 }
281 }
282 $rs->close();
283
284 return $users;
4eab2e7f
DM
285}
286
4eab2e7f 287/**
7a2d0f61 288 * Is a given scale used by the instance of workshop?
4eab2e7f 289 *
7a2d0f61
DM
290 * The function asks all installed grading strategy subplugins. The workshop
291 * core itself does not use scales. Both grade for submission and grade for
292 * assessments do not use scales.
293 *
294 * @param int $workshopid id of workshop instance
295 * @param int $scaleid id of the scale to check
296 * @return bool
4eab2e7f
DM
297 */
298function workshop_scale_used($workshopid, $scaleid) {
7a2d0f61
DM
299 global $CFG; // other files included from here
300
301 $strategies = get_plugin_list('workshopform');
302 foreach ($strategies as $strategy => $strategypath) {
303 $strategylib = $strategypath . '/lib.php';
304 if (is_readable($strategylib)) {
305 require_once($strategylib);
306 } else {
307 throw new coding_exception('the grading forms subplugin must contain library ' . $strategylib);
308 }
309 $classname = 'workshop_' . $strategy . '_strategy';
310 if (method_exists($classname, 'scale_used')) {
311 if (call_user_func_array(array($classname, 'scale_used'), array($scaleid, $workshopid))) {
312 // no need to include any other files - scale is used
313 return true;
314 }
315 }
316 }
4eab2e7f 317
7a2d0f61 318 return false;
4eab2e7f
DM
319}
320
4eab2e7f 321/**
7a2d0f61
DM
322 * Is a given scale used by any instance of workshop?
323 *
324 * The function asks all installed grading strategy subplugins. The workshop
325 * core itself does not use scales. Both grade for submission and grade for
326 * assessments do not use scales.
4eab2e7f 327 *
7a2d0f61
DM
328 * @param int $scaleid id of the scale to check
329 * @return bool
4eab2e7f
DM
330 */
331function workshop_scale_used_anywhere($scaleid) {
7a2d0f61
DM
332 global $CFG; // other files included from here
333
334 $strategies = get_plugin_list('workshopform');
335 foreach ($strategies as $strategy => $strategypath) {
336 $strategylib = $strategypath . '/lib.php';
337 if (is_readable($strategylib)) {
338 require_once($strategylib);
339 } else {
340 throw new coding_exception('the grading forms subplugin must contain library ' . $strategylib);
341 }
342 $classname = 'workshop_' . $strategy . '_strategy';
343 if (method_exists($classname, 'scale_used')) {
344 if (call_user_func(array($classname, 'scale_used'), $scaleid)) {
345 // no need to include any other files - scale is used
346 return true;
347 }
348 }
4eab2e7f 349 }
7a2d0f61
DM
350
351 return false;
4eab2e7f
DM
352}
353
0dc47fb9
DM
354/**
355 * Returns all other caps used in the module
356 *
357 * @return array
358 */
359function workshop_get_extra_capabilities() {
360 return array('moodle/site:accessallgroups');
361}
362
365c2cc2
DM
363////////////////////////////////////////////////////////////////////////////////
364// Gradebook API //
365////////////////////////////////////////////////////////////////////////////////
366
367/**
368 * Creates or updates grade items for the give workshop instance
369 *
370 * Needed by grade_update_mod_grades() in lib/gradelib.php. Also used by
371 * {@link workshop_update_grades()}.
372 *
7a789aa8
DM
373 * @param stdclass $workshop instance object with extra cmidnumber and modname property
374 * @param stdclass $submissiongrades data for the first grade item
375 * @param stdclass $assessmentgrades data for the second grade item
365c2cc2
DM
376 * @return void
377 */
7a789aa8 378function workshop_grade_item_update(stdclass $workshop, $submissiongrades=null, $assessmentgrades=null) {
365c2cc2
DM
379 global $CFG;
380 require_once($CFG->libdir.'/gradelib.php');
381
7a789aa8 382 $a = new stdclass();
365c2cc2
DM
383 $a->workshopname = clean_param($workshop->name, PARAM_NOTAGS);
384
385 $item = array();
386 $item['itemname'] = get_string('gradeitemsubmission', 'workshop', $a);
365c2cc2
DM
387 $item['gradetype'] = GRADE_TYPE_VALUE;
388 $item['grademax'] = $workshop->grade;
389 $item['grademin'] = 0;
390 grade_update('mod/workshop', $workshop->course, 'mod', 'workshop', $workshop->id, 0, $submissiongrades , $item);
391
392 $item = array();
393 $item['itemname'] = get_string('gradeitemassessment', 'workshop', $a);
365c2cc2
DM
394 $item['gradetype'] = GRADE_TYPE_VALUE;
395 $item['grademax'] = $workshop->gradinggrade;
396 $item['grademin'] = 0;
397 grade_update('mod/workshop', $workshop->course, 'mod', 'workshop', $workshop->id, 1, $assessmentgrades, $item);
398}
399
400/**
401 * Update workshop grades in the gradebook
402 *
403 * Needed by grade_update_mod_grades() in lib/gradelib.php
404 *
7a789aa8 405 * @param stdclass $workshop instance object with extra cmidnumber and modname property
365c2cc2
DM
406 * @param int $userid update grade of specific user only, 0 means all participants
407 * @return void
408 */
7a789aa8 409function workshop_update_grades(stdclass $workshop, $userid=0) {
365c2cc2
DM
410 global $CFG, $DB;
411 require_once($CFG->libdir.'/gradelib.php');
412
413 $whereuser = $userid ? ' AND authorid = :userid' : '';
10bc4bce
DM
414 $params = array('workshopid' => $workshop->id, 'userid' => $userid);
415 $sql = 'SELECT authorid, grade, gradeover, gradeoverby, feedbackauthor, feedbackauthorformat, timemodified, timegraded
365c2cc2
DM
416 FROM {workshop_submissions}
417 WHERE workshopid = :workshopid AND example=0' . $whereuser;
10bc4bce
DM
418 $records = $DB->get_records_sql($sql, $params);
419 $submissiongrades = array();
420 foreach ($records as $record) {
7a789aa8 421 $grade = new stdclass();
10bc4bce
DM
422 $grade->userid = $record->authorid;
423 if (!is_null($record->gradeover)) {
424 $grade->rawgrade = grade_floatval($workshop->grade * $record->gradeover / 100);
425 $grade->usermodified = $record->gradeoverby;
426 } else {
427 $grade->rawgrade = grade_floatval($workshop->grade * $record->grade / 100);
428 }
429 $grade->feedback = $record->feedbackauthor;
430 $grade->feedbackformat = $record->feedbackauthorformat;
431 $grade->datesubmitted = $record->timemodified;
432 $grade->dategraded = $record->timegraded;
433 $submissiongrades[$record->authorid] = $grade;
434 }
365c2cc2
DM
435
436 $whereuser = $userid ? ' AND userid = :userid' : '';
10bc4bce
DM
437 $params = array('workshopid' => $workshop->id, 'userid' => $userid);
438 $sql = 'SELECT userid, gradinggrade, timegraded
365c2cc2
DM
439 FROM {workshop_aggregations}
440 WHERE workshopid = :workshopid' . $whereuser;
10bc4bce
DM
441 $records = $DB->get_records_sql($sql, $params);
442 $assessmentgrades = array();
443 foreach ($records as $record) {
7a789aa8 444 $grade = new stdclass();
10bc4bce
DM
445 $grade->userid = $record->userid;
446 $grade->rawgrade = grade_floatval($workshop->gradinggrade * $record->gradinggrade / 100);
447 $grade->dategraded = $record->timegraded;
448 $assessmentgrades[$record->userid] = $grade;
449 }
365c2cc2
DM
450
451 workshop_grade_item_update($workshop, $submissiongrades, $assessmentgrades);
452}
453
b8ead2e6
DM
454////////////////////////////////////////////////////////////////////////////////
455// File API //
456////////////////////////////////////////////////////////////////////////////////
0dc47fb9 457
a39d7d87
DM
458/**
459 * Returns the lists of all browsable file areas within the given module context
460 *
461 * The file area workshop_intro for the activity introduction field is added automatically
64f93798 462 * by {@link file_browser::get_file_info_context_module()}
a39d7d87 463 *
7a789aa8
DM
464 * @param stdclass $course
465 * @param stdclass $cm
466 * @param stdclass $context
a39d7d87
DM
467 * @return array of [(string)filearea] => (string)description
468 */
469function workshop_get_file_areas($course, $cm, $context) {
470 $areas = array();
64f93798
PS
471 $areas['instructauthors'] = get_string('areainstructauthors', 'workshop');
472 $areas['instructreviewers'] = get_string('areainstructreviewers', 'workshop');
473 $areas['submission_content'] = get_string('areasubmissioncontent', 'workshop');
474 $areas['submission_attachment'] = get_string('areasubmissionattachment', 'workshop');
475
a39d7d87
DM
476 return $areas;
477}
478
0dc47fb9 479/**
b8ead2e6 480 * Serves the files from the workshop file areas
0dc47fb9 481 *
b8ead2e6
DM
482 * Apart from module intro (handled by pluginfile.php automatically), workshop files may be
483 * media inserted into submission content (like images) and submission attachments. For these two,
484 * the fileareas workshop_submission_content and workshop_submission_attachment are used.
485 * The access rights to the files are checked here. The user must be either a peer-reviewer
486 * of the submission or have capability ... (todo) to access the submission files.
64f93798 487 * Besides that, areas workshop_instructauthors and mod_workshop instructreviewers contain the media
6516b9e9 488 * embedded using the mod_form.php.
0dc47fb9 489 *
7a789aa8 490 * @param stdclass $course
64f93798 491 * @param stdclass $cm
7a789aa8 492 * @param stdclass $context
0dc47fb9
DM
493 * @param string $filearea
494 * @param array $args
495 * @param bool $forcedownload
6516b9e9 496 * @return void this should never return to the caller
0dc47fb9 497 */
64f93798 498function workshop_pluginfile($course, $cm, $context, $filearea, array $args, $forcedownload) {
0dc47fb9
DM
499 global $DB;
500
64f93798
PS
501 if ($context->contextlevel != CONTEXT_MODULE) {
502 return false;
6516b9e9 503 }
64f93798 504
6516b9e9
DM
505 require_login($course, true, $cm);
506
64f93798 507 if ($filearea === 'instructauthors') {
6516b9e9
DM
508 // submission instructions may contain sensitive data
509 if (!has_any_capability(array('moodle/course:manageactivities', 'mod/workshop:submit'), $context)) {
510 send_file_not_found();
511 }
512
513 array_shift($args); // we do not use itemids here
64f93798
PS
514 $relativepath = implode('/', $args);
515 $fullpath = "/$context->id/mod_workshop/$filearea/0/$relativepath"; // beware, slashes are not used here!
6516b9e9
DM
516
517 $fs = get_file_storage();
518 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
15d12b54
DM
519 send_file_not_found();
520 }
521
522 $lifetime = isset($CFG->filelifetime) ? $CFG->filelifetime : 86400;
523
524 // finally send the file
525 send_stored_file($file, $lifetime, 0);
526 }
527
64f93798 528 if ($filearea === 'instructreviewers') {
15d12b54
DM
529 // submission instructions may contain sensitive data
530 if (!has_any_capability(array('moodle/course:manageactivities', 'mod/workshop:peerassess'), $context)) {
531 send_file_not_found();
532 }
533
534 array_shift($args); // we do not use itemids here
64f93798
PS
535 $relativepath = implode('/', $args);
536 $fullpath = "/$context->id/mod_workshop/$filearea/0/$relativepath"; // beware, slashes are not used here!
15d12b54
DM
537
538 $fs = get_file_storage();
539 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
6516b9e9
DM
540 send_file_not_found();
541 }
542
543 $lifetime = isset($CFG->filelifetime) ? $CFG->filelifetime : 86400;
544
545 // finally send the file
546 send_stored_file($file, $lifetime, 0);
0dc47fb9 547
64f93798 548 } else if ($filearea === 'submission_content' or $filearea === 'submission_attachment') {
18cbfe9b 549 $itemid = (int)array_shift($args);
64f93798 550 if (!$workshop = $DB->get_record('workshop', array('id' => $cm->instance))) {
18cbfe9b
DM
551 return false;
552 }
64f93798 553 if (!$submission = $DB->get_record('workshop_submissions', array('id' => $itemid, 'workshopid' => $workshop->id))) {
18cbfe9b
DM
554 return false;
555 }
556 // TODO now make sure the user is allowed to see the file
557 $fs = get_file_storage();
64f93798
PS
558 $relativepath = implode('/', $args);
559 $fullpath = "/$context->id/mod_workshop/$filearea/$itemid/$relativepath";
18cbfe9b
DM
560 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
561 return false;
562 }
563 // finally send the file
564 // these files are uploaded by students - forcing download for security reasons
a39d7d87 565 send_stored_file($file, 0, 0, true);
0dc47fb9 566 }
18cbfe9b
DM
567
568 return false;
0dc47fb9
DM
569}
570
571/**
572 * File browsing support for workshop file areas
573 *
7a789aa8
DM
574 * @param stdclass $browser
575 * @param stdclass $areas
576 * @param stdclass $course
577 * @param stdclass $cm
578 * @param stdclass $context
0dc47fb9
DM
579 * @param string $filearea
580 * @param int $itemid
581 * @param string $filepath
582 * @param string $filename
7a789aa8 583 * @return stdclass file_info instance or null if not found
0dc47fb9
DM
584 */
585function workshop_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) {
586 global $CFG, $DB;
0dc47fb9 587
a39d7d87 588 if (!has_capability('moodle/course:managefiles', $context)) {
0dc47fb9
DM
589 return null;
590 }
0dc47fb9
DM
591
592 $fs = get_file_storage();
a39d7d87 593
64f93798 594 if ($filearea === 'content' or $filearea === 'attachment') {
a39d7d87
DM
595
596 if (is_null($itemid)) {
597 require_once($CFG->dirroot . '/mod/workshop/fileinfolib.php');
598 return new workshop_file_info_submissions_container($browser, $course, $cm, $context, $areas, $filearea);
599 }
600
601 // we are inside the submission container
602
603 $filepath = is_null($filepath) ? '/' : $filepath;
604 $filename = is_null($filename) ? '.' : $filename;
605
64f93798 606 if (!$storedfile = $fs->get_file($context->id, 'mod_workshop', $filearea, $itemid, $filepath, $filename)) {
a39d7d87 607 if ($filepath === '/' and $filename === '.') {
64f93798 608 $storedfile = new virtual_root_file($context->id, 'mod_workshop', $filearea, $itemid);
a39d7d87
DM
609 } else {
610 // not found
611 return null;
612 }
613 }
614
615 // let us display the author's name instead of itemid (submission id)
fa159f43
DM
616 // todo some sort of caching should happen here
617
235b31c8 618 $sql = 'SELECT s.id, u.lastname, u.firstname
fa159f43 619 FROM {workshop_submissions} s
00aca3c1 620 INNER JOIN {user} u ON (s.authorid = u.id)
235b31c8 621 WHERE s.workshopid = ?';
fa159f43
DM
622 $params = array($cm->instance);
623 $authors = $DB->get_records_sql($sql, $params);
a39d7d87
DM
624 $urlbase = $CFG->wwwroot . '/pluginfile.php';
625 $topvisiblename = fullname($authors[$itemid]);
626 // do not allow manual modification of any files!
627 return new file_info_stored($browser, $context, $storedfile, $urlbase, $topvisiblename, true, true, false, false);
0dc47fb9
DM
628 }
629
64f93798 630 if ($filearea == 'instructauthors' or $filearea == 'instructreviewers') {
a39d7d87
DM
631 // always only itemid 0
632
633 $filepath = is_null($filepath) ? '/' : $filepath;
634 $filename = is_null($filename) ? '.' : $filename;
635
636 $urlbase = $CFG->wwwroot.'/pluginfile.php';
64f93798 637 if (!$storedfile = $fs->get_file($context->id, 'mod_workshop', $filearea, 0, $filepath, $filename)) {
a39d7d87 638 if ($filepath === '/' and $filename === '.') {
64f93798 639 $storedfile = new virtual_root_file($context->id, 'mod_workshop', $filearea, 0);
a39d7d87
DM
640 } else {
641 // not found
642 return null;
643 }
644 }
645 return new file_info_stored($browser, $context, $storedfile, $urlbase, $areas[$filearea], false, true, true, false);
0dc47fb9 646 }
0dc47fb9
DM
647}
648
6516b9e9
DM
649////////////////////////////////////////////////////////////////////////////////
650// Navigation API //
651////////////////////////////////////////////////////////////////////////////////
652
39861053
DM
653/**
654 * Extends the global navigation tree by adding workshop nodes if there is a relevant content
655 *
b761e6d9
DM
656 * This can be called by an AJAX request so do not rely on $PAGE as it might not be set up properly.
657 *
39861053 658 * @param navigation_node $navref An object representing the navigation tree node of the workshop module instance
7a789aa8
DM
659 * @param stdclass $course
660 * @param stdclass $module
661 * @param stdclass $cm
39861053 662 */
7a789aa8 663function workshop_extend_navigation(navigation_node $navref, stdclass $course, stdclass $module, stdclass $cm) {
39861053
DM
664 global $CFG;
665
4f0c2d00 666 if (has_capability('mod/workshop:submit', get_context_instance(CONTEXT_MODULE, $cm->id))) {
a6855934 667 $url = new moodle_url('/mod/workshop/submission.php', array('cmid' => $cm->id));
3406acde
SH
668 $mysubmission = $navref->add(get_string('mysubmission', 'workshop'), $url);
669 $mysubmission->mainnavonly = true;
39861053
DM
670 }
671}
672
673/**
674 * Extends the settings navigation with the Workshop settings
675
b761e6d9
DM
676 * This function is called when the context for the page is a workshop module. This is not called by AJAX
677 * so it is safe to rely on the $PAGE.
39861053
DM
678 *
679 * @param settings_navigation $settingsnav {@link settings_navigation}
0b29477b 680 * @param navigation_node $workshopnode {@link navigation_node}
39861053 681 */
0b29477b
SH
682function workshop_extend_settings_navigation(settings_navigation $settingsnav, navigation_node $workshopnode=null) {
683 global $PAGE;
39861053 684
39861053
DM
685 //$workshopobject = $DB->get_record("workshop", array("id" => $PAGE->cm->instance));
686
b761e6d9 687 if (has_capability('mod/workshop:editdimensions', $PAGE->cm->context)) {
a6855934 688 $url = new moodle_url('/mod/workshop/editform.php', array('cmid' => $PAGE->cm->id));
b761e6d9 689 $workshopnode->add(get_string('editassessmentform', 'workshop'), $url, settings_navigation::TYPE_SETTING);
39861053 690 }
428a28e1 691 if (has_capability('mod/workshop:allocate', $PAGE->cm->context)) {
a6855934 692 $url = new moodle_url('/mod/workshop/allocation.php', array('cmid' => $PAGE->cm->id));
b761e6d9 693 $workshopnode->add(get_string('allocate', 'workshop'), $url, settings_navigation::TYPE_SETTING);
39861053
DM
694 }
695}