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