Mostly working on the view.php UI plus some changes
[moodle.git] / mod / workshop / locallib.php
CommitLineData
de811c0c 1<?php
53fad4b9
DM
2
3// This file is part of Moodle - http://moodle.org/
4//
de811c0c
DM
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
53fad4b9 14//
de811c0c
DM
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
53fad4b9 17
de811c0c 18/**
6e309973 19 * Library of internal classes and functions for module workshop
de811c0c 20 *
53fad4b9 21 * All the workshop specific functions, needed to implement the module
6e309973 22 * logic, should go to here. Instead of having bunch of function named
53fad4b9 23 * workshop_something() taking the workshop instance as the first
a39d7d87 24 * parameter, we use a class workshop that provides all methods.
53fad4b9 25 *
de811c0c
DM
26 * @package mod-workshop
27 * @copyright 2009 David Mudrak <david.mudrak@gmail.com>
28 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
29 */
30
31defined('MOODLE_INTERNAL') || die();
32
b13142da 33require_once(dirname(__FILE__).'/lib.php'); // we extend this library here
0968b1a3 34
6e309973
DM
35/**
36 * Full-featured workshop API
37 *
a39d7d87 38 * This wraps the workshop database record with a set of methods that are called
6e309973 39 * from the module itself. The class should be initialized right after you get
a39d7d87 40 * $workshop, $cm and $course records at the begining of the script.
6e309973 41 */
a39d7d87
DM
42class workshop {
43
b761e6d9
DM
44 /** return statuses of {@link add_allocation} to be passed to a workshop renderer method */
45 const ALLOCATION_EXISTS = -1;
46 const ALLOCATION_ERROR = -2;
47
48 /** the internal code of the workshop phases as are stored in the database */
49 const PHASE_SETUP = 10;
50 const PHASE_SUBMISSION = 20;
51 const PHASE_ASSESSMENT = 30;
52 const PHASE_EVALUATION = 40;
53 const PHASE_CLOSED = 50;
54
65ba104c 55 /** @var stdClass course module record */
a39d7d87
DM
56 public $cm = null;
57
65ba104c 58 /** @var stdClass course record */
a39d7d87 59 public $course = null;
6e309973 60
b761e6d9
DM
61 /** @var stdClass the workshop instance context */
62 public $context = null;
63
64 /**
65 * @var workshop_strategy grading strategy instance
66 * Do not use directly, get the instance using {@link workshop::grading_strategy_instance()}
67 */
b13142da
DM
68 protected $strategyinstance = null;
69
70 /** @var stdClass underlying database record */
71 protected $dbrecord = null;
6e309973
DM
72
73 /**
65ba104c 74 * Initializes the workshop API instance using the data from DB
a39d7d87
DM
75 *
76 * Makes deep copy of all passed records properties. Replaces integer $course attribute
77 * with a full database record (course should not be stored in instances table anyway).
6e309973 78 *
b13142da 79 * @param stdClass $dbrecord Workshop instance data from {workshop} table
06d73dd5
DM
80 * @param stdClass $cm Course module record as returned by {@link get_coursemodule_from_id()}
81 * @param stdClass $course Course record from {course} table
0dc47fb9 82 */
b13142da
DM
83 public function __construct(stdClass $dbrecord, stdClass $cm, stdClass $course) {
84 $this->dbrecord = $dbrecord;
85 $this->cm = $cm;
86 $this->course = $course;
b761e6d9 87 $this->context = get_context_instance(CONTEXT_MODULE, $this->cm->id);
b13142da
DM
88 }
89
90 /**
91 * Magic method to retrieve the value of the underlying database record's field
92 *
93 * @throws coding_exception if the field does not exist
94 * @param mixed $key the name of the database field
95 * @return mixed|null the value of the field
96 */
97 public function __get($key) {
98 if (!isset($this->dbrecord->{$key})) {
99 throw new coding_exception('You are trying to get a non-existing property');
a39d7d87 100 }
b13142da 101 return $this->dbrecord->{$key};
6e309973
DM
102 }
103
6e309973
DM
104 /**
105 * Fetches all users with the capability mod/workshop:submit in the current context
106 *
3d2924e9 107 * The returned objects contain id, lastname and firstname properties and are ordered by lastname,firstname
53fad4b9
DM
108 *
109 * @param bool $musthavesubmission If true, return only users who have already submitted. All possible authors otherwise.
65ba104c 110 * @return array array[userid] => stdClass{->id ->lastname ->firstname}
6e309973 111 */
53fad4b9 112 public function get_peer_authors($musthavesubmission=true) {
66c9894d 113 global $DB;
6e309973 114
b761e6d9 115 $users = get_users_by_capability($this->context, 'mod/workshop:submit',
235b31c8 116 'u.id, u.lastname, u.firstname', 'u.lastname,u.firstname', '', '', '', '', false, false, true);
66c9894d 117
3d2924e9 118 if ($musthavesubmission) {
a39d7d87 119 $userswithsubmission = array();
235b31c8 120 $submissions = $DB->get_records_list('workshop_submissions', 'userid', array_keys($users),'', 'id,userid');
b8ead2e6
DM
121 foreach ($submissions as $submission) {
122 $userswithsubmission[$submission->userid] = null;
123 }
66c9894d
DM
124 $userswithsubmission = array_intersect_key($users, $userswithsubmission);
125 }
126
127 if ($musthavesubmission) {
128 return $userswithsubmission;
129 } else {
130 return $users;
131 }
6e309973
DM
132 }
133
6e309973
DM
134 /**
135 * Fetches all users with the capability mod/workshop:peerassess in the current context
136 *
b13142da 137 * The returned objects contain id, lastname and firstname properties and are ordered by lastname,firstname
53fad4b9
DM
138 *
139 * @param bool $musthavesubmission If true, return only users who have already submitted. All possible users otherwise.
65ba104c 140 * @return array array[userid] => stdClass{->id ->lastname ->firstname}
6e309973 141 */
53fad4b9 142 public function get_peer_reviewers($musthavesubmission=false) {
6e309973 143 global $DB;
3d2924e9 144
b761e6d9 145 $users = get_users_by_capability($this->context, 'mod/workshop:peerassess',
235b31c8 146 'u.id, u.lastname, u.firstname', 'u.lastname,u.firstname', '', '', '', '', false, false, true);
3d2924e9
DM
147
148 if ($musthavesubmission) {
149 // users without their own submission can not be reviewers
235b31c8 150 $submissions = $DB->get_records_list('workshop_submissions', 'userid', array_keys($users),'', 'id,userid');
3d2924e9
DM
151 foreach ($submissions as $submission) {
152 $userswithsubmission[$submission->userid] = null;
6e309973 153 }
3d2924e9 154 $userswithsubmission = array_intersect_key($users, $userswithsubmission);
0968b1a3 155 }
3d2924e9 156
53fad4b9
DM
157 if ($musthavesubmission) {
158 return $userswithsubmission;
159 } else {
160 return $users;
161 }
0968b1a3
DM
162 }
163
b8ead2e6
DM
164 /**
165 * Groups the given users by the group membership
166 *
167 * This takes the module grouping settings into account. If "Available for group members only"
168 * is set, returns only groups withing the course module grouping. Always returns group [0] with
169 * all the given users.
170 *
65ba104c
DM
171 * @param array $users array[userid] => stdClass{->id ->lastname ->firstname}
172 * @return array array[groupid][userid] => stdClass{->id ->lastname ->firstname}
53fad4b9 173 */
3d2924e9 174 public function get_grouped($users) {
53fad4b9 175 global $DB;
3d2924e9 176 global $CFG;
53fad4b9 177
b8ead2e6
DM
178 $grouped = array(); // grouped users to be returned
179 if (empty($users)) {
180 return $grouped;
a7c5b918 181 }
3d2924e9 182 if (!empty($CFG->enablegroupings) and $this->cm->groupmembersonly) {
53fad4b9
DM
183 // Available for group members only - the workshop is available only
184 // to users assigned to groups within the selected grouping, or to
185 // any group if no grouping is selected.
186 $groupingid = $this->cm->groupingid;
b8ead2e6 187 // All users that are members of at least one group will be
53fad4b9 188 // added into a virtual group id 0
b8ead2e6 189 $grouped[0] = array();
53fad4b9
DM
190 } else {
191 $groupingid = 0;
b8ead2e6
DM
192 // there is no need to be member of a group so $grouped[0] will contain
193 // all users
194 $grouped[0] = $users;
53fad4b9 195 }
b8ead2e6 196 $gmemberships = groups_get_all_groups($this->cm->course, array_keys($users), $groupingid,
53fad4b9
DM
197 'gm.id,gm.groupid,gm.userid');
198 foreach ($gmemberships as $gmembership) {
b8ead2e6
DM
199 if (!isset($grouped[$gmembership->groupid])) {
200 $grouped[$gmembership->groupid] = array();
53fad4b9 201 }
b8ead2e6
DM
202 $grouped[$gmembership->groupid][$gmembership->userid] = $users[$gmembership->userid];
203 $grouped[0][$gmembership->userid] = $users[$gmembership->userid];
53fad4b9 204 }
b8ead2e6 205 return $grouped;
53fad4b9 206 }
6e309973
DM
207
208 /**
209 * Returns submissions from this workshop
210 *
211 * Fetches data from {workshop_submissions} and adds some useful information from other
212 * tables.
53fad4b9
DM
213 *
214 * @param mixed $userid int|array|'all' If set to [array of] integer, return submission[s] of the given user[s] only
215 * @param mixed $examples false|true|'all' Only regular submissions, only examples, all submissions
65ba104c 216 * @return stdClass moodle_recordset
6e309973 217 */
66c9894d 218 public function get_submissions_recordset($userid='all', $examples=false) {
6e309973
DM
219 global $DB;
220
235b31c8 221 $sql = 'SELECT s.*, u.lastname AS authorlastname, u.firstname AS authorfirstname
3d2924e9
DM
222 FROM {workshop_submissions} s
223 INNER JOIN {user} u ON (s.userid = u.id)
235b31c8 224 WHERE s.workshopid = :workshopid';
3d2924e9 225 $params = array('workshopid' => $this->id);
6e309973 226
b13142da
DM
227 if ('all' === $examples) {
228 // no additional conditions
229 } elseif ($examples === true) {
235b31c8 230 $sql .= ' AND example = 1';
b13142da 231 } elseif ($examples === false) {
235b31c8 232 $sql .= ' AND example = 0';
b13142da
DM
233 } else {
234 throw new coding_exception('Illegal parameter value: $examples may be false|true|"all"');
6e309973 235 }
3d2924e9
DM
236
237 if ('all' === $userid) {
238 // no additional conditions
239 } elseif (is_array($userid)) {
240 list($usql, $uparams) = $DB->get_in_or_equal($userid, SQL_PARAMS_NAMED);
241 $sql .= " AND userid $usql";
6e309973 242 $params = array_merge($params, $uparams);
3d2924e9 243 } else {
235b31c8 244 $sql .= ' AND userid = :userid';
3d2924e9 245 $params['userid'] = $userid;
6e309973
DM
246 }
247
248 return $DB->get_recordset_sql($sql, $params);
249 }
250
53fad4b9
DM
251 /**
252 * Returns a submission submitted by the given author or authors.
253 *
254 * This is intended for regular workshop participants, not for example submissions by teachers.
255 * If an array of authors is provided, returns array of stripped submission records so they do not
256 * include text fields (to prevent possible memory-lack issues).
257 *
258 * @param mixed $id integer|array author ID or IDs
65ba104c 259 * @return mixed false if not found, stdClass if $id is int, array if $id is array
53fad4b9
DM
260 */
261 public function get_submission_by_author($id) {
262 if (empty($id)) {
263 return false;
264 }
265 $rs = $this->get_submissions_recordset($id, false);
3d2924e9 266 if (is_array($id)) {
53fad4b9
DM
267 $submissions = array();
268 foreach ($rs as $submission) {
65ba104c 269 $submissions[$submission->id] = new stdClass();
53fad4b9
DM
270 foreach ($submission as $property => $value) {
271 // we do not want text fields here to prevent possible memory issues
272 if (in_array($property, array('id', 'workshopid', 'example', 'userid', 'authorlastname', 'authorfirstname',
273 'timecreated', 'timemodified', 'grade', 'gradeover', 'gradeoverby', 'gradinggrade'))) {
274 $submissions[$submission->id]->{$property} = $value;
275 }
276 }
277 }
278 return $submissions;
279 } else {
3d2924e9
DM
280 $submission = $rs->current();
281 $rs->close();
282 if (empty($submission->id)) {
283 return false;
284 } else {
285 return $submission;
286 }
53fad4b9
DM
287 }
288 }
6e309973
DM
289
290 /**
291 * Returns the list of assessments with some data added
292 *
293 * Fetches data from {workshop_assessments} and adds some useful information from other
294 * tables.
295 *
296 * @param mixed $reviewerid 'all'|int|array User ID of the reviewer
297 * @param mixed $id 'all'|int Assessment ID
65ba104c 298 * @return stdClass moodle_recordset
6e309973 299 */
66c9894d 300 public function get_assessments_recordset($reviewerid='all', $id='all') {
6e309973 301 global $DB;
53fad4b9 302
235b31c8 303 $sql = 'SELECT a.*,
3d2924e9
DM
304 reviewer.id AS reviewerid,reviewer.firstname AS reviewerfirstname,reviewer.lastname as reviewerlastname,
305 s.title,
306 author.id AS authorid, author.firstname AS authorfirstname,author.lastname as authorlastname
307 FROM {workshop_assessments} a
308 INNER JOIN {user} reviewer ON (a.userid = reviewer.id)
309 INNER JOIN {workshop_submissions} s ON (a.submissionid = s.id)
310 INNER JOIN {user} author ON (s.userid = author.id)
235b31c8 311 WHERE s.workshopid = :workshopid';
3d2924e9
DM
312 $params = array('workshopid' => $this->id);
313
314 if ('all' === $reviewerid) {
315 // no additional conditions
316 } elseif (is_array($reviewerid)) {
317 list($usql, $uparams) = $DB->get_in_or_equal($reviewerid, SQL_PARAMS_NAMED);
318 $sql .= " AND reviewer.id $usql";
6e309973 319 $params = array_merge($params, $uparams);
3d2924e9 320 } else {
235b31c8 321 $sql .= ' AND reviewer.id = :reviewerid';
3d2924e9 322 $params['reviewerid'] = $reviewerid;
6e309973 323 }
3d2924e9
DM
324
325 if ('all' === $id) {
326 // no additional conditions
327 } else {
235b31c8 328 $sql .= ' AND a.id = :assessmentid';
3d2924e9 329 $params['assessmentid'] = $id;
6e309973
DM
330 }
331
332 return $DB->get_recordset_sql($sql, $params);
333 }
334
53fad4b9
DM
335 /**
336 * Returns the list of assessments with some data added
337 *
338 * Fetches data from {workshop_assessments} and adds some useful information from other
339 * tables. The returned objects are lightweight version of those returned by get_assessments_recordset(),
340 * mainly they do not contain text fields.
341 *
342 * @param mixed $reviewerid 'all'|int|array User ID of the reviewer
b8ead2e6 343 * @param mixed $id 'all'|int Assessment ID
65ba104c 344 * @return array [assessmentid] => assessment stdClass
a39d7d87 345 * @see workshop::get_assessments_recordset() for the structure of returned objects
53fad4b9 346 */
b8ead2e6
DM
347 public function get_assessments($reviewerid='all', $id='all') {
348 $rs = $this->get_assessments_recordset($reviewerid, $id);
53fad4b9
DM
349 $assessments = array();
350 foreach ($rs as $assessment) {
351 // copy selected properties into the array to be returned. This is here mainly in order not
352 // to include text comments.
65ba104c 353 $assessments[$assessment->id] = new stdClass();
53fad4b9
DM
354 foreach ($assessment as $property => $value) {
355 if (in_array($property, array('id', 'submissionid', 'userid', 'timecreated', 'timemodified',
356 'timeagreed', 'grade', 'gradinggrade', 'gradinggradeover', 'gradinggradeoverby',
357 'reviewerid', 'reviewerfirstname', 'reviewerlastname', 'title', 'authorid',
358 'authorfirstname', 'authorlastname'))) {
359 $assessments[$assessment->id]->{$property} = $value;
360 }
361 }
362 }
363 $rs->close();
364 return $assessments;
365 }
366
367 /**
368 * Get the information about the given assessment
369 *
370 * @param int $id Assessment ID
a39d7d87 371 * @see workshop::get_assessments_recordset() for the structure of data returned
65ba104c 372 * @return mixed false if not found, stdClass otherwise
53fad4b9
DM
373 */
374 public function get_assessment_by_id($id) {
375 $rs = $this->get_assessments_recordset('all', $id);
376 $assessment = $rs->current();
377 $rs->close();
378 if (empty($assessment->id)) {
379 return false;
380 } else {
381 return $assessment;
382 }
383 }
384
385 /**
386 * Get the information about all assessments assigned to the given reviewer
387 *
388 * @param int $id Reviewer ID
a39d7d87 389 * @see workshop::get_assessments_recordset() for the structure of data returned
53fad4b9
DM
390 * @return array array of objects
391 */
392 public function get_assessments_by_reviewer($id) {
393 $rs = $this->get_assessments_recordset($id);
394 $assessments = array();
395 foreach ($rs as $assessment) {
396 $assessments[$assessment->id] = $assessment;
397 }
398 $rs->close();
399 return $assessment;
400 }
6e309973
DM
401
402 /**
403 * Returns the list of allocations in the workshop
404 *
405 * This returns the list of all users who can submit their work or review submissions (or both
406 * which is the common case). So basically this is to return list of all students participating
407 * in the workshop. For every participant, it adds information about their submission and their
53fad4b9 408 * reviews.
6e309973
DM
409 *
410 * The returned structure is recordset of objects with following properties:
411 * [authorid] [authorfirstname] [authorlastname] [authorpicture] [authorimagealt]
412 * [submissionid] [submissiontitle] [submissiongrade] [assessmentid]
53fad4b9 413 * [timeallocated] [reviewerid] [reviewerfirstname] [reviewerlastname]
6e309973
DM
414 * [reviewerpicture] [reviewerimagealt]
415 *
3d2924e9 416 * TODO This should be refactored when capability handling proposed by Petr is implemented so that
6e309973 417 * we can check capabilities directly in SQL joins.
53fad4b9
DM
418 * Note that the returned recordset includes participants without submission as well as those
419 * without any review allocated yet.
6e309973 420 *
65ba104c 421 * @return stdClass moodle_recordset
6e309973 422 */
66c9894d 423 public function get_allocations_recordset() {
6e309973 424 global $DB;
6e309973 425
b761e6d9 426 $users = get_users_by_capability($this->context, array('mod/workshop:submit', 'mod/workshop:peerassess'),
235b31c8 427 'u.id', 'u.lastname,u.firstname', '', '', '', '', false, false, true);
3d2924e9
DM
428
429 list($usql, $params) = $DB->get_in_or_equal(array_keys($users), SQL_PARAMS_NAMED);
430 $params['workshopid'] = $this->id;
431
1dbbccb7 432 $sql = "SELECT author.id AS authorid, author.firstname AS authorfirstname, author.lastname AS authorlastname,
3d2924e9
DM
433 author.picture AS authorpicture, author.imagealt AS authorimagealt,
434 s.id AS submissionid, s.title AS submissiontitle, s.grade AS submissiongrade,
435 a.id AS assessmentid, a.timecreated AS timeallocated, a.userid AS reviewerid,
436 reviewer.firstname AS reviewerfirstname, reviewer.lastname AS reviewerlastname,
437 reviewer.picture as reviewerpicture, reviewer.imagealt AS reviewerimagealt
438 FROM {user} author
439 LEFT JOIN {workshop_submissions} s ON (s.userid = author.id)
440 LEFT JOIN {workshop_assessments} a ON (s.id = a.submissionid)
441 LEFT JOIN {user} reviewer ON (a.userid = reviewer.id)
442 WHERE author.id $usql AND s.workshopid = :workshopid
1dbbccb7 443 ORDER BY author.lastname,author.firstname,reviewer.lastname,reviewer.firstname";
3d2924e9 444
6e309973
DM
445 return $DB->get_recordset_sql($sql, $params);
446 }
447
6e309973
DM
448 /**
449 * Allocate a submission to a user for review
53fad4b9 450 *
65ba104c 451 * @param stdClass $submission Submission record
6e309973 452 * @param int $reviewerid User ID
53fad4b9 453 * @param bool $bulk repeated inserts into DB expected
6e309973
DM
454 * @return int ID of the new assessment or an error code
455 */
65ba104c 456 public function add_allocation(stdClass $submission, $reviewerid, $bulk=false) {
6e309973
DM
457 global $DB;
458
459 if ($DB->record_exists('workshop_assessments', array('submissionid' => $submission->id, 'userid' => $reviewerid))) {
b761e6d9 460 return self::ALLOCATION_EXISTS;
6e309973
DM
461 }
462
6e309973 463 $now = time();
65ba104c 464 $assessment = new stdClass();
53fad4b9 465 $assessment->submissionid = $submission->id;
6e309973
DM
466 $assessment->userid = $reviewerid;
467 $assessment->timecreated = $now;
468 $assessment->timemodified = $now;
469
235b31c8 470 return $DB->insert_record('workshop_assessments', $assessment, true, $bulk);
6e309973
DM
471 }
472
6e309973 473 /**
53fad4b9 474 * Delete assessment record or records
6e309973 475 *
53fad4b9
DM
476 * @param mixed $id int|array assessment id or array of assessments ids
477 * @return bool false if $id not a valid parameter, true otherwise
6e309973
DM
478 */
479 public function delete_assessment($id) {
480 global $DB;
481
482 // todo remove all given grades from workshop_grades;
6e309973 483
53fad4b9 484 if (is_array($id)) {
235b31c8 485 return $DB->delete_records_list('workshop_assessments', 'id', $id);
3d2924e9 486 } else {
235b31c8 487 return $DB->delete_records('workshop_assessments', array('id' => $id));
53fad4b9 488 }
53fad4b9 489 }
6e309973
DM
490
491 /**
492 * Returns instance of grading strategy class
53fad4b9 493 *
65ba104c 494 * @return stdClass Instance of a grading strategy
6e309973
DM
495 */
496 public function grading_strategy_instance() {
3d2924e9
DM
497 global $CFG; // because we require other libs here
498
3fd2b0e1 499 if (is_null($this->strategyinstance)) {
0dc47fb9 500 $strategylib = dirname(__FILE__) . '/grading/' . $this->strategy . '/strategy.php';
6e309973
DM
501 if (is_readable($strategylib)) {
502 require_once($strategylib);
503 } else {
b761e6d9 504 throw new coding_exception('the grading subplugin must contain library ' . $strategylib);
6e309973 505 }
0dc47fb9 506 $classname = 'workshop_' . $this->strategy . '_strategy';
3fd2b0e1
DM
507 $this->strategyinstance = new $classname($this);
508 if (!in_array('workshop_strategy', class_implements($this->strategyinstance))) {
b761e6d9 509 throw new coding_exception($classname . ' does not implement workshop_strategy interface');
6e309973
DM
510 }
511 }
3fd2b0e1 512 return $this->strategyinstance;
6e309973
DM
513 }
514
66c9894d
DM
515 /**
516 * Return list of available allocation methods
517 *
518 * @return array Array ['string' => 'string'] of localized allocation method names
519 */
520 public function installed_allocators() {
ed597c77 521 $installed = get_plugin_list('workshopallocation');
66c9894d 522 $forms = array();
ed597c77
DM
523 foreach ($installed as $allocation => $allocationpath) {
524 if (file_exists($allocationpath . '/allocator.php')) {
525 $forms[$allocation] = get_string('pluginname', 'workshopallocation_' . $allocation);
526 }
66c9894d
DM
527 }
528 // usability - make sure that manual allocation appears the first
529 if (isset($forms['manual'])) {
530 $m = array('manual' => $forms['manual']);
531 unset($forms['manual']);
532 $forms = array_merge($m, $forms);
533 }
534 return $forms;
535 }
0968b1a3 536
66c9894d
DM
537 /**
538 * Returns instance of submissions allocator
53fad4b9 539 *
65ba104c
DM
540 * @param stdClass $method The name of the allocation method, must be PARAM_ALPHA
541 * @return stdClass Instance of submissions allocator
66c9894d
DM
542 */
543 public function allocator_instance($method) {
3d2924e9
DM
544 global $CFG; // because we require other libs here
545
66c9894d
DM
546 $allocationlib = dirname(__FILE__) . '/allocation/' . $method . '/allocator.php';
547 if (is_readable($allocationlib)) {
548 require_once($allocationlib);
549 } else {
a39d7d87 550 throw new coding_exception('Unable to find allocator.php');
66c9894d
DM
551 }
552 $classname = 'workshop_' . $method . '_allocator';
553 return new $classname($this);
554 }
555
b8ead2e6 556 /**
65ba104c 557 * @return stdClass {@link moodle_url} the URL of this workshop's view page
b8ead2e6
DM
558 */
559 public function view_url() {
560 global $CFG;
561 return new moodle_url($CFG->wwwroot . '/mod/workshop/view.php', array('id' => $this->cm->id));
562 }
563
564 /**
65ba104c 565 * @return stdClass {@link moodle_url} the URL of the page for editing this workshop's grading form
b8ead2e6
DM
566 */
567 public function editform_url() {
568 global $CFG;
569 return new moodle_url($CFG->wwwroot . '/mod/workshop/editform.php', array('cmid' => $this->cm->id));
570 }
571
572 /**
65ba104c 573 * @return stdClass {@link moodle_url} the URL of the page for previewing this workshop's grading form
b8ead2e6
DM
574 */
575 public function previewform_url() {
576 global $CFG;
577 return new moodle_url($CFG->wwwroot . '/mod/workshop/assessment.php', array('preview' => $this->cm->id));
578 }
579
580 /**
581 * @param int $assessmentid The ID of assessment record
65ba104c 582 * @return stdClass {@link moodle_url} the URL of the assessment page
b8ead2e6 583 */
a39d7d87 584 public function assess_url($assessmentid) {
b8ead2e6 585 global $CFG;
a39d7d87 586 return new moodle_url($CFG->wwwroot . '/mod/workshop/assessment.php', array('asid' => $assessmentid));
b8ead2e6
DM
587 }
588
39861053
DM
589 /**
590 * @return stdClass {@link moodle_url} the URL of the page to view own submission
591 */
592 public function submission_url() {
593 global $CFG;
594 return new moodle_url($CFG->wwwroot . '/mod/workshop/submission.php', array('cmid' => $this->cm->id));
595 }
596
b8ead2e6
DM
597 /**
598 * Returns an object containing all data to display the user's full name and picture
599 *
600 * @param int $id optional user id, defaults to the current user
65ba104c 601 * @return stdClass containing properties lastname, firstname, picture and imagealt
b8ead2e6
DM
602 */
603 public function user_info($id=null) {
604 global $USER, $DB;
605
606 if (is_null($id) || ($id == $USER->id)) {
607 return $USER;
608 } else {
609 return $DB->get_record('user', array('id' => $id), 'id,lastname,firstname,picture,imagealt', MUST_EXIST);
610 }
611 }
612
c1e883bb 613 /**
b13142da 614 * Are users allowed to create/edit their submissions?
c1e883bb
DM
615 *
616 * TODO: this depends on the workshop phase, phase deadlines, submitting after deadlines possibility
617 *
618 * @return bool
619 */
620 public function submitting_allowed() {
621 return true;
622 }
623
b13142da
DM
624 /**
625 * Returns the localized name of the grading strategy method to be displayed to the users
626 *
627 * @return string
628 */
629 public function strategy_name() {
630 return get_string('pluginname', 'workshopgrading_' . $this->strategy);
631 }
b761e6d9
DM
632
633 /**
634 * Prepare an individual workshop plan for the given user.
635 *
636 * @param mixed $userid
637 * @return TODO
638 */
639 public function prepare_user_plan($userid) {
640 global $DB;
641
642 $phases = array();
643
644 // Prepare tasks for the setup phase
645 $phase = new stdClass();
646 $phase->title = get_string('phasesetup', 'workshop');
647 $phase->tasks = array();
648 if (has_capability('mod/workshop:editdimensions', $this->context, $userid)) {
649 $task = new stdClass();
650 $task->title = get_string('taskeditform', 'workshop');
651 $task->completed = $this->assessment_form_ready();
652 $phase->tasks['editform'] = $task;
653 }
654 $phases[self::PHASE_SETUP] = $phase;
655
656 // Prepare tasks for the submission phase
657 $phase = new stdClass();
658 $phase->title = get_string('phasesubmission', 'workshop');
659 $phase->tasks = array();
660 if (has_capability('mod/workshop:submit', $this->context, $userid)) {
661 $task = new stdClass();
662 $task->title = get_string('tasksubmit', 'workshop');
663 $task->completed = $DB->record_exists('workshop_submissions',
664 array('workshopid' => $this->id, 'example' => 0, 'userid' => $userid));
665 $phase->tasks['submit'] = $task;
666 }
667 $phases[self::PHASE_SUBMISSION] = $phase;
668
669 // Prepare tasks for the peer-assessment phase (includes eventual self-assessments)
670 $phase = new stdClass();
671 $phase->title = get_string('phaseassessment', 'workshop');
672 $phase->tasks = array();
673 $phase->isreviewer = has_capability('mod/workshop:peerassess', $this->context, $userid);
674 $phase->assessments = $this->get_assessments($userid); // todo make sure this does not contain assessment of examples
675 $numofpeers = 0; // number of allocated peer-assessments
676 $numofpeerstodo = 0; // number of peer-assessments to do
677 $numofself = 0; // number of allocated self-assessments - should be 0 or 1
678 $numofselftodo = 0; // number of self-assessments to do - should be 0 or 1
679 foreach ($phase->assessments as $a) {
680 if ($a->authorid == $userid) {
681 $numofself++;
682 if (is_null($a->grade)) {
683 $numofselftodo++;
684 }
685 } else {
686 $numofpeers++;
687 if (is_null($a->grade)) {
688 $numofpeerstodo++;
689 }
690 }
691 }
692 unset($a);
693 if ($numofpeers) {
694 $task = new stdClass();
695 $task->completed = ($numofpeerstodo == 0);
696 $a = new stdClass();
697 $a->total = $numofpeers;
698 $a->todo = $numofpeerstodo;
699 $task->title = get_string('taskassesspeers', 'workshop');
700 $task->info = get_string('taskassesspeersinfo', 'workshop', $a);
701 unset($a);
702 $phase->tasks['assesspeers'] = $task;
703 }
704 if ($numofself) {
705 $task = new stdClass();
706 $task->completed = ($numofselftodo == 0);
707 $task->title = get_string('taskassessself', 'workshop');
708 $phase->tasks['assessself'] = $task;
709 }
710 $phases[self::PHASE_ASSESSMENT] = $phase;
711
712 // Prepare tasks for the grading evaluation phase - todo
713 $phase = new stdClass();
714 $phase->title = get_string('phaseevaluation', 'workshop');
715 $phase->tasks = array();
716 $phases[self::PHASE_EVALUATION] = $phase;
717
718 // Prepare tasks for the "workshop closed" phase - todo
719 $phase = new stdClass();
720 $phase->title = get_string('phaseclosed', 'workshop');
721 $phase->tasks = array();
722 $phases[self::PHASE_CLOSED] = $phase;
723
724 // Polish data, set default values if not done explicitly
725 foreach ($phases as $phasecode => $phase) {
726 $phase->title = isset($phase->title) ? $phase->title : '';
727 $phase->tasks = isset($phase->tasks) ? $phase->tasks : array();
728 if ($phasecode == $this->phase) {
729 $phase->active = true;
730 } else {
731 $phase->active = false;
732 }
733
734 foreach ($phase->tasks as $taskcode => $task) {
735 $task->title = isset($task->title) ? $task->title : '';
736 $task->info = isset($task->info) ? $task->info : '';
737 $task->completed = isset($task->completed) ? $task->completed : null;
738 }
739 }
740 return $phases;
741 }
742
743 /**
744 * Has the assessment form been defined?
745 *
746 * @return bool
747 */
748 public function assessment_form_ready() {
749 return $this->grading_strategy_instance()->form_ready();
750 }
751
66c9894d 752}