3 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
19 * Library of workshop module functions needed by Moodle core and other subsystems
21 * All the functions neeeded by Moodle core, gradebook, file subsystem etc
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
29 defined('MOODLE_INTERNAL') ||
die();
31 require_once($CFG->dirroot
. '/calendar/lib.php');
33 define('WORKSHOP_EVENT_TYPE_SUBMISSION_OPEN', 'opensubmission');
34 define('WORKSHOP_EVENT_TYPE_SUBMISSION_CLOSE', 'closesubmission');
35 define('WORKSHOP_EVENT_TYPE_ASSESSMENT_OPEN', 'openassessment');
36 define('WORKSHOP_EVENT_TYPE_ASSESSMENT_CLOSE', 'closeassessment');
37 define('WORKSHOP_SUBMISSION_TYPE_DISABLED', 0);
38 define('WORKSHOP_SUBMISSION_TYPE_AVAILABLE', 1);
39 define('WORKSHOP_SUBMISSION_TYPE_REQUIRED', 2);
41 ////////////////////////////////////////////////////////////////////////////////
43 ////////////////////////////////////////////////////////////////////////////////
46 * Returns the information if the module supports a feature
48 * @see plugin_supports() in lib/moodlelib.php
49 * @param string $feature FEATURE_xx constant for requested feature
50 * @return mixed true if the feature is supported, null if unknown
52 function workshop_supports($feature) {
54 case FEATURE_GRADE_HAS_GRADE
: return true;
55 case FEATURE_GROUPS
: return true;
56 case FEATURE_GROUPINGS
: return true;
57 case FEATURE_MOD_INTRO
: return true;
58 case FEATURE_BACKUP_MOODLE2
: return true;
59 case FEATURE_COMPLETION_TRACKS_VIEWS
:
61 case FEATURE_SHOW_DESCRIPTION
: return true;
62 case FEATURE_PLAGIARISM
: return true;
68 * Saves a new instance of the workshop into the database
70 * Given an object containing all the necessary data,
71 * (defined by the form in mod_form.php) this function
72 * will save a new instance and return the id number
73 * of the new instance.
75 * @param stdClass $workshop An object from the form in mod_form.php
76 * @return int The id of the newly inserted workshop record
78 function workshop_add_instance(stdclass
$workshop) {
80 require_once(__DIR__
. '/locallib.php');
82 $workshop->phase
= workshop
::PHASE_SETUP
;
83 $workshop->timecreated
= time();
84 $workshop->timemodified
= $workshop->timecreated
;
85 $workshop->useexamples
= (int)!empty($workshop->useexamples
);
86 $workshop->usepeerassessment
= 1;
87 $workshop->useselfassessment
= (int)!empty($workshop->useselfassessment
);
88 $workshop->latesubmissions
= (int)!empty($workshop->latesubmissions
);
89 $workshop->phaseswitchassessment
= (int)!empty($workshop->phaseswitchassessment
);
90 $workshop->evaluation
= 'best';
92 if (isset($workshop->gradinggradepass
)) {
93 $workshop->gradinggradepass
= (float)unformat_float($workshop->gradinggradepass
);
96 if (isset($workshop->submissiongradepass
)) {
97 $workshop->submissiongradepass
= (float)unformat_float($workshop->submissiongradepass
);
100 if (isset($workshop->submissionfiletypes
)) {
101 $filetypesutil = new \core_form\filetypes_util
();
102 $submissionfiletypes = $filetypesutil->normalize_file_types($workshop->submissionfiletypes
);
103 $workshop->submissionfiletypes
= implode(' ', $submissionfiletypes);
106 if (isset($workshop->overallfeedbackfiletypes
)) {
107 $filetypesutil = new \core_form\filetypes_util
();
108 $overallfeedbackfiletypes = $filetypesutil->normalize_file_types($workshop->overallfeedbackfiletypes
);
109 $workshop->overallfeedbackfiletypes
= implode(' ', $overallfeedbackfiletypes);
112 // insert the new record so we get the id
113 $workshop->id
= $DB->insert_record('workshop', $workshop);
115 // we need to use context now, so we need to make sure all needed info is already in db
116 $cmid = $workshop->coursemodule
;
117 $DB->set_field('course_modules', 'instance', $workshop->id
, array('id' => $cmid));
118 $context = context_module
::instance($cmid);
120 // process the custom wysiwyg editors
121 if ($draftitemid = $workshop->instructauthorseditor
['itemid']) {
122 $workshop->instructauthors
= file_save_draft_area_files($draftitemid, $context->id
, 'mod_workshop', 'instructauthors',
123 0, workshop
::instruction_editors_options($context), $workshop->instructauthorseditor
['text']);
124 $workshop->instructauthorsformat
= $workshop->instructauthorseditor
['format'];
127 if ($draftitemid = $workshop->instructreviewerseditor
['itemid']) {
128 $workshop->instructreviewers
= file_save_draft_area_files($draftitemid, $context->id
, 'mod_workshop', 'instructreviewers',
129 0, workshop
::instruction_editors_options($context), $workshop->instructreviewerseditor
['text']);
130 $workshop->instructreviewersformat
= $workshop->instructreviewerseditor
['format'];
133 if ($draftitemid = $workshop->conclusioneditor
['itemid']) {
134 $workshop->conclusion
= file_save_draft_area_files($draftitemid, $context->id
, 'mod_workshop', 'conclusion',
135 0, workshop
::instruction_editors_options($context), $workshop->conclusioneditor
['text']);
136 $workshop->conclusionformat
= $workshop->conclusioneditor
['format'];
139 // re-save the record with the replaced URLs in editor fields
140 $DB->update_record('workshop', $workshop);
142 // create gradebook items
143 workshop_grade_item_update($workshop);
144 workshop_grade_item_category_update($workshop);
146 // create calendar events
147 workshop_calendar_update($workshop, $workshop->coursemodule
);
148 if (!empty($workshop->completionexpected
)) {
149 \core_completion\api
::update_completion_date_event($cmid, 'workshop', $workshop->id
, $workshop->completionexpected
);
152 return $workshop->id
;
156 * Given an object containing all the necessary data,
157 * (defined by the form in mod_form.php) this function
158 * will update an existing instance with new data.
160 * @param stdClass $workshop An object from the form in mod_form.php
161 * @return bool success
163 function workshop_update_instance(stdclass
$workshop) {
165 require_once(__DIR__
. '/locallib.php');
167 $workshop->timemodified
= time();
168 $workshop->id
= $workshop->instance
;
169 $workshop->useexamples
= (int)!empty($workshop->useexamples
);
170 $workshop->usepeerassessment
= 1;
171 $workshop->useselfassessment
= (int)!empty($workshop->useselfassessment
);
172 $workshop->latesubmissions
= (int)!empty($workshop->latesubmissions
);
173 $workshop->phaseswitchassessment
= (int)!empty($workshop->phaseswitchassessment
);
175 if (isset($workshop->gradinggradepass
)) {
176 $workshop->gradinggradepass
= (float)unformat_float($workshop->gradinggradepass
);
179 if (isset($workshop->submissiongradepass
)) {
180 $workshop->submissiongradepass
= (float)unformat_float($workshop->submissiongradepass
);
183 if (isset($workshop->submissionfiletypes
)) {
184 $filetypesutil = new \core_form\filetypes_util
();
185 $submissionfiletypes = $filetypesutil->normalize_file_types($workshop->submissionfiletypes
);
186 $workshop->submissionfiletypes
= implode(' ', $submissionfiletypes);
189 if (isset($workshop->overallfeedbackfiletypes
)) {
190 $filetypesutil = new \core_form\filetypes_util
();
191 $overallfeedbackfiletypes = $filetypesutil->normalize_file_types($workshop->overallfeedbackfiletypes
);
192 $workshop->overallfeedbackfiletypes
= implode(' ', $overallfeedbackfiletypes);
195 // todo - if the grading strategy is being changed, we may want to replace all aggregated peer grades with nulls
197 $DB->update_record('workshop', $workshop);
198 $context = context_module
::instance($workshop->coursemodule
);
200 // process the custom wysiwyg editors
201 if ($draftitemid = $workshop->instructauthorseditor
['itemid']) {
202 $workshop->instructauthors
= file_save_draft_area_files($draftitemid, $context->id
, 'mod_workshop', 'instructauthors',
203 0, workshop
::instruction_editors_options($context), $workshop->instructauthorseditor
['text']);
204 $workshop->instructauthorsformat
= $workshop->instructauthorseditor
['format'];
207 if ($draftitemid = $workshop->instructreviewerseditor
['itemid']) {
208 $workshop->instructreviewers
= file_save_draft_area_files($draftitemid, $context->id
, 'mod_workshop', 'instructreviewers',
209 0, workshop
::instruction_editors_options($context), $workshop->instructreviewerseditor
['text']);
210 $workshop->instructreviewersformat
= $workshop->instructreviewerseditor
['format'];
213 if ($draftitemid = $workshop->conclusioneditor
['itemid']) {
214 $workshop->conclusion
= file_save_draft_area_files($draftitemid, $context->id
, 'mod_workshop', 'conclusion',
215 0, workshop
::instruction_editors_options($context), $workshop->conclusioneditor
['text']);
216 $workshop->conclusionformat
= $workshop->conclusioneditor
['format'];
219 // re-save the record with the replaced URLs in editor fields
220 $DB->update_record('workshop', $workshop);
222 // update gradebook items
223 workshop_grade_item_update($workshop);
224 workshop_grade_item_category_update($workshop);
226 // update calendar events
227 workshop_calendar_update($workshop, $workshop->coursemodule
);
228 $completionexpected = (!empty($workshop->completionexpected
)) ?
$workshop->completionexpected
: null;
229 \core_completion\api
::update_completion_date_event($workshop->coursemodule
, 'workshop', $workshop->id
, $completionexpected);
235 * Given an ID of an instance of this module,
236 * this function will permanently delete the instance
237 * and any data that depends on it.
239 * @param int $id Id of the module instance
240 * @return boolean Success/Failure
242 function workshop_delete_instance($id) {
244 require_once($CFG->libdir
.'/gradelib.php');
246 if (! $workshop = $DB->get_record('workshop', array('id' => $id))) {
250 // delete all associated aggregations
251 $DB->delete_records('workshop_aggregations', array('workshopid' => $workshop->id
));
253 // get the list of ids of all submissions
254 $submissions = $DB->get_records('workshop_submissions', array('workshopid' => $workshop->id
), '', 'id');
256 // get the list of all allocated assessments
257 $assessments = $DB->get_records_list('workshop_assessments', 'submissionid', array_keys($submissions), '', 'id');
259 // delete the associated records from the workshop core tables
260 $DB->delete_records_list('workshop_grades', 'assessmentid', array_keys($assessments));
261 $DB->delete_records_list('workshop_assessments', 'id', array_keys($assessments));
262 $DB->delete_records_list('workshop_submissions', 'id', array_keys($submissions));
264 // call the static clean-up methods of all available subplugins
265 $strategies = core_component
::get_plugin_list('workshopform');
266 foreach ($strategies as $strategy => $path) {
267 require_once($path.'/lib.php');
268 $classname = 'workshop_'.$strategy.'_strategy';
269 call_user_func($classname.'::delete_instance', $workshop->id
);
272 $allocators = core_component
::get_plugin_list('workshopallocation');
273 foreach ($allocators as $allocator => $path) {
274 require_once($path.'/lib.php');
275 $classname = 'workshop_'.$allocator.'_allocator';
276 call_user_func($classname.'::delete_instance', $workshop->id
);
279 $evaluators = core_component
::get_plugin_list('workshopeval');
280 foreach ($evaluators as $evaluator => $path) {
281 require_once($path.'/lib.php');
282 $classname = 'workshop_'.$evaluator.'_evaluation';
283 call_user_func($classname.'::delete_instance', $workshop->id
);
286 // delete the calendar events
287 $events = $DB->get_records('event', array('modulename' => 'workshop', 'instance' => $workshop->id
));
288 foreach ($events as $event) {
289 $event = calendar_event
::load($event);
294 grade_update('mod/workshop', $workshop->course
, 'mod', 'workshop', $workshop->id
, 0, null, array('deleted' => true));
295 grade_update('mod/workshop', $workshop->course
, 'mod', 'workshop', $workshop->id
, 1, null, array('deleted' => true));
297 // finally remove the workshop record itself
298 // We must delete the module record after we delete the grade item.
299 $DB->delete_records('workshop', array('id' => $workshop->id
));
305 * This standard function will check all instances of this module
306 * and make sure there are up-to-date events created for each of them.
307 * If courseid = 0, then every workshop event in the site is checked, else
308 * only workshop events belonging to the course specified are checked.
310 * @param integer $courseid The Course ID.
311 * @param int|stdClass $instance workshop module instance or ID.
312 * @param int|stdClass $cm Course module object or ID.
313 * @return bool Returns true if the calendar events were successfully updated.
315 function workshop_refresh_events($courseid = 0, $instance = null, $cm = null) {
318 // If we have instance information then we can just update the one event instead of updating all events.
319 if (isset($instance)) {
320 if (!is_object($instance)) {
321 $instance = $DB->get_record('workshop', array('id' => $instance), '*', MUST_EXIST
);
324 if (!is_object($cm)) {
325 $cm = (object)array('id' => $cm);
328 $cm = get_coursemodule_from_instance('workshop', $instance->id
);
330 workshop_calendar_update($instance, $cm->id
);
335 // Make sure that the course id is numeric.
336 if (!is_numeric($courseid)) {
339 if (!$workshops = $DB->get_records('workshop', array('course' => $courseid))) {
343 if (!$workshops = $DB->get_records('workshop')) {
347 foreach ($workshops as $workshop) {
348 if (!$cm = get_coursemodule_from_instance('workshop', $workshop->id
, $courseid, false)) {
351 workshop_calendar_update($workshop, $cm->id
);
357 * List the actions that correspond to a view of this module.
358 * This is used by the participation report.
360 * Note: This is not used by new logging system. Event with
361 * crud = 'r' and edulevel = LEVEL_PARTICIPATING will
362 * be considered as view action.
366 function workshop_get_view_actions() {
367 return array('view', 'view all', 'view submission', 'view example');
371 * List the actions that correspond to a post of this module.
372 * This is used by the participation report.
374 * Note: This is not used by new logging system. Event with
375 * crud = ('c' || 'u' || 'd') and edulevel = LEVEL_PARTICIPATING
376 * will be considered as post action.
380 function workshop_get_post_actions() {
381 return array('add', 'add assessment', 'add example', 'add submission',
382 'update', 'update assessment', 'update example', 'update submission');
386 * Return a small object with summary information about what a
387 * user has done with a given particular instance of this module
388 * Used for user activity reports.
389 * $return->time = the time they did it
390 * $return->info = a short text description
392 * @param stdClass $course The course record.
393 * @param stdClass $user The user record.
394 * @param cm_info|stdClass $mod The course module info object or record.
395 * @param stdClass $workshop The workshop instance record.
396 * @return stdclass|null
398 function workshop_user_outline($course, $user, $mod, $workshop) {
400 require_once($CFG->libdir
.'/gradelib.php');
402 $grades = grade_get_grades($course->id
, 'mod', 'workshop', $workshop->id
, $user->id
);
404 $submissiongrade = null;
405 $assessmentgrade = null;
410 if (!empty($grades->items
[0]->grades
)) {
411 $submissiongrade = reset($grades->items
[0]->grades
);
412 $time = max($time, $submissiongrade->dategraded
);
413 if (!$submissiongrade->hidden ||
has_capability('moodle/grade:viewhidden', context_course
::instance($course->id
))) {
414 $info .= get_string('submissiongrade', 'workshop') . ': ' . $submissiongrade->str_long_grade
415 . html_writer
::empty_tag('br');
417 $info .= get_string('submissiongrade', 'workshop') . ': ' . get_string('hidden', 'grades')
418 . html_writer
::empty_tag('br');
421 if (!empty($grades->items
[1]->grades
)) {
422 $assessmentgrade = reset($grades->items
[1]->grades
);
423 $time = max($time, $assessmentgrade->dategraded
);
424 if (!$assessmentgrade->hidden ||
has_capability('moodle/grade:viewhidden', context_course
::instance($course->id
))) {
425 $info .= get_string('gradinggrade', 'workshop') . ': ' . $assessmentgrade->str_long_grade
;
427 $info .= get_string('gradinggrade', 'workshop') . ': ' . get_string('hidden', 'grades');
431 if (!empty($info) and !empty($time)) {
432 $return = new stdclass();
433 $return->time
= $time;
434 $return->info
= $info;
442 * Print a detailed representation of what a user has done with
443 * a given particular instance of this module, for user activity reports.
445 * @param stdClass $course The course record.
446 * @param stdClass $user The user record.
447 * @param cm_info|stdClass $mod The course module info object or record.
448 * @param stdClass $workshop The workshop instance record.
449 * @return string HTML
451 function workshop_user_complete($course, $user, $mod, $workshop) {
452 global $CFG, $DB, $OUTPUT;
453 require_once(__DIR__
.'/locallib.php');
454 require_once($CFG->libdir
.'/gradelib.php');
456 $workshop = new workshop($workshop, $mod, $course);
457 $grades = grade_get_grades($course->id
, 'mod', 'workshop', $workshop->id
, $user->id
);
459 if (!empty($grades->items
[0]->grades
)) {
460 $submissiongrade = reset($grades->items
[0]->grades
);
461 if (!$submissiongrade->hidden ||
has_capability('moodle/grade:viewhidden', context_course
::instance($course->id
))) {
462 $info = get_string('submissiongrade', 'workshop') . ': ' . $submissiongrade->str_long_grade
;
464 $info = get_string('submissiongrade', 'workshop') . ': ' . get_string('hidden', 'grades');
466 echo html_writer
::tag('li', $info, array('class'=>'submissiongrade'));
468 if (!empty($grades->items
[1]->grades
)) {
469 $assessmentgrade = reset($grades->items
[1]->grades
);
470 if (!$assessmentgrade->hidden ||
has_capability('moodle/grade:viewhidden', context_course
::instance($course->id
))) {
471 $info = get_string('gradinggrade', 'workshop') . ': ' . $assessmentgrade->str_long_grade
;
473 $info = get_string('gradinggrade', 'workshop') . ': ' . get_string('hidden', 'grades');
475 echo html_writer
::tag('li', $info, array('class'=>'gradinggrade'));
478 if (has_capability('mod/workshop:viewallsubmissions', $workshop->context
)) {
479 $canviewsubmission = true;
480 if (groups_get_activity_groupmode($workshop->cm
) == SEPARATEGROUPS
) {
481 // user must have accessallgroups or share at least one group with the submission author
482 if (!has_capability('moodle/site:accessallgroups', $workshop->context
)) {
483 $usersgroups = groups_get_activity_allowed_groups($workshop->cm
);
484 $authorsgroups = groups_get_all_groups($workshop->course
->id
, $user->id
, $workshop->cm
->groupingid
, 'g.id');
485 $sharedgroups = array_intersect_key($usersgroups, $authorsgroups);
486 if (empty($sharedgroups)) {
487 $canviewsubmission = false;
491 if ($canviewsubmission and $submission = $workshop->get_submission_by_author($user->id
)) {
492 $title = format_string($submission->title
);
493 $url = $workshop->submission_url($submission->id
);
494 $link = html_writer
::link($url, $title);
495 $info = get_string('submission', 'workshop').': '.$link;
496 echo html_writer
::tag('li', $info, array('class'=>'submission'));
500 if (has_capability('mod/workshop:viewallassessments', $workshop->context
)) {
501 if ($assessments = $workshop->get_assessments_by_reviewer($user->id
)) {
502 foreach ($assessments as $assessment) {
504 $a->submissionurl
= $workshop->submission_url($assessment->submissionid
)->out();
505 $a->assessmenturl
= $workshop->assess_url($assessment->id
)->out();
506 $a->submissiontitle
= s($assessment->submissiontitle
);
507 echo html_writer
::tag('li', get_string('assessmentofsubmission', 'workshop', $a));
514 * Given a course and a time, this module should find recent activity
515 * that has occurred in workshop activities and print it out.
516 * Return true if there was output, or false is there was none.
518 * @param stdClass $course
519 * @param bool $viewfullnames
520 * @param int $timestart
523 function workshop_print_recent_activity($course, $viewfullnames, $timestart) {
524 global $CFG, $USER, $DB, $OUTPUT;
526 $authoramefields = get_all_user_name_fields(true, 'author', null, 'author');
527 $reviewerfields = get_all_user_name_fields(true, 'reviewer', null, 'reviewer');
529 $sql = "SELECT s.id AS submissionid, s.title AS submissiontitle, s.timemodified AS submissionmodified,
530 author.id AS authorid, $authoramefields, a.id AS assessmentid, a.timemodified AS assessmentmodified,
531 reviewer.id AS reviewerid, $reviewerfields, cm.id AS cmid
533 INNER JOIN {course_modules} cm ON cm.instance = w.id
534 INNER JOIN {modules} md ON md.id = cm.module
535 INNER JOIN {workshop_submissions} s ON s.workshopid = w.id
536 INNER JOIN {user} author ON s.authorid = author.id
537 LEFT JOIN {workshop_assessments} a ON a.submissionid = s.id
538 LEFT JOIN {user} reviewer ON a.reviewerid = reviewer.id
540 AND md.name = 'workshop'
542 AND (s.timemodified > ? OR a.timemodified > ?)
543 ORDER BY s.timemodified";
545 $rs = $DB->get_recordset_sql($sql, array($course->id
, $timestart, $timestart));
547 $modinfo = get_fast_modinfo($course); // reference needed because we might load the groups
549 $submissions = array(); // recent submissions indexed by submission id
550 $assessments = array(); // recent assessments indexed by assessment id
553 foreach ($rs as $activity) {
554 if (!array_key_exists($activity->cmid
, $modinfo->cms
)) {
555 // this should not happen but just in case
559 $cm = $modinfo->cms
[$activity->cmid
];
560 if (!$cm->uservisible
) {
564 // remember all user names we can use later
565 if (empty($users[$activity->authorid
])) {
567 $users[$activity->authorid
] = username_load_fields_from_object($u, $activity, 'author');
569 if ($activity->reviewerid
and empty($users[$activity->reviewerid
])) {
571 $users[$activity->reviewerid
] = username_load_fields_from_object($u, $activity, 'reviewer');
574 $context = context_module
::instance($cm->id
);
575 $groupmode = groups_get_activity_groupmode($cm, $course);
577 if ($activity->submissionmodified
> $timestart and empty($submissions[$activity->submissionid
])) {
579 $s->title
= $activity->submissiontitle
;
580 $s->authorid
= $activity->authorid
;
581 $s->timemodified
= $activity->submissionmodified
;
582 $s->cmid
= $activity->cmid
;
583 if ($activity->authorid
== $USER->id ||
has_capability('mod/workshop:viewauthornames', $context)) {
584 $s->authornamevisible
= true;
586 $s->authornamevisible
= false;
589 // the following do-while wrapper allows to break from deeply nested if-statements
591 if ($s->authorid
=== $USER->id
) {
592 // own submissions always visible
593 $submissions[$activity->submissionid
] = $s;
597 if (has_capability('mod/workshop:viewallsubmissions', $context)) {
598 if ($groupmode == SEPARATEGROUPS
and !has_capability('moodle/site:accessallgroups', $context)) {
600 // shortcut - guest user does not belong into any group
604 // this might be slow - show only submissions by users who share group with me in this cm
605 if (!$modinfo->get_groups($cm->groupingid
)) {
608 $authorsgroups = groups_get_all_groups($course->id
, $s->authorid
, $cm->groupingid
);
609 if (is_array($authorsgroups)) {
610 $authorsgroups = array_keys($authorsgroups);
611 $intersect = array_intersect($authorsgroups, $modinfo->get_groups($cm->groupingid
));
612 if (empty($intersect)) {
615 // can see all submissions and shares a group with the author
616 $submissions[$activity->submissionid
] = $s;
622 // can see all submissions from all groups
623 $submissions[$activity->submissionid
] = $s;
629 if ($activity->assessmentmodified
> $timestart and empty($assessments[$activity->assessmentid
])) {
631 $a->submissionid
= $activity->submissionid
;
632 $a->submissiontitle
= $activity->submissiontitle
;
633 $a->reviewerid
= $activity->reviewerid
;
634 $a->timemodified
= $activity->assessmentmodified
;
635 $a->cmid
= $activity->cmid
;
636 if ($activity->reviewerid
== $USER->id ||
has_capability('mod/workshop:viewreviewernames', $context)) {
637 $a->reviewernamevisible
= true;
639 $a->reviewernamevisible
= false;
642 // the following do-while wrapper allows to break from deeply nested if-statements
644 if ($a->reviewerid
=== $USER->id
) {
645 // own assessments always visible
646 $assessments[$activity->assessmentid
] = $a;
650 if (has_capability('mod/workshop:viewallassessments', $context)) {
651 if ($groupmode == SEPARATEGROUPS
and !has_capability('moodle/site:accessallgroups', $context)) {
653 // shortcut - guest user does not belong into any group
657 // this might be slow - show only submissions by users who share group with me in this cm
658 if (!$modinfo->get_groups($cm->groupingid
)) {
661 $reviewersgroups = groups_get_all_groups($course->id
, $a->reviewerid
, $cm->groupingid
);
662 if (is_array($reviewersgroups)) {
663 $reviewersgroups = array_keys($reviewersgroups);
664 $intersect = array_intersect($reviewersgroups, $modinfo->get_groups($cm->groupingid
));
665 if (empty($intersect)) {
668 // can see all assessments and shares a group with the reviewer
669 $assessments[$activity->assessmentid
] = $a;
675 // can see all assessments from all groups
676 $assessments[$activity->assessmentid
] = $a;
686 if (!empty($submissions)) {
688 echo $OUTPUT->heading(get_string('recentsubmissions', 'workshop'), 3);
689 foreach ($submissions as $id => $submission) {
690 $link = new moodle_url('/mod/workshop/submission.php', array('id'=>$id, 'cmid'=>$submission->cmid
));
691 if ($submission->authornamevisible
) {
692 $author = $users[$submission->authorid
];
696 print_recent_activity_note($submission->timemodified
, $author, $submission->title
, $link->out(), false, $viewfullnames);
700 if (!empty($assessments)) {
702 echo $OUTPUT->heading(get_string('recentassessments', 'workshop'), 3);
703 core_collator
::asort_objects_by_property($assessments, 'timemodified');
704 foreach ($assessments as $id => $assessment) {
705 $link = new moodle_url('/mod/workshop/assessment.php', array('asid' => $id));
706 if ($assessment->reviewernamevisible
) {
707 $reviewer = $users[$assessment->reviewerid
];
711 print_recent_activity_note($assessment->timemodified
, $reviewer, $assessment->submissiontitle
, $link->out(), false, $viewfullnames);
723 * Returns all activity in course workshops since a given time
725 * @param array $activities sequentially indexed array of objects
727 * @param int $timestart
728 * @param int $courseid
730 * @param int $userid defaults to 0
731 * @param int $groupid defaults to 0
732 * @return void adds items into $activities and increases $index
734 function workshop_get_recent_mod_activity(&$activities, &$index, $timestart, $courseid, $cmid, $userid=0, $groupid=0) {
735 global $CFG, $COURSE, $USER, $DB;
737 if ($COURSE->id
== $courseid) {
740 $course = $DB->get_record('course', array('id'=>$courseid));
743 $modinfo = get_fast_modinfo($course);
745 $cm = $modinfo->cms
[$cmid];
749 $userselect = "AND (author.id = :authorid OR reviewer.id = :reviewerid)";
750 $params['authorid'] = $userid;
751 $params['reviewerid'] = $userid;
757 $groupselect = "AND (authorgroupmembership.groupid = :authorgroupid OR reviewergroupmembership.groupid = :reviewergroupid)";
758 $groupjoin = "LEFT JOIN {groups_members} authorgroupmembership ON authorgroupmembership.userid = author.id
759 LEFT JOIN {groups_members} reviewergroupmembership ON reviewergroupmembership.userid = reviewer.id";
760 $params['authorgroupid'] = $groupid;
761 $params['reviewergroupid'] = $groupid;
767 $params['cminstance'] = $cm->instance
;
768 $params['submissionmodified'] = $timestart;
769 $params['assessmentmodified'] = $timestart;
771 $authornamefields = get_all_user_name_fields(true, 'author', null, 'author');
772 $reviewerfields = get_all_user_name_fields(true, 'reviewer', null, 'reviewer');
774 $sql = "SELECT s.id AS submissionid, s.title AS submissiontitle, s.timemodified AS submissionmodified,
775 author.id AS authorid, $authornamefields, author.picture AS authorpicture, author.imagealt AS authorimagealt,
776 author.email AS authoremail, a.id AS assessmentid, a.timemodified AS assessmentmodified,
777 reviewer.id AS reviewerid, $reviewerfields, reviewer.picture AS reviewerpicture,
778 reviewer.imagealt AS reviewerimagealt, reviewer.email AS revieweremail
779 FROM {workshop_submissions} s
780 INNER JOIN {workshop} w ON s.workshopid = w.id
781 INNER JOIN {user} author ON s.authorid = author.id
782 LEFT JOIN {workshop_assessments} a ON a.submissionid = s.id
783 LEFT JOIN {user} reviewer ON a.reviewerid = reviewer.id
785 WHERE w.id = :cminstance
787 $userselect $groupselect
788 AND (s.timemodified > :submissionmodified OR a.timemodified > :assessmentmodified)
789 ORDER BY s.timemodified ASC, a.timemodified ASC";
791 $rs = $DB->get_recordset_sql($sql, $params);
793 $groupmode = groups_get_activity_groupmode($cm, $course);
794 $context = context_module
::instance($cm->id
);
795 $grader = has_capability('moodle/grade:viewall', $context);
796 $accessallgroups = has_capability('moodle/site:accessallgroups', $context);
797 $viewauthors = has_capability('mod/workshop:viewauthornames', $context);
798 $viewreviewers = has_capability('mod/workshop:viewreviewernames', $context);
800 $submissions = array(); // recent submissions indexed by submission id
801 $assessments = array(); // recent assessments indexed by assessment id
804 foreach ($rs as $activity) {
806 // remember all user names we can use later
807 if (empty($users[$activity->authorid
])) {
809 $additionalfields = explode(',', user_picture
::fields());
810 $u = username_load_fields_from_object($u, $activity, 'author', $additionalfields);
811 $users[$activity->authorid
] = $u;
813 if ($activity->reviewerid
and empty($users[$activity->reviewerid
])) {
815 $additionalfields = explode(',', user_picture
::fields());
816 $u = username_load_fields_from_object($u, $activity, 'reviewer', $additionalfields);
817 $users[$activity->reviewerid
] = $u;
820 if ($activity->submissionmodified
> $timestart and empty($submissions[$activity->submissionid
])) {
822 $s->id
= $activity->submissionid
;
823 $s->title
= $activity->submissiontitle
;
824 $s->authorid
= $activity->authorid
;
825 $s->timemodified
= $activity->submissionmodified
;
826 if ($activity->authorid
== $USER->id ||
has_capability('mod/workshop:viewauthornames', $context)) {
827 $s->authornamevisible
= true;
829 $s->authornamevisible
= false;
832 // the following do-while wrapper allows to break from deeply nested if-statements
834 if ($s->authorid
=== $USER->id
) {
835 // own submissions always visible
836 $submissions[$activity->submissionid
] = $s;
840 if (has_capability('mod/workshop:viewallsubmissions', $context)) {
841 if ($groupmode == SEPARATEGROUPS
and !has_capability('moodle/site:accessallgroups', $context)) {
843 // shortcut - guest user does not belong into any group
847 // this might be slow - show only submissions by users who share group with me in this cm
848 if (!$modinfo->get_groups($cm->groupingid
)) {
851 $authorsgroups = groups_get_all_groups($course->id
, $s->authorid
, $cm->groupingid
);
852 if (is_array($authorsgroups)) {
853 $authorsgroups = array_keys($authorsgroups);
854 $intersect = array_intersect($authorsgroups, $modinfo->get_groups($cm->groupingid
));
855 if (empty($intersect)) {
858 // can see all submissions and shares a group with the author
859 $submissions[$activity->submissionid
] = $s;
865 // can see all submissions from all groups
866 $submissions[$activity->submissionid
] = $s;
872 if ($activity->assessmentmodified
> $timestart and empty($assessments[$activity->assessmentid
])) {
874 $a->id
= $activity->assessmentid
;
875 $a->submissionid
= $activity->submissionid
;
876 $a->submissiontitle
= $activity->submissiontitle
;
877 $a->reviewerid
= $activity->reviewerid
;
878 $a->timemodified
= $activity->assessmentmodified
;
879 if ($activity->reviewerid
== $USER->id ||
has_capability('mod/workshop:viewreviewernames', $context)) {
880 $a->reviewernamevisible
= true;
882 $a->reviewernamevisible
= false;
885 // the following do-while wrapper allows to break from deeply nested if-statements
887 if ($a->reviewerid
=== $USER->id
) {
888 // own assessments always visible
889 $assessments[$activity->assessmentid
] = $a;
893 if (has_capability('mod/workshop:viewallassessments', $context)) {
894 if ($groupmode == SEPARATEGROUPS
and !has_capability('moodle/site:accessallgroups', $context)) {
896 // shortcut - guest user does not belong into any group
900 // this might be slow - show only submissions by users who share group with me in this cm
901 if (!$modinfo->get_groups($cm->groupingid
)) {
904 $reviewersgroups = groups_get_all_groups($course->id
, $a->reviewerid
, $cm->groupingid
);
905 if (is_array($reviewersgroups)) {
906 $reviewersgroups = array_keys($reviewersgroups);
907 $intersect = array_intersect($reviewersgroups, $modinfo->get_groups($cm->groupingid
));
908 if (empty($intersect)) {
911 // can see all assessments and shares a group with the reviewer
912 $assessments[$activity->assessmentid
] = $a;
918 // can see all assessments from all groups
919 $assessments[$activity->assessmentid
] = $a;
927 $workshopname = format_string($cm->name
, true);
930 require_once($CFG->libdir
.'/gradelib.php');
931 $grades = grade_get_grades($courseid, 'mod', 'workshop', $cm->instance
, array_keys($users));
934 foreach ($submissions as $submission) {
935 $tmpactivity = new stdclass();
936 $tmpactivity->type
= 'workshop';
937 $tmpactivity->cmid
= $cm->id
;
938 $tmpactivity->name
= $workshopname;
939 $tmpactivity->sectionnum
= $cm->sectionnum
;
940 $tmpactivity->timestamp
= $submission->timemodified
;
941 $tmpactivity->subtype
= 'submission';
942 $tmpactivity->content
= $submission;
944 $tmpactivity->grade
= $grades->items
[0]->grades
[$submission->authorid
]->str_long_grade
;
946 if ($submission->authornamevisible
and !empty($users[$submission->authorid
])) {
947 $tmpactivity->user
= $users[$submission->authorid
];
949 $activities[$index++
] = $tmpactivity;
952 foreach ($assessments as $assessment) {
953 $tmpactivity = new stdclass();
954 $tmpactivity->type
= 'workshop';
955 $tmpactivity->cmid
= $cm->id
;
956 $tmpactivity->name
= $workshopname;
957 $tmpactivity->sectionnum
= $cm->sectionnum
;
958 $tmpactivity->timestamp
= $assessment->timemodified
;
959 $tmpactivity->subtype
= 'assessment';
960 $tmpactivity->content
= $assessment;
962 $tmpactivity->grade
= $grades->items
[1]->grades
[$assessment->reviewerid
]->str_long_grade
;
964 if ($assessment->reviewernamevisible
and !empty($users[$assessment->reviewerid
])) {
965 $tmpactivity->user
= $users[$assessment->reviewerid
];
967 $activities[$index++
] = $tmpactivity;
972 * Print single activity item prepared by {@see workshop_get_recent_mod_activity()}
974 function workshop_print_recent_mod_activity($activity, $courseid, $detail, $modnames, $viewfullnames) {
975 global $CFG, $OUTPUT;
977 if (!empty($activity->user
)) {
978 echo html_writer
::tag('div', $OUTPUT->user_picture($activity->user
, array('courseid'=>$courseid)),
979 array('style' => 'float: left; padding: 7px;'));
982 if ($activity->subtype
== 'submission') {
983 echo html_writer
::start_tag('div', array('class'=>'submission', 'style'=>'padding: 7px; float:left;'));
986 echo html_writer
::start_tag('h4', array('class'=>'workshop'));
987 $url = new moodle_url('/mod/workshop/view.php', array('id'=>$activity->cmid
));
988 $name = s($activity->name
);
989 echo $OUTPUT->image_icon('icon', $name, $activity->type
);
990 echo ' ' . $modnames[$activity->type
];
991 echo html_writer
::link($url, $name, array('class'=>'name', 'style'=>'margin-left: 5px'));
992 echo html_writer
::end_tag('h4');
995 echo html_writer
::start_tag('div', array('class'=>'title'));
996 $url = new moodle_url('/mod/workshop/submission.php', array('cmid'=>$activity->cmid
, 'id'=>$activity->content
->id
));
997 $name = s($activity->content
->title
);
998 echo html_writer
::tag('strong', html_writer
::link($url, $name));
999 echo html_writer
::end_tag('div');
1001 if (!empty($activity->user
)) {
1002 echo html_writer
::start_tag('div', array('class'=>'user'));
1003 $url = new moodle_url('/user/view.php', array('id'=>$activity->user
->id
, 'course'=>$courseid));
1004 $name = fullname($activity->user
);
1005 $link = html_writer
::link($url, $name);
1006 echo get_string('submissionby', 'workshop', $link);
1007 echo ' - '.userdate($activity->timestamp
);
1008 echo html_writer
::end_tag('div');
1010 echo html_writer
::start_tag('div', array('class'=>'anonymous'));
1011 echo get_string('submission', 'workshop');
1012 echo ' - '.userdate($activity->timestamp
);
1013 echo html_writer
::end_tag('div');
1016 echo html_writer
::end_tag('div');
1019 if ($activity->subtype
== 'assessment') {
1020 echo html_writer
::start_tag('div', array('class'=>'assessment', 'style'=>'padding: 7px; float:left;'));
1023 echo html_writer
::start_tag('h4', array('class'=>'workshop'));
1024 $url = new moodle_url('/mod/workshop/view.php', array('id'=>$activity->cmid
));
1025 $name = s($activity->name
);
1026 echo $OUTPUT->image_icon('icon', $name, $activity->type
);
1027 echo ' ' . $modnames[$activity->type
];
1028 echo html_writer
::link($url, $name, array('class'=>'name', 'style'=>'margin-left: 5px'));
1029 echo html_writer
::end_tag('h4');
1032 echo html_writer
::start_tag('div', array('class'=>'title'));
1033 $url = new moodle_url('/mod/workshop/assessment.php', array('asid'=>$activity->content
->id
));
1034 $name = s($activity->content
->submissiontitle
);
1035 echo html_writer
::tag('em', html_writer
::link($url, $name));
1036 echo html_writer
::end_tag('div');
1038 if (!empty($activity->user
)) {
1039 echo html_writer
::start_tag('div', array('class'=>'user'));
1040 $url = new moodle_url('/user/view.php', array('id'=>$activity->user
->id
, 'course'=>$courseid));
1041 $name = fullname($activity->user
);
1042 $link = html_writer
::link($url, $name);
1043 echo get_string('assessmentbyfullname', 'workshop', $link);
1044 echo ' - '.userdate($activity->timestamp
);
1045 echo html_writer
::end_tag('div');
1047 echo html_writer
::start_tag('div', array('class'=>'anonymous'));
1048 echo get_string('assessment', 'workshop');
1049 echo ' - '.userdate($activity->timestamp
);
1050 echo html_writer
::end_tag('div');
1053 echo html_writer
::end_tag('div');
1056 echo html_writer
::empty_tag('br', array('style'=>'clear:both'));
1060 * Is a given scale used by the instance of workshop?
1062 * The function asks all installed grading strategy subplugins. The workshop
1063 * core itself does not use scales. Both grade for submission and grade for
1064 * assessments do not use scales.
1066 * @param int $workshopid id of workshop instance
1067 * @param int $scaleid id of the scale to check
1070 function workshop_scale_used($workshopid, $scaleid) {
1071 global $CFG; // other files included from here
1073 $strategies = core_component
::get_plugin_list('workshopform');
1074 foreach ($strategies as $strategy => $strategypath) {
1075 $strategylib = $strategypath . '/lib.php';
1076 if (is_readable($strategylib)) {
1077 require_once($strategylib);
1079 throw new coding_exception('the grading forms subplugin must contain library ' . $strategylib);
1081 $classname = 'workshop_' . $strategy . '_strategy';
1082 if (method_exists($classname, 'scale_used')) {
1083 if (call_user_func_array(array($classname, 'scale_used'), array($scaleid, $workshopid))) {
1084 // no need to include any other files - scale is used
1094 * Is a given scale used by any instance of workshop?
1096 * The function asks all installed grading strategy subplugins. The workshop
1097 * core itself does not use scales. Both grade for submission and grade for
1098 * assessments do not use scales.
1100 * @param int $scaleid id of the scale to check
1103 function workshop_scale_used_anywhere($scaleid) {
1104 global $CFG; // other files included from here
1106 $strategies = core_component
::get_plugin_list('workshopform');
1107 foreach ($strategies as $strategy => $strategypath) {
1108 $strategylib = $strategypath . '/lib.php';
1109 if (is_readable($strategylib)) {
1110 require_once($strategylib);
1112 throw new coding_exception('the grading forms subplugin must contain library ' . $strategylib);
1114 $classname = 'workshop_' . $strategy . '_strategy';
1115 if (method_exists($classname, 'scale_used')) {
1116 if (call_user_func(array($classname, 'scale_used'), $scaleid)) {
1117 // no need to include any other files - scale is used
1126 ////////////////////////////////////////////////////////////////////////////////
1128 ////////////////////////////////////////////////////////////////////////////////
1131 * Creates or updates grade items for the give workshop instance
1133 * Needed by grade_update_mod_grades() in lib/gradelib.php. Also used by
1134 * {@link workshop_update_grades()}.
1136 * @param stdClass $workshop instance object with extra cmidnumber property
1137 * @param stdClass $submissiongrades data for the first grade item
1138 * @param stdClass $assessmentgrades data for the second grade item
1141 function workshop_grade_item_update(stdclass
$workshop, $submissiongrades=null, $assessmentgrades=null) {
1143 require_once($CFG->libdir
.'/gradelib.php');
1145 $a = new stdclass();
1146 $a->workshopname
= clean_param($workshop->name
, PARAM_NOTAGS
);
1149 $item['itemname'] = get_string('gradeitemsubmission', 'workshop', $a);
1150 $item['gradetype'] = GRADE_TYPE_VALUE
;
1151 $item['grademax'] = $workshop->grade
;
1152 $item['grademin'] = 0;
1153 grade_update('mod/workshop', $workshop->course
, 'mod', 'workshop', $workshop->id
, 0, $submissiongrades , $item);
1156 $item['itemname'] = get_string('gradeitemassessment', 'workshop', $a);
1157 $item['gradetype'] = GRADE_TYPE_VALUE
;
1158 $item['grademax'] = $workshop->gradinggrade
;
1159 $item['grademin'] = 0;
1160 grade_update('mod/workshop', $workshop->course
, 'mod', 'workshop', $workshop->id
, 1, $assessmentgrades, $item);
1164 * Update workshop grades in the gradebook
1166 * Needed by grade_update_mod_grades() in lib/gradelib.php
1169 * @param stdClass $workshop instance object with extra cmidnumber and modname property
1170 * @param int $userid update grade of specific user only, 0 means all participants
1173 function workshop_update_grades(stdclass
$workshop, $userid=0) {
1175 require_once($CFG->libdir
.'/gradelib.php');
1177 $whereuser = $userid ?
' AND authorid = :userid' : '';
1178 $params = array('workshopid' => $workshop->id
, 'userid' => $userid);
1179 $sql = 'SELECT authorid, grade, gradeover, gradeoverby, feedbackauthor, feedbackauthorformat, timemodified, timegraded
1180 FROM {workshop_submissions}
1181 WHERE workshopid = :workshopid AND example=0' . $whereuser;
1182 $records = $DB->get_records_sql($sql, $params);
1183 $submissiongrades = array();
1184 foreach ($records as $record) {
1185 $grade = new stdclass();
1186 $grade->userid
= $record->authorid
;
1187 if (!is_null($record->gradeover
)) {
1188 $grade->rawgrade
= grade_floatval($workshop->grade
* $record->gradeover
/ 100);
1189 $grade->usermodified
= $record->gradeoverby
;
1191 $grade->rawgrade
= grade_floatval($workshop->grade
* $record->grade
/ 100);
1193 $grade->feedback
= $record->feedbackauthor
;
1194 $grade->feedbackformat
= $record->feedbackauthorformat
;
1195 $grade->datesubmitted
= $record->timemodified
;
1196 $grade->dategraded
= $record->timegraded
;
1197 $submissiongrades[$record->authorid
] = $grade;
1200 $whereuser = $userid ?
' AND userid = :userid' : '';
1201 $params = array('workshopid' => $workshop->id
, 'userid' => $userid);
1202 $sql = 'SELECT userid, gradinggrade, timegraded
1203 FROM {workshop_aggregations}
1204 WHERE workshopid = :workshopid' . $whereuser;
1205 $records = $DB->get_records_sql($sql, $params);
1206 $assessmentgrades = array();
1207 foreach ($records as $record) {
1208 $grade = new stdclass();
1209 $grade->userid
= $record->userid
;
1210 $grade->rawgrade
= grade_floatval($workshop->gradinggrade
* $record->gradinggrade
/ 100);
1211 $grade->dategraded
= $record->timegraded
;
1212 $assessmentgrades[$record->userid
] = $grade;
1215 workshop_grade_item_update($workshop, $submissiongrades, $assessmentgrades);
1219 * Update the grade items categories if they are changed via mod_form.php
1221 * We must do it manually here in the workshop module because modedit supports only
1222 * single grade item while we use two.
1224 * @param stdClass $workshop An object from the form in mod_form.php
1226 function workshop_grade_item_category_update($workshop) {
1228 $gradeitems = grade_item
::fetch_all(array(
1229 'itemtype' => 'mod',
1230 'itemmodule' => 'workshop',
1231 'iteminstance' => $workshop->id
,
1232 'courseid' => $workshop->course
));
1234 if (!empty($gradeitems)) {
1235 foreach ($gradeitems as $gradeitem) {
1236 if ($gradeitem->itemnumber
== 0) {
1237 if (isset($workshop->submissiongradepass
) &&
1238 $gradeitem->gradepass
!= $workshop->submissiongradepass
) {
1239 $gradeitem->gradepass
= $workshop->submissiongradepass
;
1240 $gradeitem->update();
1242 if ($gradeitem->categoryid
!= $workshop->gradecategory
) {
1243 $gradeitem->set_parent($workshop->gradecategory
);
1245 } else if ($gradeitem->itemnumber
== 1) {
1246 if (isset($workshop->gradinggradepass
) &&
1247 $gradeitem->gradepass
!= $workshop->gradinggradepass
) {
1248 $gradeitem->gradepass
= $workshop->gradinggradepass
;
1249 $gradeitem->update();
1251 if ($gradeitem->categoryid
!= $workshop->gradinggradecategory
) {
1252 $gradeitem->set_parent($workshop->gradinggradecategory
);
1259 ////////////////////////////////////////////////////////////////////////////////
1261 ////////////////////////////////////////////////////////////////////////////////
1264 * Returns the lists of all browsable file areas within the given module context
1266 * The file area workshop_intro for the activity introduction field is added automatically
1267 * by {@link file_browser::get_file_info_context_module()}
1269 * @package mod_workshop
1272 * @param stdClass $course
1273 * @param stdClass $cm
1274 * @param stdClass $context
1275 * @return array of [(string)filearea] => (string)description
1277 function workshop_get_file_areas($course, $cm, $context) {
1279 $areas['instructauthors'] = get_string('areainstructauthors', 'workshop');
1280 $areas['instructreviewers'] = get_string('areainstructreviewers', 'workshop');
1281 $areas['submission_content'] = get_string('areasubmissioncontent', 'workshop');
1282 $areas['submission_attachment'] = get_string('areasubmissionattachment', 'workshop');
1283 $areas['conclusion'] = get_string('areaconclusion', 'workshop');
1284 $areas['overallfeedback_content'] = get_string('areaoverallfeedbackcontent', 'workshop');
1285 $areas['overallfeedback_attachment'] = get_string('areaoverallfeedbackattachment', 'workshop');
1291 * Serves the files from the workshop file areas
1293 * Apart from module intro (handled by pluginfile.php automatically), workshop files may be
1294 * media inserted into submission content (like images) and submission attachments. For these two,
1295 * the fileareas submission_content and submission_attachment are used.
1296 * Besides that, areas instructauthors, instructreviewers and conclusion contain the media
1297 * embedded using the mod_form.php.
1299 * @package mod_workshop
1302 * @param stdClass $course the course object
1303 * @param stdClass $cm the course module object
1304 * @param stdClass $context the workshop's context
1305 * @param string $filearea the name of the file area
1306 * @param array $args extra arguments (itemid, path)
1307 * @param bool $forcedownload whether or not force download
1308 * @param array $options additional options affecting the file serving
1309 * @return bool false if the file not found, just send the file otherwise and do not return anything
1311 function workshop_pluginfile($course, $cm, $context, $filearea, array $args, $forcedownload, array $options=array()) {
1312 global $DB, $CFG, $USER;
1314 if ($context->contextlevel
!= CONTEXT_MODULE
) {
1318 require_login($course, true, $cm);
1320 if ($filearea === 'instructauthors' or $filearea === 'instructreviewers' or $filearea === 'conclusion') {
1321 // The $args are supposed to contain just the path, not the item id.
1322 $relativepath = implode('/', $args);
1323 $fullpath = "/$context->id/mod_workshop/$filearea/0/$relativepath";
1325 $fs = get_file_storage();
1326 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
1327 send_file_not_found();
1329 send_stored_file($file, null, 0, $forcedownload, $options);
1331 } else if ($filearea === 'submission_content' or $filearea === 'submission_attachment') {
1332 $itemid = (int)array_shift($args);
1333 if (!$workshop = $DB->get_record('workshop', array('id' => $cm->instance
))) {
1336 if (!$submission = $DB->get_record('workshop_submissions', array('id' => $itemid, 'workshopid' => $workshop->id
))) {
1340 // make sure the user is allowed to see the file
1341 if (empty($submission->example
)) {
1342 if ($USER->id
!= $submission->authorid
) {
1343 if ($submission->published
== 1 and $workshop->phase
== 50
1344 and has_capability('mod/workshop:viewpublishedsubmissions', $context)) {
1345 // Published submission, we can go (workshop does not take the group mode
1346 // into account in this case yet).
1347 } else if (!$DB->record_exists('workshop_assessments', array('submissionid' => $submission->id
, 'reviewerid' => $USER->id
))) {
1348 if (!has_capability('mod/workshop:viewallsubmissions', $context)) {
1349 send_file_not_found();
1351 $gmode = groups_get_activity_groupmode($cm, $course);
1352 if ($gmode == SEPARATEGROUPS
and !has_capability('moodle/site:accessallgroups', $context)) {
1353 // check there is at least one common group with both the $USER
1354 // and the submission author
1356 FROM {workshop_submissions} s
1357 JOIN {user} a ON (a.id = s.authorid)
1358 JOIN {groups_members} agm ON (a.id = agm.userid)
1359 JOIN {user} u ON (u.id = ?)
1360 JOIN {groups_members} ugm ON (u.id = ugm.userid)
1361 WHERE s.example = 0 AND s.workshopid = ? AND s.id = ? AND agm.groupid = ugm.groupid";
1362 $params = array($USER->id
, $workshop->id
, $submission->id
);
1363 if (!$DB->record_exists_sql($sql, $params)) {
1364 send_file_not_found();
1372 $fs = get_file_storage();
1373 $relativepath = implode('/', $args);
1374 $fullpath = "/$context->id/mod_workshop/$filearea/$itemid/$relativepath";
1375 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
1378 // finally send the file
1379 // these files are uploaded by students - forcing download for security reasons
1380 send_stored_file($file, 0, 0, true, $options);
1382 } else if ($filearea === 'overallfeedback_content' or $filearea === 'overallfeedback_attachment') {
1383 $itemid = (int)array_shift($args);
1384 if (!$workshop = $DB->get_record('workshop', array('id' => $cm->instance
))) {
1387 if (!$assessment = $DB->get_record('workshop_assessments', array('id' => $itemid))) {
1390 if (!$submission = $DB->get_record('workshop_submissions', array('id' => $assessment->submissionid
, 'workshopid' => $workshop->id
))) {
1394 if ($USER->id
== $assessment->reviewerid
) {
1395 // Reviewers can always see their own files.
1396 } else if ($USER->id
== $submission->authorid
and $workshop->phase
== 50) {
1397 // Authors can see the feedback once the workshop is closed.
1398 } else if (!empty($submission->example
) and $assessment->weight
== 1) {
1399 // Reference assessments of example submissions can be displayed.
1400 } else if (!has_capability('mod/workshop:viewallassessments', $context)) {
1401 send_file_not_found();
1403 $gmode = groups_get_activity_groupmode($cm, $course);
1404 if ($gmode == SEPARATEGROUPS
and !has_capability('moodle/site:accessallgroups', $context)) {
1405 // Check there is at least one common group with both the $USER
1406 // and the submission author.
1408 FROM {workshop_submissions} s
1409 JOIN {user} a ON (a.id = s.authorid)
1410 JOIN {groups_members} agm ON (a.id = agm.userid)
1411 JOIN {user} u ON (u.id = ?)
1412 JOIN {groups_members} ugm ON (u.id = ugm.userid)
1413 WHERE s.example = 0 AND s.workshopid = ? AND s.id = ? AND agm.groupid = ugm.groupid";
1414 $params = array($USER->id
, $workshop->id
, $submission->id
);
1415 if (!$DB->record_exists_sql($sql, $params)) {
1416 send_file_not_found();
1421 $fs = get_file_storage();
1422 $relativepath = implode('/', $args);
1423 $fullpath = "/$context->id/mod_workshop/$filearea/$itemid/$relativepath";
1424 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
1427 // finally send the file
1428 // these files are uploaded by students - forcing download for security reasons
1429 send_stored_file($file, 0, 0, true, $options);
1436 * File browsing support for workshop file areas
1438 * @package mod_workshop
1441 * @param file_browser $browser
1442 * @param array $areas
1443 * @param stdClass $course
1444 * @param stdClass $cm
1445 * @param stdClass $context
1446 * @param string $filearea
1447 * @param int $itemid
1448 * @param string $filepath
1449 * @param string $filename
1450 * @return file_info instance or null if not found
1452 function workshop_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) {
1453 global $CFG, $DB, $USER;
1455 /** @var array internal cache for author names */
1456 static $submissionauthors = array();
1458 $fs = get_file_storage();
1460 if ($filearea === 'submission_content' or $filearea === 'submission_attachment') {
1462 if (!has_capability('mod/workshop:viewallsubmissions', $context)) {
1466 if (is_null($itemid)) {
1467 // no itemid (submissionid) passed, display the list of all submissions
1468 require_once($CFG->dirroot
. '/mod/workshop/fileinfolib.php');
1469 return new workshop_file_info_submissions_container($browser, $course, $cm, $context, $areas, $filearea);
1472 // make sure the user can see the particular submission in separate groups mode
1473 $gmode = groups_get_activity_groupmode($cm, $course);
1475 if ($gmode == SEPARATEGROUPS
and !has_capability('moodle/site:accessallgroups', $context)) {
1476 // check there is at least one common group with both the $USER
1477 // and the submission author (this is not expected to be a frequent
1478 // usecase so we can live with pretty ineffective one query per submission here...)
1480 FROM {workshop_submissions} s
1481 JOIN {user} a ON (a.id = s.authorid)
1482 JOIN {groups_members} agm ON (a.id = agm.userid)
1483 JOIN {user} u ON (u.id = ?)
1484 JOIN {groups_members} ugm ON (u.id = ugm.userid)
1485 WHERE s.example = 0 AND s.workshopid = ? AND s.id = ? AND agm.groupid = ugm.groupid";
1486 $params = array($USER->id
, $cm->instance
, $itemid);
1487 if (!$DB->record_exists_sql($sql, $params)) {
1492 // we are inside some particular submission container
1494 $filepath = is_null($filepath) ?
'/' : $filepath;
1495 $filename = is_null($filename) ?
'.' : $filename;
1497 if (!$storedfile = $fs->get_file($context->id
, 'mod_workshop', $filearea, $itemid, $filepath, $filename)) {
1498 if ($filepath === '/' and $filename === '.') {
1499 $storedfile = new virtual_root_file($context->id
, 'mod_workshop', $filearea, $itemid);
1506 // Checks to see if the user can manage files or is the owner.
1507 // TODO MDL-33805 - Do not use userid here and move the capability check above.
1508 if (!has_capability('moodle/course:managefiles', $context) && $storedfile->get_userid() != $USER->id
) {
1512 // let us display the author's name instead of itemid (submission id)
1514 if (isset($submissionauthors[$itemid])) {
1515 $topvisiblename = $submissionauthors[$itemid];
1519 $userfields = get_all_user_name_fields(true, 'u');
1520 $sql = "SELECT s.id, $userfields
1521 FROM {workshop_submissions} s
1522 JOIN {user} u ON (s.authorid = u.id)
1523 WHERE s.example = 0 AND s.workshopid = ?";
1524 $params = array($cm->instance
);
1525 $rs = $DB->get_recordset_sql($sql, $params);
1527 foreach ($rs as $submissionauthor) {
1528 $title = s(fullname($submissionauthor)); // this is generally not unique...
1529 $submissionauthors[$submissionauthor->id
] = $title;
1533 if (!isset($submissionauthors[$itemid])) {
1534 // should not happen
1537 $topvisiblename = $submissionauthors[$itemid];
1541 $urlbase = $CFG->wwwroot
. '/pluginfile.php';
1542 // do not allow manual modification of any files!
1543 return new file_info_stored($browser, $context, $storedfile, $urlbase, $topvisiblename, true, true, false, false);
1546 if ($filearea === 'overallfeedback_content' or $filearea === 'overallfeedback_attachment') {
1548 if (!has_capability('mod/workshop:viewallassessments', $context)) {
1552 if (is_null($itemid)) {
1553 // No itemid (assessmentid) passed, display the list of all assessments.
1554 require_once($CFG->dirroot
. '/mod/workshop/fileinfolib.php');
1555 return new workshop_file_info_overallfeedback_container($browser, $course, $cm, $context, $areas, $filearea);
1558 // Make sure the user can see the particular assessment in separate groups mode.
1559 $gmode = groups_get_activity_groupmode($cm, $course);
1560 if ($gmode == SEPARATEGROUPS
and !has_capability('moodle/site:accessallgroups', $context)) {
1561 // Check there is at least one common group with both the $USER
1562 // and the submission author.
1564 FROM {workshop_submissions} s
1565 JOIN {user} a ON (a.id = s.authorid)
1566 JOIN {groups_members} agm ON (a.id = agm.userid)
1567 JOIN {user} u ON (u.id = ?)
1568 JOIN {groups_members} ugm ON (u.id = ugm.userid)
1569 WHERE s.example = 0 AND s.workshopid = ? AND s.id = ? AND agm.groupid = ugm.groupid";
1570 $params = array($USER->id
, $cm->instance
, $itemid);
1571 if (!$DB->record_exists_sql($sql, $params)) {
1576 // We are inside a particular assessment container.
1577 $filepath = is_null($filepath) ?
'/' : $filepath;
1578 $filename = is_null($filename) ?
'.' : $filename;
1580 if (!$storedfile = $fs->get_file($context->id
, 'mod_workshop', $filearea, $itemid, $filepath, $filename)) {
1581 if ($filepath === '/' and $filename === '.') {
1582 $storedfile = new virtual_root_file($context->id
, 'mod_workshop', $filearea, $itemid);
1589 // Check to see if the user can manage files or is the owner.
1590 if (!has_capability('moodle/course:managefiles', $context) and $storedfile->get_userid() != $USER->id
) {
1594 $urlbase = $CFG->wwwroot
. '/pluginfile.php';
1596 // Do not allow manual modification of any files.
1597 return new file_info_stored($browser, $context, $storedfile, $urlbase, $itemid, true, true, false, false);
1600 if ($filearea == 'instructauthors' or $filearea == 'instructreviewers' or $filearea == 'conclusion') {
1601 // always only itemid 0
1603 $filepath = is_null($filepath) ?
'/' : $filepath;
1604 $filename = is_null($filename) ?
'.' : $filename;
1606 $urlbase = $CFG->wwwroot
.'/pluginfile.php';
1607 if (!$storedfile = $fs->get_file($context->id
, 'mod_workshop', $filearea, 0, $filepath, $filename)) {
1608 if ($filepath === '/' and $filename === '.') {
1609 $storedfile = new virtual_root_file($context->id
, 'mod_workshop', $filearea, 0);
1615 return new file_info_stored($browser, $context, $storedfile, $urlbase, $areas[$filearea], false, true, true, false);
1619 ////////////////////////////////////////////////////////////////////////////////
1620 // Navigation API //
1621 ////////////////////////////////////////////////////////////////////////////////
1624 * Extends the global navigation tree by adding workshop nodes if there is a relevant content
1626 * This can be called by an AJAX request so do not rely on $PAGE as it might not be set up properly.
1628 * @param navigation_node $navref An object representing the navigation tree node of the workshop module instance
1629 * @param stdClass $course
1630 * @param stdClass $module
1631 * @param cm_info $cm
1633 function workshop_extend_navigation(navigation_node
$navref, stdclass
$course, stdclass
$module, cm_info
$cm) {
1636 if (has_capability('mod/workshop:submit', context_module
::instance($cm->id
))) {
1637 $url = new moodle_url('/mod/workshop/submission.php', array('cmid' => $cm->id
));
1638 $mysubmission = $navref->add(get_string('mysubmission', 'workshop'), $url);
1639 $mysubmission->mainnavonly
= true;
1644 * Extends the settings navigation with the Workshop settings
1646 * This function is called when the context for the page is a workshop module. This is not called by AJAX
1647 * so it is safe to rely on the $PAGE.
1649 * @param settings_navigation $settingsnav {@link settings_navigation}
1650 * @param navigation_node $workshopnode {@link navigation_node}
1652 function workshop_extend_settings_navigation(settings_navigation
$settingsnav, navigation_node
$workshopnode=null) {
1655 //$workshopobject = $DB->get_record("workshop", array("id" => $PAGE->cm->instance));
1657 if (has_capability('mod/workshop:editdimensions', $PAGE->cm
->context
)) {
1658 $url = new moodle_url('/mod/workshop/editform.php', array('cmid' => $PAGE->cm
->id
));
1659 $workshopnode->add(get_string('editassessmentform', 'workshop'), $url, settings_navigation
::TYPE_SETTING
);
1661 if (has_capability('mod/workshop:allocate', $PAGE->cm
->context
)) {
1662 $url = new moodle_url('/mod/workshop/allocation.php', array('cmid' => $PAGE->cm
->id
));
1663 $workshopnode->add(get_string('allocate', 'workshop'), $url, settings_navigation
::TYPE_SETTING
);
1668 * Return a list of page types
1669 * @param string $pagetype current page type
1670 * @param stdClass $parentcontext Block's parent context
1671 * @param stdClass $currentcontext Current context of block
1673 function workshop_page_type_list($pagetype, $parentcontext, $currentcontext) {
1674 $module_pagetype = array('mod-workshop-*'=>get_string('page-mod-workshop-x', 'workshop'));
1675 return $module_pagetype;
1678 ////////////////////////////////////////////////////////////////////////////////
1680 ////////////////////////////////////////////////////////////////////////////////
1683 * Updates the calendar events associated to the given workshop
1685 * @param stdClass $workshop the workshop instance record
1686 * @param int $cmid course module id
1688 function workshop_calendar_update(stdClass
$workshop, $cmid) {
1691 // get the currently registered events so that we can re-use their ids
1692 $currentevents = $DB->get_records('event', array('modulename' => 'workshop', 'instance' => $workshop->id
));
1694 // the common properties for all events
1695 $base = new stdClass();
1696 $base->description
= format_module_intro('workshop', $workshop, $cmid, false);
1697 $base->courseid
= $workshop->course
;
1700 $base->modulename
= 'workshop';
1701 $base->instance
= $workshop->id
;
1702 $base->visible
= instance_is_visible('workshop', $workshop);
1703 $base->timeduration
= 0;
1705 if ($workshop->submissionstart
) {
1706 $event = clone($base);
1707 $event->name
= get_string('submissionstartevent', 'mod_workshop', $workshop->name
);
1708 $event->eventtype
= WORKSHOP_EVENT_TYPE_SUBMISSION_OPEN
;
1709 $event->type
= empty($workshop->submissionend
) ? CALENDAR_EVENT_TYPE_ACTION
: CALENDAR_EVENT_TYPE_STANDARD
;
1710 $event->timestart
= $workshop->submissionstart
;
1711 $event->timesort
= $workshop->submissionstart
;
1712 if ($reusedevent = array_shift($currentevents)) {
1713 $event->id
= $reusedevent->id
;
1715 // should not be set but just in case
1718 // update() will reuse a db record if the id field is set
1719 $eventobj = new calendar_event($event);
1720 $eventobj->update($event, false);
1723 if ($workshop->submissionend
) {
1724 $event = clone($base);
1725 $event->name
= get_string('submissionendevent', 'mod_workshop', $workshop->name
);
1726 $event->eventtype
= WORKSHOP_EVENT_TYPE_SUBMISSION_CLOSE
;
1727 $event->type
= CALENDAR_EVENT_TYPE_ACTION
;
1728 $event->timestart
= $workshop->submissionend
;
1729 $event->timesort
= $workshop->submissionend
;
1730 if ($reusedevent = array_shift($currentevents)) {
1731 $event->id
= $reusedevent->id
;
1733 // should not be set but just in case
1736 // update() will reuse a db record if the id field is set
1737 $eventobj = new calendar_event($event);
1738 $eventobj->update($event, false);
1741 if ($workshop->assessmentstart
) {
1742 $event = clone($base);
1743 $event->name
= get_string('assessmentstartevent', 'mod_workshop', $workshop->name
);
1744 $event->eventtype
= WORKSHOP_EVENT_TYPE_ASSESSMENT_OPEN
;
1745 $event->type
= empty($workshop->assessmentend
) ? CALENDAR_EVENT_TYPE_ACTION
: CALENDAR_EVENT_TYPE_STANDARD
;
1746 $event->timestart
= $workshop->assessmentstart
;
1747 $event->timesort
= $workshop->assessmentstart
;
1748 if ($reusedevent = array_shift($currentevents)) {
1749 $event->id
= $reusedevent->id
;
1751 // should not be set but just in case
1754 // update() will reuse a db record if the id field is set
1755 $eventobj = new calendar_event($event);
1756 $eventobj->update($event, false);
1759 if ($workshop->assessmentend
) {
1760 $event = clone($base);
1761 $event->name
= get_string('assessmentendevent', 'mod_workshop', $workshop->name
);
1762 $event->eventtype
= WORKSHOP_EVENT_TYPE_ASSESSMENT_CLOSE
;
1763 $event->type
= CALENDAR_EVENT_TYPE_ACTION
;
1764 $event->timestart
= $workshop->assessmentend
;
1765 $event->timesort
= $workshop->assessmentend
;
1766 if ($reusedevent = array_shift($currentevents)) {
1767 $event->id
= $reusedevent->id
;
1769 // should not be set but just in case
1772 // update() will reuse a db record if the id field is set
1773 $eventobj = new calendar_event($event);
1774 $eventobj->update($event, false);
1777 // delete any leftover events
1778 foreach ($currentevents as $oldevent) {
1779 $oldevent = calendar_event
::load($oldevent);
1780 $oldevent->delete();
1785 * This function receives a calendar event and returns the action associated with it, or null if there is none.
1787 * This is used by block_myoverview in order to display the event appropriately. If null is returned then the event
1788 * is not displayed on the block.
1790 * @param calendar_event $event
1791 * @param \core_calendar\action_factory $factory
1792 * @param int $userid User id to use for all capability checks, etc. Set to 0 for current user (default).
1793 * @return \core_calendar\local\event\entities\action_interface|null
1795 function mod_workshop_core_calendar_provide_event_action(calendar_event
$event,
1796 \core_calendar\action_factory
$factory, int $userid = 0) {
1800 $userid = $USER->id
;
1803 $cm = get_fast_modinfo($event->courseid
, $userid)->instances
['workshop'][$event->instance
];
1805 if (!$cm->uservisible
) {
1806 // The module is not visible to the user for any reason.
1810 return $factory->create_instance(
1811 get_string('viewworkshopsummary', 'workshop'),
1812 new \
moodle_url('/mod/workshop/view.php', array('id' => $cm->id
)),
1819 * This function calculates the minimum and maximum cutoff values for the timestart of
1822 * It will return an array with two values, the first being the minimum cutoff value and
1823 * the second being the maximum cutoff value. Either or both values can be null, which
1824 * indicates there is no minimum or maximum, respectively.
1826 * If a cutoff is required then the function must return an array containing the cutoff
1827 * timestamp and error string to display to the user if the cutoff value is violated.
1829 * A minimum and maximum cutoff return value will look like:
1831 * [1505704373, 'The date must be after this date'],
1832 * [1506741172, 'The date must be before this date']
1835 * @param calendar_event $event The calendar event to get the time range for
1836 * @param stdClass $workshop The module instance to get the range from
1837 * @return array Returns an array with min and max date.
1839 function mod_workshop_core_calendar_get_valid_event_timestart_range(\calendar_event
$event, \stdClass
$workshop) : array {
1843 $phasesubmissionend = max($workshop->submissionstart
, $workshop->submissionend
);
1844 $phaseassessmentstart = min($workshop->assessmentstart
, $workshop->assessmentend
);
1845 if ($phaseassessmentstart == 0) {
1846 $phaseassessmentstart = max($workshop->assessmentstart
, $workshop->assessmentend
);
1849 switch ($event->eventtype
) {
1850 case WORKSHOP_EVENT_TYPE_SUBMISSION_OPEN
:
1851 if (!empty($workshop->submissionend
)) {
1853 $workshop->submissionend
- 1, // The submissionstart and submissionend cannot be exactly the same.
1854 get_string('submissionendbeforestart', 'mod_workshop')
1856 } else if ($phaseassessmentstart) {
1858 $phaseassessmentstart,
1859 get_string('phasesoverlap', 'mod_workshop')
1863 case WORKSHOP_EVENT_TYPE_SUBMISSION_CLOSE
:
1864 if (!empty($workshop->submissionstart
)) {
1866 $workshop->submissionstart +
1, // The submissionstart and submissionend cannot be exactly the same.
1867 get_string('submissionendbeforestart', 'mod_workshop')
1870 if ($phaseassessmentstart) {
1872 $phaseassessmentstart,
1873 get_string('phasesoverlap', 'mod_workshop')
1877 case WORKSHOP_EVENT_TYPE_ASSESSMENT_OPEN
:
1878 if ($phasesubmissionend) {
1880 $phasesubmissionend,
1881 get_string('phasesoverlap', 'mod_workshop')
1884 if (!empty($workshop->assessmentend
)) {
1886 $workshop->assessmentend
- 1, // The assessmentstart and assessmentend cannot be exactly the same.
1887 get_string('assessmentendbeforestart', 'mod_workshop')
1891 case WORKSHOP_EVENT_TYPE_ASSESSMENT_CLOSE
:
1892 if (!empty($workshop->assessmentstart
)) {
1894 $workshop->assessmentstart +
1, // The assessmentstart and assessmentend cannot be exactly the same.
1895 get_string('assessmentendbeforestart', 'mod_workshop')
1897 } else if ($phasesubmissionend) {
1899 $phasesubmissionend,
1900 get_string('phasesoverlap', 'mod_workshop')
1906 return [$mindate, $maxdate];
1910 * This function will update the workshop module according to the
1911 * event that has been modified.
1913 * @param \calendar_event $event
1914 * @param stdClass $workshop The module instance to get the range from
1916 function mod_workshop_core_calendar_event_timestart_updated(\calendar_event
$event, \stdClass
$workshop) : void
{
1919 $courseid = $event->courseid
;
1920 $modulename = $event->modulename
;
1921 $instanceid = $event->instance
;
1923 // Something weird going on. The event is for a different module so
1924 // we should ignore it.
1925 if ($modulename != 'workshop') {
1929 if ($workshop->id
!= $instanceid) {
1936 WORKSHOP_EVENT_TYPE_SUBMISSION_OPEN
,
1937 WORKSHOP_EVENT_TYPE_SUBMISSION_CLOSE
,
1938 WORKSHOP_EVENT_TYPE_ASSESSMENT_OPEN
,
1939 WORKSHOP_EVENT_TYPE_ASSESSMENT_CLOSE
1945 $coursemodule = get_fast_modinfo($courseid)->instances
[$modulename][$instanceid];
1946 $context = context_module
::instance($coursemodule->id
);
1948 // The user does not have the capability to modify this activity.
1949 if (!has_capability('moodle/course:manageactivities', $context)) {
1955 switch ($event->eventtype
) {
1956 case WORKSHOP_EVENT_TYPE_SUBMISSION_OPEN
:
1957 if ($event->timestart
!= $workshop->submissionstart
) {
1958 $workshop->submissionstart
= $event->timestart
;
1962 case WORKSHOP_EVENT_TYPE_SUBMISSION_CLOSE
:
1963 if ($event->timestart
!= $workshop->submissionend
) {
1964 $workshop->submissionend
= $event->timestart
;
1968 case WORKSHOP_EVENT_TYPE_ASSESSMENT_OPEN
:
1969 if ($event->timestart
!= $workshop->assessmentstart
) {
1970 $workshop->assessmentstart
= $event->timestart
;
1974 case WORKSHOP_EVENT_TYPE_ASSESSMENT_CLOSE
:
1975 if ($event->timestart
!= $workshop->assessmentend
) {
1976 $workshop->assessmentend
= $event->timestart
;
1983 $workshop->timemodified
= time();
1984 // Persist the assign instance changes.
1985 $DB->update_record('workshop', $workshop);
1986 $event = \core\event\course_module_updated
::create_from_cm($coursemodule, $context);
1991 ////////////////////////////////////////////////////////////////////////////////
1992 // Course reset API //
1993 ////////////////////////////////////////////////////////////////////////////////
1996 * Extends the course reset form with workshop specific settings.
1998 * @param MoodleQuickForm $mform
2000 function workshop_reset_course_form_definition($mform) {
2002 $mform->addElement('header', 'workshopheader', get_string('modulenameplural', 'mod_workshop'));
2004 $mform->addElement('advcheckbox', 'reset_workshop_submissions', get_string('resetsubmissions', 'mod_workshop'));
2005 $mform->addHelpButton('reset_workshop_submissions', 'resetsubmissions', 'mod_workshop');
2007 $mform->addElement('advcheckbox', 'reset_workshop_assessments', get_string('resetassessments', 'mod_workshop'));
2008 $mform->addHelpButton('reset_workshop_assessments', 'resetassessments', 'mod_workshop');
2009 $mform->disabledIf('reset_workshop_assessments', 'reset_workshop_submissions', 'checked');
2011 $mform->addElement('advcheckbox', 'reset_workshop_phase', get_string('resetphase', 'mod_workshop'));
2012 $mform->addHelpButton('reset_workshop_phase', 'resetphase', 'mod_workshop');
2016 * Provides default values for the workshop settings in the course reset form.
2018 * @param stdClass $course The course to be reset.
2020 function workshop_reset_course_form_defaults(stdClass
$course) {
2023 'reset_workshop_submissions' => 1,
2024 'reset_workshop_assessments' => 1,
2025 'reset_workshop_phase' => 1,
2032 * Performs the reset of all workshop instances in the course.
2034 * @param stdClass $data The actual course reset settings.
2035 * @return array List of results, each being array[(string)component, (string)item, (string)error]
2037 function workshop_reset_userdata(stdClass
$data) {
2040 // Any changes to the list of dates that needs to be rolled should be same during course restore and course reset.
2042 shift_course_mod_dates('workshop', array('submissionstart', 'submissionend', 'assessmentstart', 'assessmentend'),
2043 $data->timeshift
, $data->courseid
);
2045 $status[] = array('component' => get_string('modulenameplural', 'workshop'), 'item' => get_string('datechanged'),
2048 if (empty($data->reset_workshop_submissions
)
2049 and empty($data->reset_workshop_assessments
)
2050 and empty($data->reset_workshop_phase
) ) {
2051 // Nothing to do here.
2055 $workshoprecords = $DB->get_records('workshop', array('course' => $data->courseid
));
2057 if (empty($workshoprecords)) {
2058 // What a boring course - no workshops here!
2062 require_once($CFG->dirroot
. '/mod/workshop/locallib.php');
2064 $course = $DB->get_record('course', array('id' => $data->courseid
), '*', MUST_EXIST
);
2066 foreach ($workshoprecords as $workshoprecord) {
2067 $cm = get_coursemodule_from_instance('workshop', $workshoprecord->id
, $course->id
, false, MUST_EXIST
);
2068 $workshop = new workshop($workshoprecord, $cm, $course);
2069 $status = array_merge($status, $workshop->reset_userdata($data));
2076 * Get icon mapping for font-awesome.
2078 function mod_workshop_get_fontawesome_icon_map() {
2080 'mod_workshop:userplan/task-info' => 'fa-info text-info',
2081 'mod_workshop:userplan/task-todo' => 'fa-square-o',
2082 'mod_workshop:userplan/task-done' => 'fa-check text-success',
2083 'mod_workshop:userplan/task-fail' => 'fa-remove text-danger',
2088 * Check if the module has any update that affects the current user since a given time.
2090 * @param cm_info $cm course module data
2091 * @param int $from the time to check updates from
2092 * @param array $filter if we need to check only specific updates
2093 * @return stdClass an object with the different type of areas indicating if they were updated or not
2096 function workshop_check_updates_since(cm_info
$cm, $from, $filter = array()) {
2099 $updates = course_check_module_updates_since($cm, $from, array('instructauthors', 'instructreviewers', 'conclusion'), $filter);
2101 // Check if there are new submissions, assessments or assessments grades in the workshop.
2102 $updates->submissions
= (object) array('updated' => false);
2103 $updates->assessments
= (object) array('updated' => false);
2104 $updates->assessmentgrades
= (object) array('updated' => false);
2106 $select = 'workshopid = ? AND authorid = ? AND (timecreated > ? OR timegraded > ? OR timemodified > ?)';
2107 $params = array($cm->instance
, $USER->id
, $from, $from, $from);
2108 $submissions = $DB->get_records_select('workshop_submissions', $select, $params, '', 'id');
2109 if (!empty($submissions)) {
2110 $updates->submissions
->updated
= true;
2111 $updates->submissions
->itemids
= array_keys($submissions);
2114 // Get assessments updates (both submissions reviewed by me or reviews by others).
2115 $select = "SELECT a.id
2116 FROM {workshop_assessments} a
2117 JOIN {workshop_submissions} s ON a.submissionid = s.id
2118 WHERE s.workshopid = ? AND (a.timecreated > ? OR a.timemodified > ?) AND (s.authorid = ? OR a.reviewerid = ?)";
2119 $params = array($cm->instance
, $from, $from, $USER->id
, $USER->id
);
2120 $assessments = $DB->get_records_sql($select, $params);
2121 if (!empty($assessments)) {
2122 $updates->assessments
->updated
= true;
2123 $updates->assessments
->itemids
= array_keys($assessments);
2125 // Finally assessment aggregated grades.
2126 $select = 'workshopid = ? AND userid = ? AND timegraded > ?';
2127 $params = array($cm->instance
, $USER->id
, $from);
2128 $assessmentgrades = $DB->get_records_select('workshop_aggregations', $select, $params, '', 'id');
2129 if (!empty($assessmentgrades)) {
2130 $updates->assessmentgrades
->updated
= true;
2131 $updates->assessmentgrades
->itemids
= array_keys($assessmentgrades);
2134 // Now, teachers should see other students updates.
2135 $canviewallsubmissions = has_capability('mod/workshop:viewallsubmissions', $cm->context
);
2136 $canviewallassessments = has_capability('mod/workshop:viewallassessments', $cm->context
);
2137 if ($canviewallsubmissions ||
$canviewallassessments) {
2140 $inparams = array();
2141 // To filter by users in my groups when separated groups are forced.
2142 if (groups_get_activity_groupmode($cm) == SEPARATEGROUPS
) {
2143 $groupusers = array_keys(groups_get_activity_shared_group_members($cm));
2144 if (empty($groupusers)) {
2147 list($insql, $inparams) = $DB->get_in_or_equal($groupusers);
2150 if ($canviewallsubmissions) {
2151 $updates->usersubmissions
= (object) array('updated' => false);
2152 $select = 'workshopid = ? AND (timecreated > ? OR timegraded > ? OR timemodified > ?)';
2153 $params = array($cm->instance
, $from, $from, $from);
2154 if (!empty($insql)) {
2155 $select .= " AND authorid $insql";
2156 $params = array_merge($params, $inparams);
2158 $usersubmissions = $DB->get_records_select('workshop_submissions', $select, $params, '', 'id');
2159 if (!empty($usersubmissions)) {
2160 $updates->usersubmissions
->updated
= true;
2161 $updates->usersubmissions
->itemids
= array_keys($usersubmissions);
2165 if ($canviewallassessments) {
2166 $updates->userassessments
= (object) array('updated' => false);
2167 $select = "SELECT a.id
2168 FROM {workshop_assessments} a
2169 JOIN {workshop_submissions} s ON a.submissionid = s.id
2170 WHERE s.workshopid = ? AND (a.timecreated > ? OR a.timemodified > ?)";
2171 $params = array($cm->instance
, $from, $from);
2172 if (!empty($insql)) {
2173 $select .= " AND s.reviewerid $insql";
2174 $params = array_merge($params, $inparams);
2176 $userassessments = $DB->get_records_sql($select, $params);
2177 if (!empty($userassessments)) {
2178 $updates->userassessments
->updated
= true;
2179 $updates->userassessments
->itemids
= array_keys($userassessments);
2182 $updates->userassessmentgrades
= (object) array('updated' => false);
2183 $select = 'workshopid = ? AND timegraded > ?';
2184 $params = array($cm->instance
, $USER->id
);
2185 if (!empty($insql)) {
2186 $select .= " AND userid $insql";
2187 $params = array_merge($params, $inparams);
2189 $userassessmentgrades = $DB->get_records_select('workshop_aggregations', $select, $params, '', 'id');
2190 if (!empty($userassessmentgrades)) {
2191 $updates->userassessmentgrades
->updated
= true;
2192 $updates->userassessmentgrades
->itemids
= array_keys($userassessmentgrades);