[ Index ] |
PHP Cross Reference of moodle-2.8 |
[Summary view] [Print] [Text view]
1 <?php 2 3 // This file is part of Moodle - http://moodle.org/ 4 // 5 // Moodle is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // Moodle is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 // 15 // You should have received a copy of the GNU General Public License 16 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 17 18 /** 19 * Library of workshop module functions needed by Moodle core and other subsystems 20 * 21 * All the functions neeeded by Moodle core, gradebook, file subsystem etc 22 * are placed here. 23 * 24 * @package mod_workshop 25 * @copyright 2009 David Mudrak <[email protected]> 26 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 27 */ 28 29 defined('MOODLE_INTERNAL') || die(); 30 31 require_once($CFG->dirroot . '/calendar/lib.php'); 32 33 //////////////////////////////////////////////////////////////////////////////// 34 // Moodle core API // 35 //////////////////////////////////////////////////////////////////////////////// 36 37 /** 38 * Returns the information if the module supports a feature 39 * 40 * @see plugin_supports() in lib/moodlelib.php 41 * @param string $feature FEATURE_xx constant for requested feature 42 * @return mixed true if the feature is supported, null if unknown 43 */ 44 function workshop_supports($feature) { 45 switch($feature) { 46 case FEATURE_GRADE_HAS_GRADE: return true; 47 case FEATURE_GROUPS: return true; 48 case FEATURE_GROUPINGS: return true; 49 case FEATURE_MOD_INTRO: return true; 50 case FEATURE_BACKUP_MOODLE2: return true; 51 case FEATURE_COMPLETION_TRACKS_VIEWS: 52 return true; 53 case FEATURE_SHOW_DESCRIPTION: return true; 54 case FEATURE_PLAGIARISM: return true; 55 default: return null; 56 } 57 } 58 59 /** 60 * Saves a new instance of the workshop into the database 61 * 62 * Given an object containing all the necessary data, 63 * (defined by the form in mod_form.php) this function 64 * will save a new instance and return the id number 65 * of the new instance. 66 * 67 * @param stdClass $workshop An object from the form in mod_form.php 68 * @return int The id of the newly inserted workshop record 69 */ 70 function workshop_add_instance(stdclass $workshop) { 71 global $CFG, $DB; 72 require_once(dirname(__FILE__) . '/locallib.php'); 73 74 $workshop->phase = workshop::PHASE_SETUP; 75 $workshop->timecreated = time(); 76 $workshop->timemodified = $workshop->timecreated; 77 $workshop->useexamples = (int)!empty($workshop->useexamples); 78 $workshop->usepeerassessment = 1; 79 $workshop->useselfassessment = (int)!empty($workshop->useselfassessment); 80 $workshop->latesubmissions = (int)!empty($workshop->latesubmissions); 81 $workshop->phaseswitchassessment = (int)!empty($workshop->phaseswitchassessment); 82 $workshop->evaluation = 'best'; 83 84 // insert the new record so we get the id 85 $workshop->id = $DB->insert_record('workshop', $workshop); 86 87 // we need to use context now, so we need to make sure all needed info is already in db 88 $cmid = $workshop->coursemodule; 89 $DB->set_field('course_modules', 'instance', $workshop->id, array('id' => $cmid)); 90 $context = context_module::instance($cmid); 91 92 // process the custom wysiwyg editors 93 if ($draftitemid = $workshop->instructauthorseditor['itemid']) { 94 $workshop->instructauthors = file_save_draft_area_files($draftitemid, $context->id, 'mod_workshop', 'instructauthors', 95 0, workshop::instruction_editors_options($context), $workshop->instructauthorseditor['text']); 96 $workshop->instructauthorsformat = $workshop->instructauthorseditor['format']; 97 } 98 99 if ($draftitemid = $workshop->instructreviewerseditor['itemid']) { 100 $workshop->instructreviewers = file_save_draft_area_files($draftitemid, $context->id, 'mod_workshop', 'instructreviewers', 101 0, workshop::instruction_editors_options($context), $workshop->instructreviewerseditor['text']); 102 $workshop->instructreviewersformat = $workshop->instructreviewerseditor['format']; 103 } 104 105 if ($draftitemid = $workshop->conclusioneditor['itemid']) { 106 $workshop->conclusion = file_save_draft_area_files($draftitemid, $context->id, 'mod_workshop', 'conclusion', 107 0, workshop::instruction_editors_options($context), $workshop->conclusioneditor['text']); 108 $workshop->conclusionformat = $workshop->conclusioneditor['format']; 109 } 110 111 // re-save the record with the replaced URLs in editor fields 112 $DB->update_record('workshop', $workshop); 113 114 // create gradebook items 115 workshop_grade_item_update($workshop); 116 workshop_grade_item_category_update($workshop); 117 118 // create calendar events 119 workshop_calendar_update($workshop, $workshop->coursemodule); 120 121 return $workshop->id; 122 } 123 124 /** 125 * Given an object containing all the necessary data, 126 * (defined by the form in mod_form.php) this function 127 * will update an existing instance with new data. 128 * 129 * @param stdClass $workshop An object from the form in mod_form.php 130 * @return bool success 131 */ 132 function workshop_update_instance(stdclass $workshop) { 133 global $CFG, $DB; 134 require_once(dirname(__FILE__) . '/locallib.php'); 135 136 $workshop->timemodified = time(); 137 $workshop->id = $workshop->instance; 138 $workshop->useexamples = (int)!empty($workshop->useexamples); 139 $workshop->usepeerassessment = 1; 140 $workshop->useselfassessment = (int)!empty($workshop->useselfassessment); 141 $workshop->latesubmissions = (int)!empty($workshop->latesubmissions); 142 $workshop->phaseswitchassessment = (int)!empty($workshop->phaseswitchassessment); 143 144 // todo - if the grading strategy is being changed, we may want to replace all aggregated peer grades with nulls 145 146 $DB->update_record('workshop', $workshop); 147 $context = context_module::instance($workshop->coursemodule); 148 149 // process the custom wysiwyg editors 150 if ($draftitemid = $workshop->instructauthorseditor['itemid']) { 151 $workshop->instructauthors = file_save_draft_area_files($draftitemid, $context->id, 'mod_workshop', 'instructauthors', 152 0, workshop::instruction_editors_options($context), $workshop->instructauthorseditor['text']); 153 $workshop->instructauthorsformat = $workshop->instructauthorseditor['format']; 154 } 155 156 if ($draftitemid = $workshop->instructreviewerseditor['itemid']) { 157 $workshop->instructreviewers = file_save_draft_area_files($draftitemid, $context->id, 'mod_workshop', 'instructreviewers', 158 0, workshop::instruction_editors_options($context), $workshop->instructreviewerseditor['text']); 159 $workshop->instructreviewersformat = $workshop->instructreviewerseditor['format']; 160 } 161 162 if ($draftitemid = $workshop->conclusioneditor['itemid']) { 163 $workshop->conclusion = file_save_draft_area_files($draftitemid, $context->id, 'mod_workshop', 'conclusion', 164 0, workshop::instruction_editors_options($context), $workshop->conclusioneditor['text']); 165 $workshop->conclusionformat = $workshop->conclusioneditor['format']; 166 } 167 168 // re-save the record with the replaced URLs in editor fields 169 $DB->update_record('workshop', $workshop); 170 171 // update gradebook items 172 workshop_grade_item_update($workshop); 173 workshop_grade_item_category_update($workshop); 174 175 // update calendar events 176 workshop_calendar_update($workshop, $workshop->coursemodule); 177 178 return true; 179 } 180 181 /** 182 * Given an ID of an instance of this module, 183 * this function will permanently delete the instance 184 * and any data that depends on it. 185 * 186 * @param int $id Id of the module instance 187 * @return boolean Success/Failure 188 */ 189 function workshop_delete_instance($id) { 190 global $CFG, $DB; 191 require_once($CFG->libdir.'/gradelib.php'); 192 193 if (! $workshop = $DB->get_record('workshop', array('id' => $id))) { 194 return false; 195 } 196 197 // delete all associated aggregations 198 $DB->delete_records('workshop_aggregations', array('workshopid' => $workshop->id)); 199 200 // get the list of ids of all submissions 201 $submissions = $DB->get_records('workshop_submissions', array('workshopid' => $workshop->id), '', 'id'); 202 203 // get the list of all allocated assessments 204 $assessments = $DB->get_records_list('workshop_assessments', 'submissionid', array_keys($submissions), '', 'id'); 205 206 // delete the associated records from the workshop core tables 207 $DB->delete_records_list('workshop_grades', 'assessmentid', array_keys($assessments)); 208 $DB->delete_records_list('workshop_assessments', 'id', array_keys($assessments)); 209 $DB->delete_records_list('workshop_submissions', 'id', array_keys($submissions)); 210 211 // call the static clean-up methods of all available subplugins 212 $strategies = core_component::get_plugin_list('workshopform'); 213 foreach ($strategies as $strategy => $path) { 214 require_once ($path.'/lib.php'); 215 $classname = 'workshop_'.$strategy.'_strategy'; 216 call_user_func($classname.'::delete_instance', $workshop->id); 217 } 218 219 $allocators = core_component::get_plugin_list('workshopallocation'); 220 foreach ($allocators as $allocator => $path) { 221 require_once ($path.'/lib.php'); 222 $classname = 'workshop_'.$allocator.'_allocator'; 223 call_user_func($classname.'::delete_instance', $workshop->id); 224 } 225 226 $evaluators = core_component::get_plugin_list('workshopeval'); 227 foreach ($evaluators as $evaluator => $path) { 228 require_once ($path.'/lib.php'); 229 $classname = 'workshop_'.$evaluator.'_evaluation'; 230 call_user_func($classname.'::delete_instance', $workshop->id); 231 } 232 233 // delete the calendar events 234 $events = $DB->get_records('event', array('modulename' => 'workshop', 'instance' => $workshop->id)); 235 foreach ($events as $event) { 236 $event = calendar_event::load($event); 237 $event->delete(); 238 } 239 240 // finally remove the workshop record itself 241 $DB->delete_records('workshop', array('id' => $workshop->id)); 242 243 // gradebook cleanup 244 grade_update('mod/workshop', $workshop->course, 'mod', 'workshop', $workshop->id, 0, null, array('deleted' => true)); 245 grade_update('mod/workshop', $workshop->course, 'mod', 'workshop', $workshop->id, 1, null, array('deleted' => true)); 246 247 return true; 248 } 249 250 /** 251 * Return a small object with summary information about what a 252 * user has done with a given particular instance of this module 253 * Used for user activity reports. 254 * $return->time = the time they did it 255 * $return->info = a short text description 256 * 257 * @param stdClass $course The course record. 258 * @param stdClass $user The user record. 259 * @param cm_info|stdClass $mod The course module info object or record. 260 * @param stdClass $workshop The workshop instance record. 261 * @return stdclass|null 262 */ 263 function workshop_user_outline($course, $user, $mod, $workshop) { 264 global $CFG, $DB; 265 require_once($CFG->libdir.'/gradelib.php'); 266 267 $grades = grade_get_grades($course->id, 'mod', 'workshop', $workshop->id, $user->id); 268 269 $submissiongrade = null; 270 $assessmentgrade = null; 271 272 $info = ''; 273 $time = 0; 274 275 if (!empty($grades->items[0]->grades)) { 276 $submissiongrade = reset($grades->items[0]->grades); 277 $info .= get_string('submissiongrade', 'workshop') . ': ' . $submissiongrade->str_long_grade . html_writer::empty_tag('br'); 278 $time = max($time, $submissiongrade->dategraded); 279 } 280 if (!empty($grades->items[1]->grades)) { 281 $assessmentgrade = reset($grades->items[1]->grades); 282 $info .= get_string('gradinggrade', 'workshop') . ': ' . $assessmentgrade->str_long_grade; 283 $time = max($time, $assessmentgrade->dategraded); 284 } 285 286 if (!empty($info) and !empty($time)) { 287 $return = new stdclass(); 288 $return->time = $time; 289 $return->info = $info; 290 return $return; 291 } 292 293 return null; 294 } 295 296 /** 297 * Print a detailed representation of what a user has done with 298 * a given particular instance of this module, for user activity reports. 299 * 300 * @param stdClass $course The course record. 301 * @param stdClass $user The user record. 302 * @param cm_info|stdClass $mod The course module info object or record. 303 * @param stdClass $workshop The workshop instance record. 304 * @return string HTML 305 */ 306 function workshop_user_complete($course, $user, $mod, $workshop) { 307 global $CFG, $DB, $OUTPUT; 308 require_once(dirname(__FILE__).'/locallib.php'); 309 require_once($CFG->libdir.'/gradelib.php'); 310 311 $workshop = new workshop($workshop, $mod, $course); 312 $grades = grade_get_grades($course->id, 'mod', 'workshop', $workshop->id, $user->id); 313 314 if (!empty($grades->items[0]->grades)) { 315 $submissiongrade = reset($grades->items[0]->grades); 316 $info = get_string('submissiongrade', 'workshop') . ': ' . $submissiongrade->str_long_grade; 317 echo html_writer::tag('li', $info, array('class'=>'submissiongrade')); 318 } 319 if (!empty($grades->items[1]->grades)) { 320 $assessmentgrade = reset($grades->items[1]->grades); 321 $info = get_string('gradinggrade', 'workshop') . ': ' . $assessmentgrade->str_long_grade; 322 echo html_writer::tag('li', $info, array('class'=>'gradinggrade')); 323 } 324 325 if (has_capability('mod/workshop:viewallsubmissions', $workshop->context)) { 326 $canviewsubmission = true; 327 if (groups_get_activity_groupmode($workshop->cm) == SEPARATEGROUPS) { 328 // user must have accessallgroups or share at least one group with the submission author 329 if (!has_capability('moodle/site:accessallgroups', $workshop->context)) { 330 $usersgroups = groups_get_activity_allowed_groups($workshop->cm); 331 $authorsgroups = groups_get_all_groups($workshop->course->id, $user->id, $workshop->cm->groupingid, 'g.id'); 332 $sharedgroups = array_intersect_key($usersgroups, $authorsgroups); 333 if (empty($sharedgroups)) { 334 $canviewsubmission = false; 335 } 336 } 337 } 338 if ($canviewsubmission and $submission = $workshop->get_submission_by_author($user->id)) { 339 $title = format_string($submission->title); 340 $url = $workshop->submission_url($submission->id); 341 $link = html_writer::link($url, $title); 342 $info = get_string('submission', 'workshop').': '.$link; 343 echo html_writer::tag('li', $info, array('class'=>'submission')); 344 } 345 } 346 347 if (has_capability('mod/workshop:viewallassessments', $workshop->context)) { 348 if ($assessments = $workshop->get_assessments_by_reviewer($user->id)) { 349 foreach ($assessments as $assessment) { 350 $a = new stdclass(); 351 $a->submissionurl = $workshop->submission_url($assessment->submissionid)->out(); 352 $a->assessmenturl = $workshop->assess_url($assessment->id)->out(); 353 $a->submissiontitle = s($assessment->submissiontitle); 354 echo html_writer::tag('li', get_string('assessmentofsubmission', 'workshop', $a)); 355 } 356 } 357 } 358 } 359 360 /** 361 * Given a course and a time, this module should find recent activity 362 * that has occurred in workshop activities and print it out. 363 * Return true if there was output, or false is there was none. 364 * 365 * @param stdClass $course 366 * @param bool $viewfullnames 367 * @param int $timestart 368 * @return boolean 369 */ 370 function workshop_print_recent_activity($course, $viewfullnames, $timestart) { 371 global $CFG, $USER, $DB, $OUTPUT; 372 373 $authoramefields = get_all_user_name_fields(true, 'author', null, 'author'); 374 $reviewerfields = get_all_user_name_fields(true, 'reviewer', null, 'reviewer'); 375 376 $sql = "SELECT s.id AS submissionid, s.title AS submissiontitle, s.timemodified AS submissionmodified, 377 author.id AS authorid, $authoramefields, a.id AS assessmentid, a.timemodified AS assessmentmodified, 378 reviewer.id AS reviewerid, $reviewerfields, cm.id AS cmid 379 FROM {workshop} w 380 INNER JOIN {course_modules} cm ON cm.instance = w.id 381 INNER JOIN {modules} md ON md.id = cm.module 382 INNER JOIN {workshop_submissions} s ON s.workshopid = w.id 383 INNER JOIN {user} author ON s.authorid = author.id 384 LEFT JOIN {workshop_assessments} a ON a.submissionid = s.id 385 LEFT JOIN {user} reviewer ON a.reviewerid = reviewer.id 386 WHERE cm.course = ? 387 AND md.name = 'workshop' 388 AND s.example = 0 389 AND (s.timemodified > ? OR a.timemodified > ?) 390 ORDER BY s.timemodified"; 391 392 $rs = $DB->get_recordset_sql($sql, array($course->id, $timestart, $timestart)); 393 394 $modinfo = get_fast_modinfo($course); // reference needed because we might load the groups 395 396 $submissions = array(); // recent submissions indexed by submission id 397 $assessments = array(); // recent assessments indexed by assessment id 398 $users = array(); 399 400 foreach ($rs as $activity) { 401 if (!array_key_exists($activity->cmid, $modinfo->cms)) { 402 // this should not happen but just in case 403 continue; 404 } 405 406 $cm = $modinfo->cms[$activity->cmid]; 407 if (!$cm->uservisible) { 408 continue; 409 } 410 411 // remember all user names we can use later 412 if (empty($users[$activity->authorid])) { 413 $u = new stdclass(); 414 $users[$activity->authorid] = username_load_fields_from_object($u, $activity, 'author'); 415 } 416 if ($activity->reviewerid and empty($users[$activity->reviewerid])) { 417 $u = new stdclass(); 418 $users[$activity->reviewerid] = username_load_fields_from_object($u, $activity, 'reviewer'); 419 } 420 421 $context = context_module::instance($cm->id); 422 $groupmode = groups_get_activity_groupmode($cm, $course); 423 424 if ($activity->submissionmodified > $timestart and empty($submissions[$activity->submissionid])) { 425 $s = new stdclass(); 426 $s->title = $activity->submissiontitle; 427 $s->authorid = $activity->authorid; 428 $s->timemodified = $activity->submissionmodified; 429 $s->cmid = $activity->cmid; 430 if ($activity->authorid == $USER->id || has_capability('mod/workshop:viewauthornames', $context)) { 431 $s->authornamevisible = true; 432 } else { 433 $s->authornamevisible = false; 434 } 435 436 // the following do-while wrapper allows to break from deeply nested if-statements 437 do { 438 if ($s->authorid === $USER->id) { 439 // own submissions always visible 440 $submissions[$activity->submissionid] = $s; 441 break; 442 } 443 444 if (has_capability('mod/workshop:viewallsubmissions', $context)) { 445 if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) { 446 if (isguestuser()) { 447 // shortcut - guest user does not belong into any group 448 break; 449 } 450 451 // this might be slow - show only submissions by users who share group with me in this cm 452 if (!$modinfo->get_groups($cm->groupingid)) { 453 break; 454 } 455 $authorsgroups = groups_get_all_groups($course->id, $s->authorid, $cm->groupingid); 456 if (is_array($authorsgroups)) { 457 $authorsgroups = array_keys($authorsgroups); 458 $intersect = array_intersect($authorsgroups, $modinfo->get_groups($cm->groupingid)); 459 if (empty($intersect)) { 460 break; 461 } else { 462 // can see all submissions and shares a group with the author 463 $submissions[$activity->submissionid] = $s; 464 break; 465 } 466 } 467 468 } else { 469 // can see all submissions from all groups 470 $submissions[$activity->submissionid] = $s; 471 } 472 } 473 } while (0); 474 } 475 476 if ($activity->assessmentmodified > $timestart and empty($assessments[$activity->assessmentid])) { 477 $a = new stdclass(); 478 $a->submissionid = $activity->submissionid; 479 $a->submissiontitle = $activity->submissiontitle; 480 $a->reviewerid = $activity->reviewerid; 481 $a->timemodified = $activity->assessmentmodified; 482 $a->cmid = $activity->cmid; 483 if ($activity->reviewerid == $USER->id || has_capability('mod/workshop:viewreviewernames', $context)) { 484 $a->reviewernamevisible = true; 485 } else { 486 $a->reviewernamevisible = false; 487 } 488 489 // the following do-while wrapper allows to break from deeply nested if-statements 490 do { 491 if ($a->reviewerid === $USER->id) { 492 // own assessments always visible 493 $assessments[$activity->assessmentid] = $a; 494 break; 495 } 496 497 if (has_capability('mod/workshop:viewallassessments', $context)) { 498 if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) { 499 if (isguestuser()) { 500 // shortcut - guest user does not belong into any group 501 break; 502 } 503 504 // this might be slow - show only submissions by users who share group with me in this cm 505 if (!$modinfo->get_groups($cm->groupingid)) { 506 break; 507 } 508 $reviewersgroups = groups_get_all_groups($course->id, $a->reviewerid, $cm->groupingid); 509 if (is_array($reviewersgroups)) { 510 $reviewersgroups = array_keys($reviewersgroups); 511 $intersect = array_intersect($reviewersgroups, $modinfo->get_groups($cm->groupingid)); 512 if (empty($intersect)) { 513 break; 514 } else { 515 // can see all assessments and shares a group with the reviewer 516 $assessments[$activity->assessmentid] = $a; 517 break; 518 } 519 } 520 521 } else { 522 // can see all assessments from all groups 523 $assessments[$activity->assessmentid] = $a; 524 } 525 } 526 } while (0); 527 } 528 } 529 $rs->close(); 530 531 $shown = false; 532 533 if (!empty($submissions)) { 534 $shown = true; 535 echo $OUTPUT->heading(get_string('recentsubmissions', 'workshop'), 3); 536 foreach ($submissions as $id => $submission) { 537 $link = new moodle_url('/mod/workshop/submission.php', array('id'=>$id, 'cmid'=>$submission->cmid)); 538 if ($submission->authornamevisible) { 539 $author = $users[$submission->authorid]; 540 } else { 541 $author = null; 542 } 543 print_recent_activity_note($submission->timemodified, $author, $submission->title, $link->out(), false, $viewfullnames); 544 } 545 } 546 547 if (!empty($assessments)) { 548 $shown = true; 549 echo $OUTPUT->heading(get_string('recentassessments', 'workshop'), 3); 550 core_collator::asort_objects_by_property($assessments, 'timemodified'); 551 foreach ($assessments as $id => $assessment) { 552 $link = new moodle_url('/mod/workshop/assessment.php', array('asid' => $id)); 553 if ($assessment->reviewernamevisible) { 554 $reviewer = $users[$assessment->reviewerid]; 555 } else { 556 $reviewer = null; 557 } 558 print_recent_activity_note($assessment->timemodified, $reviewer, $assessment->submissiontitle, $link->out(), false, $viewfullnames); 559 } 560 } 561 562 if ($shown) { 563 return true; 564 } 565 566 return false; 567 } 568 569 /** 570 * Returns all activity in course workshops since a given time 571 * 572 * @param array $activities sequentially indexed array of objects 573 * @param int $index 574 * @param int $timestart 575 * @param int $courseid 576 * @param int $cmid 577 * @param int $userid defaults to 0 578 * @param int $groupid defaults to 0 579 * @return void adds items into $activities and increases $index 580 */ 581 function workshop_get_recent_mod_activity(&$activities, &$index, $timestart, $courseid, $cmid, $userid=0, $groupid=0) { 582 global $CFG, $COURSE, $USER, $DB; 583 584 if ($COURSE->id == $courseid) { 585 $course = $COURSE; 586 } else { 587 $course = $DB->get_record('course', array('id'=>$courseid)); 588 } 589 590 $modinfo = get_fast_modinfo($course); 591 592 $cm = $modinfo->cms[$cmid]; 593 594 $params = array(); 595 if ($userid) { 596 $userselect = "AND (author.id = :authorid OR reviewer.id = :reviewerid)"; 597 $params['authorid'] = $userid; 598 $params['reviewerid'] = $userid; 599 } else { 600 $userselect = ""; 601 } 602 603 if ($groupid) { 604 $groupselect = "AND (authorgroupmembership.groupid = :authorgroupid OR reviewergroupmembership.groupid = :reviewergroupid)"; 605 $groupjoin = "LEFT JOIN {groups_members} authorgroupmembership ON authorgroupmembership.userid = author.id 606 LEFT JOIN {groups_members} reviewergroupmembership ON reviewergroupmembership.userid = reviewer.id"; 607 $params['authorgroupid'] = $groupid; 608 $params['reviewergroupid'] = $groupid; 609 } else { 610 $groupselect = ""; 611 $groupjoin = ""; 612 } 613 614 $params['cminstance'] = $cm->instance; 615 $params['submissionmodified'] = $timestart; 616 $params['assessmentmodified'] = $timestart; 617 618 $authornamefields = get_all_user_name_fields(true, 'author', null, 'author'); 619 $reviewerfields = get_all_user_name_fields(true, 'reviewer', null, 'reviewer'); 620 621 $sql = "SELECT s.id AS submissionid, s.title AS submissiontitle, s.timemodified AS submissionmodified, 622 author.id AS authorid, $authornamefields, author.picture AS authorpicture, author.imagealt AS authorimagealt, 623 author.email AS authoremail, a.id AS assessmentid, a.timemodified AS assessmentmodified, 624 reviewer.id AS reviewerid, $reviewerfields, reviewer.picture AS reviewerpicture, 625 reviewer.imagealt AS reviewerimagealt, reviewer.email AS revieweremail 626 FROM {workshop_submissions} s 627 INNER JOIN {workshop} w ON s.workshopid = w.id 628 INNER JOIN {user} author ON s.authorid = author.id 629 LEFT JOIN {workshop_assessments} a ON a.submissionid = s.id 630 LEFT JOIN {user} reviewer ON a.reviewerid = reviewer.id 631 $groupjoin 632 WHERE w.id = :cminstance 633 AND s.example = 0 634 $userselect $groupselect 635 AND (s.timemodified > :submissionmodified OR a.timemodified > :assessmentmodified) 636 ORDER BY s.timemodified ASC, a.timemodified ASC"; 637 638 $rs = $DB->get_recordset_sql($sql, $params); 639 640 $groupmode = groups_get_activity_groupmode($cm, $course); 641 $context = context_module::instance($cm->id); 642 $grader = has_capability('moodle/grade:viewall', $context); 643 $accessallgroups = has_capability('moodle/site:accessallgroups', $context); 644 $viewauthors = has_capability('mod/workshop:viewauthornames', $context); 645 $viewreviewers = has_capability('mod/workshop:viewreviewernames', $context); 646 647 $submissions = array(); // recent submissions indexed by submission id 648 $assessments = array(); // recent assessments indexed by assessment id 649 $users = array(); 650 651 foreach ($rs as $activity) { 652 653 // remember all user names we can use later 654 if (empty($users[$activity->authorid])) { 655 $u = new stdclass(); 656 $additionalfields = explode(',', user_picture::fields()); 657 $u = username_load_fields_from_object($u, $activity, 'author', $additionalfields); 658 $users[$activity->authorid] = $u; 659 } 660 if ($activity->reviewerid and empty($users[$activity->reviewerid])) { 661 $u = new stdclass(); 662 $additionalfields = explode(',', user_picture::fields()); 663 $u = username_load_fields_from_object($u, $activity, 'reviewer', $additionalfields); 664 $users[$activity->reviewerid] = $u; 665 } 666 667 if ($activity->submissionmodified > $timestart and empty($submissions[$activity->submissionid])) { 668 $s = new stdclass(); 669 $s->id = $activity->submissionid; 670 $s->title = $activity->submissiontitle; 671 $s->authorid = $activity->authorid; 672 $s->timemodified = $activity->submissionmodified; 673 if ($activity->authorid == $USER->id || has_capability('mod/workshop:viewauthornames', $context)) { 674 $s->authornamevisible = true; 675 } else { 676 $s->authornamevisible = false; 677 } 678 679 // the following do-while wrapper allows to break from deeply nested if-statements 680 do { 681 if ($s->authorid === $USER->id) { 682 // own submissions always visible 683 $submissions[$activity->submissionid] = $s; 684 break; 685 } 686 687 if (has_capability('mod/workshop:viewallsubmissions', $context)) { 688 if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) { 689 if (isguestuser()) { 690 // shortcut - guest user does not belong into any group 691 break; 692 } 693 694 // this might be slow - show only submissions by users who share group with me in this cm 695 if (!$modinfo->get_groups($cm->groupingid)) { 696 break; 697 } 698 $authorsgroups = groups_get_all_groups($course->id, $s->authorid, $cm->groupingid); 699 if (is_array($authorsgroups)) { 700 $authorsgroups = array_keys($authorsgroups); 701 $intersect = array_intersect($authorsgroups, $modinfo->get_groups($cm->groupingid)); 702 if (empty($intersect)) { 703 break; 704 } else { 705 // can see all submissions and shares a group with the author 706 $submissions[$activity->submissionid] = $s; 707 break; 708 } 709 } 710 711 } else { 712 // can see all submissions from all groups 713 $submissions[$activity->submissionid] = $s; 714 } 715 } 716 } while (0); 717 } 718 719 if ($activity->assessmentmodified > $timestart and empty($assessments[$activity->assessmentid])) { 720 $a = new stdclass(); 721 $a->id = $activity->assessmentid; 722 $a->submissionid = $activity->submissionid; 723 $a->submissiontitle = $activity->submissiontitle; 724 $a->reviewerid = $activity->reviewerid; 725 $a->timemodified = $activity->assessmentmodified; 726 if ($activity->reviewerid == $USER->id || has_capability('mod/workshop:viewreviewernames', $context)) { 727 $a->reviewernamevisible = true; 728 } else { 729 $a->reviewernamevisible = false; 730 } 731 732 // the following do-while wrapper allows to break from deeply nested if-statements 733 do { 734 if ($a->reviewerid === $USER->id) { 735 // own assessments always visible 736 $assessments[$activity->assessmentid] = $a; 737 break; 738 } 739 740 if (has_capability('mod/workshop:viewallassessments', $context)) { 741 if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) { 742 if (isguestuser()) { 743 // shortcut - guest user does not belong into any group 744 break; 745 } 746 747 // this might be slow - show only submissions by users who share group with me in this cm 748 if (!$modinfo->get_groups($cm->groupingid)) { 749 break; 750 } 751 $reviewersgroups = groups_get_all_groups($course->id, $a->reviewerid, $cm->groupingid); 752 if (is_array($reviewersgroups)) { 753 $reviewersgroups = array_keys($reviewersgroups); 754 $intersect = array_intersect($reviewersgroups, $modinfo->get_groups($cm->groupingid)); 755 if (empty($intersect)) { 756 break; 757 } else { 758 // can see all assessments and shares a group with the reviewer 759 $assessments[$activity->assessmentid] = $a; 760 break; 761 } 762 } 763 764 } else { 765 // can see all assessments from all groups 766 $assessments[$activity->assessmentid] = $a; 767 } 768 } 769 } while (0); 770 } 771 } 772 $rs->close(); 773 774 $workshopname = format_string($cm->name, true); 775 776 if ($grader) { 777 require_once($CFG->libdir.'/gradelib.php'); 778 $grades = grade_get_grades($courseid, 'mod', 'workshop', $cm->instance, array_keys($users)); 779 } 780 781 foreach ($submissions as $submission) { 782 $tmpactivity = new stdclass(); 783 $tmpactivity->type = 'workshop'; 784 $tmpactivity->cmid = $cm->id; 785 $tmpactivity->name = $workshopname; 786 $tmpactivity->sectionnum = $cm->sectionnum; 787 $tmpactivity->timestamp = $submission->timemodified; 788 $tmpactivity->subtype = 'submission'; 789 $tmpactivity->content = $submission; 790 if ($grader) { 791 $tmpactivity->grade = $grades->items[0]->grades[$submission->authorid]->str_long_grade; 792 } 793 if ($submission->authornamevisible and !empty($users[$submission->authorid])) { 794 $tmpactivity->user = $users[$submission->authorid]; 795 } 796 $activities[$index++] = $tmpactivity; 797 } 798 799 foreach ($assessments as $assessment) { 800 $tmpactivity = new stdclass(); 801 $tmpactivity->type = 'workshop'; 802 $tmpactivity->cmid = $cm->id; 803 $tmpactivity->name = $workshopname; 804 $tmpactivity->sectionnum = $cm->sectionnum; 805 $tmpactivity->timestamp = $assessment->timemodified; 806 $tmpactivity->subtype = 'assessment'; 807 $tmpactivity->content = $assessment; 808 if ($grader) { 809 $tmpactivity->grade = $grades->items[1]->grades[$assessment->reviewerid]->str_long_grade; 810 } 811 if ($assessment->reviewernamevisible and !empty($users[$assessment->reviewerid])) { 812 $tmpactivity->user = $users[$assessment->reviewerid]; 813 } 814 $activities[$index++] = $tmpactivity; 815 } 816 } 817 818 /** 819 * Print single activity item prepared by {@see workshop_get_recent_mod_activity()} 820 */ 821 function workshop_print_recent_mod_activity($activity, $courseid, $detail, $modnames, $viewfullnames) { 822 global $CFG, $OUTPUT; 823 824 if (!empty($activity->user)) { 825 echo html_writer::tag('div', $OUTPUT->user_picture($activity->user, array('courseid'=>$courseid)), 826 array('style' => 'float: left; padding: 7px;')); 827 } 828 829 if ($activity->subtype == 'submission') { 830 echo html_writer::start_tag('div', array('class'=>'submission', 'style'=>'padding: 7px; float:left;')); 831 832 if ($detail) { 833 echo html_writer::start_tag('h4', array('class'=>'workshop')); 834 $url = new moodle_url('/mod/workshop/view.php', array('id'=>$activity->cmid)); 835 $name = s($activity->name); 836 echo html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('icon', $activity->type), 'class'=>'icon', 'alt'=>$name)); 837 echo ' ' . $modnames[$activity->type]; 838 echo html_writer::link($url, $name, array('class'=>'name', 'style'=>'margin-left: 5px')); 839 echo html_writer::end_tag('h4'); 840 } 841 842 echo html_writer::start_tag('div', array('class'=>'title')); 843 $url = new moodle_url('/mod/workshop/submission.php', array('cmid'=>$activity->cmid, 'id'=>$activity->content->id)); 844 $name = s($activity->content->title); 845 echo html_writer::tag('strong', html_writer::link($url, $name)); 846 echo html_writer::end_tag('div'); 847 848 if (!empty($activity->user)) { 849 echo html_writer::start_tag('div', array('class'=>'user')); 850 $url = new moodle_url('/user/view.php', array('id'=>$activity->user->id, 'course'=>$courseid)); 851 $name = fullname($activity->user); 852 $link = html_writer::link($url, $name); 853 echo get_string('submissionby', 'workshop', $link); 854 echo ' - '.userdate($activity->timestamp); 855 echo html_writer::end_tag('div'); 856 } else { 857 echo html_writer::start_tag('div', array('class'=>'anonymous')); 858 echo get_string('submission', 'workshop'); 859 echo ' - '.userdate($activity->timestamp); 860 echo html_writer::end_tag('div'); 861 } 862 863 echo html_writer::end_tag('div'); 864 } 865 866 if ($activity->subtype == 'assessment') { 867 echo html_writer::start_tag('div', array('class'=>'assessment', 'style'=>'padding: 7px; float:left;')); 868 869 if ($detail) { 870 echo html_writer::start_tag('h4', array('class'=>'workshop')); 871 $url = new moodle_url('/mod/workshop/view.php', array('id'=>$activity->cmid)); 872 $name = s($activity->name); 873 echo html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('icon', $activity->type), 'class'=>'icon', 'alt'=>$name)); 874 echo ' ' . $modnames[$activity->type]; 875 echo html_writer::link($url, $name, array('class'=>'name', 'style'=>'margin-left: 5px')); 876 echo html_writer::end_tag('h4'); 877 } 878 879 echo html_writer::start_tag('div', array('class'=>'title')); 880 $url = new moodle_url('/mod/workshop/assessment.php', array('asid'=>$activity->content->id)); 881 $name = s($activity->content->submissiontitle); 882 echo html_writer::tag('em', html_writer::link($url, $name)); 883 echo html_writer::end_tag('div'); 884 885 if (!empty($activity->user)) { 886 echo html_writer::start_tag('div', array('class'=>'user')); 887 $url = new moodle_url('/user/view.php', array('id'=>$activity->user->id, 'course'=>$courseid)); 888 $name = fullname($activity->user); 889 $link = html_writer::link($url, $name); 890 echo get_string('assessmentbyfullname', 'workshop', $link); 891 echo ' - '.userdate($activity->timestamp); 892 echo html_writer::end_tag('div'); 893 } else { 894 echo html_writer::start_tag('div', array('class'=>'anonymous')); 895 echo get_string('assessment', 'workshop'); 896 echo ' - '.userdate($activity->timestamp); 897 echo html_writer::end_tag('div'); 898 } 899 900 echo html_writer::end_tag('div'); 901 } 902 903 echo html_writer::empty_tag('br', array('style'=>'clear:both')); 904 } 905 906 /** 907 * Regular jobs to execute via cron 908 * 909 * @return boolean true on success, false otherwise 910 */ 911 function workshop_cron() { 912 global $CFG, $DB; 913 914 $now = time(); 915 916 mtrace(' processing workshop subplugins ...'); 917 cron_execute_plugin_type('workshopallocation', 'workshop allocation methods'); 918 919 // now when the scheduled allocator had a chance to do its job, check if there 920 // are some workshops to switch into the assessment phase 921 $workshops = $DB->get_records_select("workshop", 922 "phase = 20 AND phaseswitchassessment = 1 AND submissionend > 0 AND submissionend < ?", array($now)); 923 924 if (!empty($workshops)) { 925 mtrace('Processing automatic assessment phase switch in '.count($workshops).' workshop(s) ... ', ''); 926 require_once($CFG->dirroot.'/mod/workshop/locallib.php'); 927 foreach ($workshops as $workshop) { 928 $cm = get_coursemodule_from_instance('workshop', $workshop->id, $workshop->course, false, MUST_EXIST); 929 $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST); 930 $workshop = new workshop($workshop, $cm, $course); 931 $workshop->switch_phase(workshop::PHASE_ASSESSMENT); 932 933 $params = array( 934 'objectid' => $workshop->id, 935 'context' => $workshop->context, 936 'courseid' => $workshop->course->id, 937 'other' => array( 938 'workshopphase' => $workshop->phase 939 ) 940 ); 941 $event = \mod_workshop\event\phase_switched::create($params); 942 $event->trigger(); 943 944 // disable the automatic switching now so that it is not executed again by accident 945 // if the teacher changes the phase back to the submission one 946 $DB->set_field('workshop', 'phaseswitchassessment', 0, array('id' => $workshop->id)); 947 948 // todo inform the teachers 949 } 950 mtrace('done'); 951 } 952 953 return true; 954 } 955 956 /** 957 * Is a given scale used by the instance of workshop? 958 * 959 * The function asks all installed grading strategy subplugins. The workshop 960 * core itself does not use scales. Both grade for submission and grade for 961 * assessments do not use scales. 962 * 963 * @param int $workshopid id of workshop instance 964 * @param int $scaleid id of the scale to check 965 * @return bool 966 */ 967 function workshop_scale_used($workshopid, $scaleid) { 968 global $CFG; // other files included from here 969 970 $strategies = core_component::get_plugin_list('workshopform'); 971 foreach ($strategies as $strategy => $strategypath) { 972 $strategylib = $strategypath . '/lib.php'; 973 if (is_readable($strategylib)) { 974 require_once($strategylib); 975 } else { 976 throw new coding_exception('the grading forms subplugin must contain library ' . $strategylib); 977 } 978 $classname = 'workshop_' . $strategy . '_strategy'; 979 if (method_exists($classname, 'scale_used')) { 980 if (call_user_func_array(array($classname, 'scale_used'), array($scaleid, $workshopid))) { 981 // no need to include any other files - scale is used 982 return true; 983 } 984 } 985 } 986 987 return false; 988 } 989 990 /** 991 * Is a given scale used by any instance of workshop? 992 * 993 * The function asks all installed grading strategy subplugins. The workshop 994 * core itself does not use scales. Both grade for submission and grade for 995 * assessments do not use scales. 996 * 997 * @param int $scaleid id of the scale to check 998 * @return bool 999 */ 1000 function workshop_scale_used_anywhere($scaleid) { 1001 global $CFG; // other files included from here 1002 1003 $strategies = core_component::get_plugin_list('workshopform'); 1004 foreach ($strategies as $strategy => $strategypath) { 1005 $strategylib = $strategypath . '/lib.php'; 1006 if (is_readable($strategylib)) { 1007 require_once($strategylib); 1008 } else { 1009 throw new coding_exception('the grading forms subplugin must contain library ' . $strategylib); 1010 } 1011 $classname = 'workshop_' . $strategy . '_strategy'; 1012 if (method_exists($classname, 'scale_used')) { 1013 if (call_user_func(array($classname, 'scale_used'), $scaleid)) { 1014 // no need to include any other files - scale is used 1015 return true; 1016 } 1017 } 1018 } 1019 1020 return false; 1021 } 1022 1023 /** 1024 * Returns all other caps used in the module 1025 * 1026 * @return array 1027 */ 1028 function workshop_get_extra_capabilities() { 1029 return array('moodle/site:accessallgroups'); 1030 } 1031 1032 //////////////////////////////////////////////////////////////////////////////// 1033 // Gradebook API // 1034 //////////////////////////////////////////////////////////////////////////////// 1035 1036 /** 1037 * Creates or updates grade items for the give workshop instance 1038 * 1039 * Needed by grade_update_mod_grades() in lib/gradelib.php. Also used by 1040 * {@link workshop_update_grades()}. 1041 * 1042 * @param stdClass $workshop instance object with extra cmidnumber property 1043 * @param stdClass $submissiongrades data for the first grade item 1044 * @param stdClass $assessmentgrades data for the second grade item 1045 * @return void 1046 */ 1047 function workshop_grade_item_update(stdclass $workshop, $submissiongrades=null, $assessmentgrades=null) { 1048 global $CFG; 1049 require_once($CFG->libdir.'/gradelib.php'); 1050 1051 $a = new stdclass(); 1052 $a->workshopname = clean_param($workshop->name, PARAM_NOTAGS); 1053 1054 $item = array(); 1055 $item['itemname'] = get_string('gradeitemsubmission', 'workshop', $a); 1056 $item['gradetype'] = GRADE_TYPE_VALUE; 1057 $item['grademax'] = $workshop->grade; 1058 $item['grademin'] = 0; 1059 grade_update('mod/workshop', $workshop->course, 'mod', 'workshop', $workshop->id, 0, $submissiongrades , $item); 1060 1061 $item = array(); 1062 $item['itemname'] = get_string('gradeitemassessment', 'workshop', $a); 1063 $item['gradetype'] = GRADE_TYPE_VALUE; 1064 $item['grademax'] = $workshop->gradinggrade; 1065 $item['grademin'] = 0; 1066 grade_update('mod/workshop', $workshop->course, 'mod', 'workshop', $workshop->id, 1, $assessmentgrades, $item); 1067 } 1068 1069 /** 1070 * Update workshop grades in the gradebook 1071 * 1072 * Needed by grade_update_mod_grades() in lib/gradelib.php 1073 * 1074 * @category grade 1075 * @param stdClass $workshop instance object with extra cmidnumber and modname property 1076 * @param int $userid update grade of specific user only, 0 means all participants 1077 * @return void 1078 */ 1079 function workshop_update_grades(stdclass $workshop, $userid=0) { 1080 global $CFG, $DB; 1081 require_once($CFG->libdir.'/gradelib.php'); 1082 1083 $whereuser = $userid ? ' AND authorid = :userid' : ''; 1084 $params = array('workshopid' => $workshop->id, 'userid' => $userid); 1085 $sql = 'SELECT authorid, grade, gradeover, gradeoverby, feedbackauthor, feedbackauthorformat, timemodified, timegraded 1086 FROM {workshop_submissions} 1087 WHERE workshopid = :workshopid AND example=0' . $whereuser; 1088 $records = $DB->get_records_sql($sql, $params); 1089 $submissiongrades = array(); 1090 foreach ($records as $record) { 1091 $grade = new stdclass(); 1092 $grade->userid = $record->authorid; 1093 if (!is_null($record->gradeover)) { 1094 $grade->rawgrade = grade_floatval($workshop->grade * $record->gradeover / 100); 1095 $grade->usermodified = $record->gradeoverby; 1096 } else { 1097 $grade->rawgrade = grade_floatval($workshop->grade * $record->grade / 100); 1098 } 1099 $grade->feedback = $record->feedbackauthor; 1100 $grade->feedbackformat = $record->feedbackauthorformat; 1101 $grade->datesubmitted = $record->timemodified; 1102 $grade->dategraded = $record->timegraded; 1103 $submissiongrades[$record->authorid] = $grade; 1104 } 1105 1106 $whereuser = $userid ? ' AND userid = :userid' : ''; 1107 $params = array('workshopid' => $workshop->id, 'userid' => $userid); 1108 $sql = 'SELECT userid, gradinggrade, timegraded 1109 FROM {workshop_aggregations} 1110 WHERE workshopid = :workshopid' . $whereuser; 1111 $records = $DB->get_records_sql($sql, $params); 1112 $assessmentgrades = array(); 1113 foreach ($records as $record) { 1114 $grade = new stdclass(); 1115 $grade->userid = $record->userid; 1116 $grade->rawgrade = grade_floatval($workshop->gradinggrade * $record->gradinggrade / 100); 1117 $grade->dategraded = $record->timegraded; 1118 $assessmentgrades[$record->userid] = $grade; 1119 } 1120 1121 workshop_grade_item_update($workshop, $submissiongrades, $assessmentgrades); 1122 } 1123 1124 /** 1125 * Update the grade items categories if they are changed via mod_form.php 1126 * 1127 * We must do it manually here in the workshop module because modedit supports only 1128 * single grade item while we use two. 1129 * 1130 * @param stdClass $workshop An object from the form in mod_form.php 1131 */ 1132 function workshop_grade_item_category_update($workshop) { 1133 1134 $gradeitems = grade_item::fetch_all(array( 1135 'itemtype' => 'mod', 1136 'itemmodule' => 'workshop', 1137 'iteminstance' => $workshop->id, 1138 'courseid' => $workshop->course)); 1139 1140 if (!empty($gradeitems)) { 1141 foreach ($gradeitems as $gradeitem) { 1142 if ($gradeitem->itemnumber == 0) { 1143 if ($gradeitem->categoryid != $workshop->gradecategory) { 1144 $gradeitem->set_parent($workshop->gradecategory); 1145 } 1146 } else if ($gradeitem->itemnumber == 1) { 1147 if ($gradeitem->categoryid != $workshop->gradinggradecategory) { 1148 $gradeitem->set_parent($workshop->gradinggradecategory); 1149 } 1150 } 1151 if (!empty($workshop->add)) { 1152 $gradecategory = $gradeitem->get_parent_category(); 1153 if (grade_category::aggregation_uses_aggregationcoef($gradecategory->aggregation)) { 1154 if ($gradecategory->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN) { 1155 $gradeitem->aggregationcoef = 1; 1156 } else { 1157 $gradeitem->aggregationcoef = 0; 1158 } 1159 $gradeitem->update(); 1160 } 1161 } 1162 } 1163 } 1164 } 1165 1166 //////////////////////////////////////////////////////////////////////////////// 1167 // File API // 1168 //////////////////////////////////////////////////////////////////////////////// 1169 1170 /** 1171 * Returns the lists of all browsable file areas within the given module context 1172 * 1173 * The file area workshop_intro for the activity introduction field is added automatically 1174 * by {@link file_browser::get_file_info_context_module()} 1175 * 1176 * @package mod_workshop 1177 * @category files 1178 * 1179 * @param stdClass $course 1180 * @param stdClass $cm 1181 * @param stdClass $context 1182 * @return array of [(string)filearea] => (string)description 1183 */ 1184 function workshop_get_file_areas($course, $cm, $context) { 1185 $areas = array(); 1186 $areas['instructauthors'] = get_string('areainstructauthors', 'workshop'); 1187 $areas['instructreviewers'] = get_string('areainstructreviewers', 'workshop'); 1188 $areas['submission_content'] = get_string('areasubmissioncontent', 'workshop'); 1189 $areas['submission_attachment'] = get_string('areasubmissionattachment', 'workshop'); 1190 $areas['conclusion'] = get_string('areaconclusion', 'workshop'); 1191 $areas['overallfeedback_content'] = get_string('areaoverallfeedbackcontent', 'workshop'); 1192 $areas['overallfeedback_attachment'] = get_string('areaoverallfeedbackattachment', 'workshop'); 1193 1194 return $areas; 1195 } 1196 1197 /** 1198 * Serves the files from the workshop file areas 1199 * 1200 * Apart from module intro (handled by pluginfile.php automatically), workshop files may be 1201 * media inserted into submission content (like images) and submission attachments. For these two, 1202 * the fileareas submission_content and submission_attachment are used. 1203 * Besides that, areas instructauthors, instructreviewers and conclusion contain the media 1204 * embedded using the mod_form.php. 1205 * 1206 * @package mod_workshop 1207 * @category files 1208 * 1209 * @param stdClass $course the course object 1210 * @param stdClass $cm the course module object 1211 * @param stdClass $context the workshop's context 1212 * @param string $filearea the name of the file area 1213 * @param array $args extra arguments (itemid, path) 1214 * @param bool $forcedownload whether or not force download 1215 * @param array $options additional options affecting the file serving 1216 * @return bool false if the file not found, just send the file otherwise and do not return anything 1217 */ 1218 function workshop_pluginfile($course, $cm, $context, $filearea, array $args, $forcedownload, array $options=array()) { 1219 global $DB, $CFG, $USER; 1220 1221 if ($context->contextlevel != CONTEXT_MODULE) { 1222 return false; 1223 } 1224 1225 require_login($course, true, $cm); 1226 1227 if ($filearea === 'instructauthors') { 1228 array_shift($args); // itemid is ignored here 1229 $relativepath = implode('/', $args); 1230 $fullpath = "/$context->id/mod_workshop/$filearea/0/$relativepath"; 1231 1232 $fs = get_file_storage(); 1233 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) { 1234 send_file_not_found(); 1235 } 1236 1237 // finally send the file 1238 send_stored_file($file, null, 0, $forcedownload, $options); 1239 1240 } else if ($filearea === 'instructreviewers') { 1241 array_shift($args); // itemid is ignored here 1242 $relativepath = implode('/', $args); 1243 $fullpath = "/$context->id/mod_workshop/$filearea/0/$relativepath"; 1244 1245 $fs = get_file_storage(); 1246 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) { 1247 send_file_not_found(); 1248 } 1249 1250 // finally send the file 1251 send_stored_file($file, null, 0, $forcedownload, $options); 1252 1253 } else if ($filearea === 'conclusion') { 1254 array_shift($args); // itemid is ignored here 1255 $relativepath = implode('/', $args); 1256 $fullpath = "/$context->id/mod_workshop/$filearea/0/$relativepath"; 1257 1258 $fs = get_file_storage(); 1259 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) { 1260 send_file_not_found(); 1261 } 1262 1263 // finally send the file 1264 send_stored_file($file, null, 0, $forcedownload, $options); 1265 1266 } else if ($filearea === 'submission_content' or $filearea === 'submission_attachment') { 1267 $itemid = (int)array_shift($args); 1268 if (!$workshop = $DB->get_record('workshop', array('id' => $cm->instance))) { 1269 return false; 1270 } 1271 if (!$submission = $DB->get_record('workshop_submissions', array('id' => $itemid, 'workshopid' => $workshop->id))) { 1272 return false; 1273 } 1274 1275 // make sure the user is allowed to see the file 1276 if (empty($submission->example)) { 1277 if ($USER->id != $submission->authorid) { 1278 if ($submission->published == 1 and $workshop->phase == 50 1279 and has_capability('mod/workshop:viewpublishedsubmissions', $context)) { 1280 // Published submission, we can go (workshop does not take the group mode 1281 // into account in this case yet). 1282 } else if (!$DB->record_exists('workshop_assessments', array('submissionid' => $submission->id, 'reviewerid' => $USER->id))) { 1283 if (!has_capability('mod/workshop:viewallsubmissions', $context)) { 1284 send_file_not_found(); 1285 } else { 1286 $gmode = groups_get_activity_groupmode($cm, $course); 1287 if ($gmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) { 1288 // check there is at least one common group with both the $USER 1289 // and the submission author 1290 $sql = "SELECT 'x' 1291 FROM {workshop_submissions} s 1292 JOIN {user} a ON (a.id = s.authorid) 1293 JOIN {groups_members} agm ON (a.id = agm.userid) 1294 JOIN {user} u ON (u.id = ?) 1295 JOIN {groups_members} ugm ON (u.id = ugm.userid) 1296 WHERE s.example = 0 AND s.workshopid = ? AND s.id = ? AND agm.groupid = ugm.groupid"; 1297 $params = array($USER->id, $workshop->id, $submission->id); 1298 if (!$DB->record_exists_sql($sql, $params)) { 1299 send_file_not_found(); 1300 } 1301 } 1302 } 1303 } 1304 } 1305 } 1306 1307 $fs = get_file_storage(); 1308 $relativepath = implode('/', $args); 1309 $fullpath = "/$context->id/mod_workshop/$filearea/$itemid/$relativepath"; 1310 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) { 1311 return false; 1312 } 1313 // finally send the file 1314 // these files are uploaded by students - forcing download for security reasons 1315 send_stored_file($file, 0, 0, true, $options); 1316 1317 } else if ($filearea === 'overallfeedback_content' or $filearea === 'overallfeedback_attachment') { 1318 $itemid = (int)array_shift($args); 1319 if (!$workshop = $DB->get_record('workshop', array('id' => $cm->instance))) { 1320 return false; 1321 } 1322 if (!$assessment = $DB->get_record('workshop_assessments', array('id' => $itemid))) { 1323 return false; 1324 } 1325 if (!$submission = $DB->get_record('workshop_submissions', array('id' => $assessment->submissionid, 'workshopid' => $workshop->id))) { 1326 return false; 1327 } 1328 1329 if ($USER->id == $assessment->reviewerid) { 1330 // Reviewers can always see their own files. 1331 } else if ($USER->id == $submission->authorid and $workshop->phase == 50) { 1332 // Authors can see the feedback once the workshop is closed. 1333 } else if (!empty($submission->example) and $assessment->weight == 1) { 1334 // Reference assessments of example submissions can be displayed. 1335 } else if (!has_capability('mod/workshop:viewallassessments', $context)) { 1336 send_file_not_found(); 1337 } else { 1338 $gmode = groups_get_activity_groupmode($cm, $course); 1339 if ($gmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) { 1340 // Check there is at least one common group with both the $USER 1341 // and the submission author. 1342 $sql = "SELECT 'x' 1343 FROM {workshop_submissions} s 1344 JOIN {user} a ON (a.id = s.authorid) 1345 JOIN {groups_members} agm ON (a.id = agm.userid) 1346 JOIN {user} u ON (u.id = ?) 1347 JOIN {groups_members} ugm ON (u.id = ugm.userid) 1348 WHERE s.example = 0 AND s.workshopid = ? AND s.id = ? AND agm.groupid = ugm.groupid"; 1349 $params = array($USER->id, $workshop->id, $submission->id); 1350 if (!$DB->record_exists_sql($sql, $params)) { 1351 send_file_not_found(); 1352 } 1353 } 1354 } 1355 1356 $fs = get_file_storage(); 1357 $relativepath = implode('/', $args); 1358 $fullpath = "/$context->id/mod_workshop/$filearea/$itemid/$relativepath"; 1359 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) { 1360 return false; 1361 } 1362 // finally send the file 1363 // these files are uploaded by students - forcing download for security reasons 1364 send_stored_file($file, 0, 0, true, $options); 1365 } 1366 1367 return false; 1368 } 1369 1370 /** 1371 * File browsing support for workshop file areas 1372 * 1373 * @package mod_workshop 1374 * @category files 1375 * 1376 * @param file_browser $browser 1377 * @param array $areas 1378 * @param stdClass $course 1379 * @param stdClass $cm 1380 * @param stdClass $context 1381 * @param string $filearea 1382 * @param int $itemid 1383 * @param string $filepath 1384 * @param string $filename 1385 * @return file_info instance or null if not found 1386 */ 1387 function workshop_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) { 1388 global $CFG, $DB, $USER; 1389 1390 /** @var array internal cache for author names */ 1391 static $submissionauthors = array(); 1392 1393 $fs = get_file_storage(); 1394 1395 if ($filearea === 'submission_content' or $filearea === 'submission_attachment') { 1396 1397 if (!has_capability('mod/workshop:viewallsubmissions', $context)) { 1398 return null; 1399 } 1400 1401 if (is_null($itemid)) { 1402 // no itemid (submissionid) passed, display the list of all submissions 1403 require_once($CFG->dirroot . '/mod/workshop/fileinfolib.php'); 1404 return new workshop_file_info_submissions_container($browser, $course, $cm, $context, $areas, $filearea); 1405 } 1406 1407 // make sure the user can see the particular submission in separate groups mode 1408 $gmode = groups_get_activity_groupmode($cm, $course); 1409 1410 if ($gmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) { 1411 // check there is at least one common group with both the $USER 1412 // and the submission author (this is not expected to be a frequent 1413 // usecase so we can live with pretty ineffective one query per submission here...) 1414 $sql = "SELECT 'x' 1415 FROM {workshop_submissions} s 1416 JOIN {user} a ON (a.id = s.authorid) 1417 JOIN {groups_members} agm ON (a.id = agm.userid) 1418 JOIN {user} u ON (u.id = ?) 1419 JOIN {groups_members} ugm ON (u.id = ugm.userid) 1420 WHERE s.example = 0 AND s.workshopid = ? AND s.id = ? AND agm.groupid = ugm.groupid"; 1421 $params = array($USER->id, $cm->instance, $itemid); 1422 if (!$DB->record_exists_sql($sql, $params)) { 1423 return null; 1424 } 1425 } 1426 1427 // we are inside some particular submission container 1428 1429 $filepath = is_null($filepath) ? '/' : $filepath; 1430 $filename = is_null($filename) ? '.' : $filename; 1431 1432 if (!$storedfile = $fs->get_file($context->id, 'mod_workshop', $filearea, $itemid, $filepath, $filename)) { 1433 if ($filepath === '/' and $filename === '.') { 1434 $storedfile = new virtual_root_file($context->id, 'mod_workshop', $filearea, $itemid); 1435 } else { 1436 // not found 1437 return null; 1438 } 1439 } 1440 1441 // Checks to see if the user can manage files or is the owner. 1442 // TODO MDL-33805 - Do not use userid here and move the capability check above. 1443 if (!has_capability('moodle/course:managefiles', $context) && $storedfile->get_userid() != $USER->id) { 1444 return null; 1445 } 1446 1447 // let us display the author's name instead of itemid (submission id) 1448 1449 if (isset($submissionauthors[$itemid])) { 1450 $topvisiblename = $submissionauthors[$itemid]; 1451 1452 } else { 1453 1454 $sql = "SELECT s.id, u.lastname, u.firstname 1455 FROM {workshop_submissions} s 1456 JOIN {user} u ON (s.authorid = u.id) 1457 WHERE s.example = 0 AND s.workshopid = ?"; 1458 $params = array($cm->instance); 1459 $rs = $DB->get_recordset_sql($sql, $params); 1460 1461 foreach ($rs as $submissionauthor) { 1462 $title = s(fullname($submissionauthor)); // this is generally not unique... 1463 $submissionauthors[$submissionauthor->id] = $title; 1464 } 1465 $rs->close(); 1466 1467 if (!isset($submissionauthors[$itemid])) { 1468 // should not happen 1469 return null; 1470 } else { 1471 $topvisiblename = $submissionauthors[$itemid]; 1472 } 1473 } 1474 1475 $urlbase = $CFG->wwwroot . '/pluginfile.php'; 1476 // do not allow manual modification of any files! 1477 return new file_info_stored($browser, $context, $storedfile, $urlbase, $topvisiblename, true, true, false, false); 1478 } 1479 1480 if ($filearea === 'overallfeedback_content' or $filearea === 'overallfeedback_attachment') { 1481 1482 if (!has_capability('mod/workshop:viewallassessments', $context)) { 1483 return null; 1484 } 1485 1486 if (is_null($itemid)) { 1487 // No itemid (assessmentid) passed, display the list of all assessments. 1488 require_once($CFG->dirroot . '/mod/workshop/fileinfolib.php'); 1489 return new workshop_file_info_overallfeedback_container($browser, $course, $cm, $context, $areas, $filearea); 1490 } 1491 1492 // Make sure the user can see the particular assessment in separate groups mode. 1493 $gmode = groups_get_activity_groupmode($cm, $course); 1494 if ($gmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) { 1495 // Check there is at least one common group with both the $USER 1496 // and the submission author. 1497 $sql = "SELECT 'x' 1498 FROM {workshop_submissions} s 1499 JOIN {user} a ON (a.id = s.authorid) 1500 JOIN {groups_members} agm ON (a.id = agm.userid) 1501 JOIN {user} u ON (u.id = ?) 1502 JOIN {groups_members} ugm ON (u.id = ugm.userid) 1503 WHERE s.example = 0 AND s.workshopid = ? AND s.id = ? AND agm.groupid = ugm.groupid"; 1504 $params = array($USER->id, $cm->instance, $itemid); 1505 if (!$DB->record_exists_sql($sql, $params)) { 1506 return null; 1507 } 1508 } 1509 1510 // We are inside a particular assessment container. 1511 $filepath = is_null($filepath) ? '/' : $filepath; 1512 $filename = is_null($filename) ? '.' : $filename; 1513 1514 if (!$storedfile = $fs->get_file($context->id, 'mod_workshop', $filearea, $itemid, $filepath, $filename)) { 1515 if ($filepath === '/' and $filename === '.') { 1516 $storedfile = new virtual_root_file($context->id, 'mod_workshop', $filearea, $itemid); 1517 } else { 1518 // Not found 1519 return null; 1520 } 1521 } 1522 1523 // Check to see if the user can manage files or is the owner. 1524 if (!has_capability('moodle/course:managefiles', $context) and $storedfile->get_userid() != $USER->id) { 1525 return null; 1526 } 1527 1528 $urlbase = $CFG->wwwroot . '/pluginfile.php'; 1529 1530 // Do not allow manual modification of any files. 1531 return new file_info_stored($browser, $context, $storedfile, $urlbase, $itemid, true, true, false, false); 1532 } 1533 1534 if ($filearea == 'instructauthors' or $filearea == 'instructreviewers' or $filearea == 'conclusion') { 1535 // always only itemid 0 1536 1537 $filepath = is_null($filepath) ? '/' : $filepath; 1538 $filename = is_null($filename) ? '.' : $filename; 1539 1540 $urlbase = $CFG->wwwroot.'/pluginfile.php'; 1541 if (!$storedfile = $fs->get_file($context->id, 'mod_workshop', $filearea, 0, $filepath, $filename)) { 1542 if ($filepath === '/' and $filename === '.') { 1543 $storedfile = new virtual_root_file($context->id, 'mod_workshop', $filearea, 0); 1544 } else { 1545 // not found 1546 return null; 1547 } 1548 } 1549 return new file_info_stored($browser, $context, $storedfile, $urlbase, $areas[$filearea], false, true, true, false); 1550 } 1551 } 1552 1553 //////////////////////////////////////////////////////////////////////////////// 1554 // Navigation API // 1555 //////////////////////////////////////////////////////////////////////////////// 1556 1557 /** 1558 * Extends the global navigation tree by adding workshop nodes if there is a relevant content 1559 * 1560 * This can be called by an AJAX request so do not rely on $PAGE as it might not be set up properly. 1561 * 1562 * @param navigation_node $navref An object representing the navigation tree node of the workshop module instance 1563 * @param stdClass $course 1564 * @param stdClass $module 1565 * @param cm_info $cm 1566 */ 1567 function workshop_extend_navigation(navigation_node $navref, stdclass $course, stdclass $module, cm_info $cm) { 1568 global $CFG; 1569 1570 if (has_capability('mod/workshop:submit', context_module::instance($cm->id))) { 1571 $url = new moodle_url('/mod/workshop/submission.php', array('cmid' => $cm->id)); 1572 $mysubmission = $navref->add(get_string('mysubmission', 'workshop'), $url); 1573 $mysubmission->mainnavonly = true; 1574 } 1575 } 1576 1577 /** 1578 * Extends the settings navigation with the Workshop settings 1579 1580 * This function is called when the context for the page is a workshop module. This is not called by AJAX 1581 * so it is safe to rely on the $PAGE. 1582 * 1583 * @param settings_navigation $settingsnav {@link settings_navigation} 1584 * @param navigation_node $workshopnode {@link navigation_node} 1585 */ 1586 function workshop_extend_settings_navigation(settings_navigation $settingsnav, navigation_node $workshopnode=null) { 1587 global $PAGE; 1588 1589 //$workshopobject = $DB->get_record("workshop", array("id" => $PAGE->cm->instance)); 1590 1591 if (has_capability('mod/workshop:editdimensions', $PAGE->cm->context)) { 1592 $url = new moodle_url('/mod/workshop/editform.php', array('cmid' => $PAGE->cm->id)); 1593 $workshopnode->add(get_string('editassessmentform', 'workshop'), $url, settings_navigation::TYPE_SETTING); 1594 } 1595 if (has_capability('mod/workshop:allocate', $PAGE->cm->context)) { 1596 $url = new moodle_url('/mod/workshop/allocation.php', array('cmid' => $PAGE->cm->id)); 1597 $workshopnode->add(get_string('allocate', 'workshop'), $url, settings_navigation::TYPE_SETTING); 1598 } 1599 } 1600 1601 /** 1602 * Return a list of page types 1603 * @param string $pagetype current page type 1604 * @param stdClass $parentcontext Block's parent context 1605 * @param stdClass $currentcontext Current context of block 1606 */ 1607 function workshop_page_type_list($pagetype, $parentcontext, $currentcontext) { 1608 $module_pagetype = array('mod-workshop-*'=>get_string('page-mod-workshop-x', 'workshop')); 1609 return $module_pagetype; 1610 } 1611 1612 //////////////////////////////////////////////////////////////////////////////// 1613 // Calendar API // 1614 //////////////////////////////////////////////////////////////////////////////// 1615 1616 /** 1617 * Updates the calendar events associated to the given workshop 1618 * 1619 * @param stdClass $workshop the workshop instance record 1620 * @param int $cmid course module id 1621 */ 1622 function workshop_calendar_update(stdClass $workshop, $cmid) { 1623 global $DB; 1624 1625 // get the currently registered events so that we can re-use their ids 1626 $currentevents = $DB->get_records('event', array('modulename' => 'workshop', 'instance' => $workshop->id)); 1627 1628 // the common properties for all events 1629 $base = new stdClass(); 1630 $base->description = format_module_intro('workshop', $workshop, $cmid, false); 1631 $base->courseid = $workshop->course; 1632 $base->groupid = 0; 1633 $base->userid = 0; 1634 $base->modulename = 'workshop'; 1635 $base->eventtype = 'pluginname'; 1636 $base->instance = $workshop->id; 1637 $base->visible = instance_is_visible('workshop', $workshop); 1638 $base->timeduration = 0; 1639 1640 if ($workshop->submissionstart) { 1641 $event = clone($base); 1642 $event->name = get_string('submissionstartevent', 'mod_workshop', $workshop->name); 1643 $event->timestart = $workshop->submissionstart; 1644 if ($reusedevent = array_shift($currentevents)) { 1645 $event->id = $reusedevent->id; 1646 } else { 1647 // should not be set but just in case 1648 unset($event->id); 1649 } 1650 // update() will reuse a db record if the id field is set 1651 $eventobj = new calendar_event($event); 1652 $eventobj->update($event, false); 1653 } 1654 1655 if ($workshop->submissionend) { 1656 $event = clone($base); 1657 $event->name = get_string('submissionendevent', 'mod_workshop', $workshop->name); 1658 $event->timestart = $workshop->submissionend; 1659 if ($reusedevent = array_shift($currentevents)) { 1660 $event->id = $reusedevent->id; 1661 } else { 1662 // should not be set but just in case 1663 unset($event->id); 1664 } 1665 // update() will reuse a db record if the id field is set 1666 $eventobj = new calendar_event($event); 1667 $eventobj->update($event, false); 1668 } 1669 1670 if ($workshop->assessmentstart) { 1671 $event = clone($base); 1672 $event->name = get_string('assessmentstartevent', 'mod_workshop', $workshop->name); 1673 $event->timestart = $workshop->assessmentstart; 1674 if ($reusedevent = array_shift($currentevents)) { 1675 $event->id = $reusedevent->id; 1676 } else { 1677 // should not be set but just in case 1678 unset($event->id); 1679 } 1680 // update() will reuse a db record if the id field is set 1681 $eventobj = new calendar_event($event); 1682 $eventobj->update($event, false); 1683 } 1684 1685 if ($workshop->assessmentend) { 1686 $event = clone($base); 1687 $event->name = get_string('assessmentendevent', 'mod_workshop', $workshop->name); 1688 $event->timestart = $workshop->assessmentend; 1689 if ($reusedevent = array_shift($currentevents)) { 1690 $event->id = $reusedevent->id; 1691 } else { 1692 // should not be set but just in case 1693 unset($event->id); 1694 } 1695 // update() will reuse a db record if the id field is set 1696 $eventobj = new calendar_event($event); 1697 $eventobj->update($event, false); 1698 } 1699 1700 // delete any leftover events 1701 foreach ($currentevents as $oldevent) { 1702 $oldevent = calendar_event::load($oldevent); 1703 $oldevent->delete(); 1704 } 1705 } 1706 1707 //////////////////////////////////////////////////////////////////////////////// 1708 // Course reset API // 1709 //////////////////////////////////////////////////////////////////////////////// 1710 1711 /** 1712 * Extends the course reset form with workshop specific settings. 1713 * 1714 * @param MoodleQuickForm $mform 1715 */ 1716 function workshop_reset_course_form_definition($mform) { 1717 1718 $mform->addElement('header', 'workshopheader', get_string('modulenameplural', 'mod_workshop')); 1719 1720 $mform->addElement('advcheckbox', 'reset_workshop_submissions', get_string('resetsubmissions', 'mod_workshop')); 1721 $mform->addHelpButton('reset_workshop_submissions', 'resetsubmissions', 'mod_workshop'); 1722 1723 $mform->addElement('advcheckbox', 'reset_workshop_assessments', get_string('resetassessments', 'mod_workshop')); 1724 $mform->addHelpButton('reset_workshop_assessments', 'resetassessments', 'mod_workshop'); 1725 $mform->disabledIf('reset_workshop_assessments', 'reset_workshop_submissions', 'checked'); 1726 1727 $mform->addElement('advcheckbox', 'reset_workshop_phase', get_string('resetphase', 'mod_workshop')); 1728 $mform->addHelpButton('reset_workshop_phase', 'resetphase', 'mod_workshop'); 1729 } 1730 1731 /** 1732 * Provides default values for the workshop settings in the course reset form. 1733 * 1734 * @param stdClass $course The course to be reset. 1735 */ 1736 function workshop_reset_course_form_defaults(stdClass $course) { 1737 1738 $defaults = array( 1739 'reset_workshop_submissions' => 1, 1740 'reset_workshop_assessments' => 1, 1741 'reset_workshop_phase' => 1, 1742 ); 1743 1744 return $defaults; 1745 } 1746 1747 /** 1748 * Performs the reset of all workshop instances in the course. 1749 * 1750 * @param stdClass $data The actual course reset settings. 1751 * @return array List of results, each being array[(string)component, (string)item, (string)error] 1752 */ 1753 function workshop_reset_userdata(stdClass $data) { 1754 global $CFG, $DB; 1755 1756 if (empty($data->reset_workshop_submissions) 1757 and empty($data->reset_workshop_assessments) 1758 and empty($data->reset_workshop_phase) ) { 1759 // Nothing to do here. 1760 return array(); 1761 } 1762 1763 $workshoprecords = $DB->get_records('workshop', array('course' => $data->courseid)); 1764 1765 if (empty($workshoprecords)) { 1766 // What a boring course - no workshops here! 1767 return array(); 1768 } 1769 1770 require_once($CFG->dirroot . '/mod/workshop/locallib.php'); 1771 1772 $course = $DB->get_record('course', array('id' => $data->courseid), '*', MUST_EXIST); 1773 $status = array(); 1774 1775 foreach ($workshoprecords as $workshoprecord) { 1776 $cm = get_coursemodule_from_instance('workshop', $workshoprecord->id, $course->id, false, MUST_EXIST); 1777 $workshop = new workshop($workshoprecord, $cm, $course); 1778 $status = array_merge($status, $workshop->reset_userdata($data)); 1779 } 1780 1781 return $status; 1782 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 20:29:05 2014 | Cross-referenced by PHPXref 0.7.1 |