Commit | Line | Data |
---|---|---|
de811c0c DM |
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 | /** | |
6e309973 | 20 | * Library of internal classes and functions for module workshop |
de811c0c DM |
21 | * |
22 | * All the workshop specific functions, needed to implement the module | |
6e309973 DM |
23 | * logic, should go to here. Instead of having bunch of function named |
24 | * workshop_something() taking the workshop instance as the first | |
25 | * parameter, we use a class workshop_api that provides all methods. | |
de811c0c DM |
26 | * |
27 | * @package mod-workshop | |
28 | * @copyright 2009 David Mudrak <david.mudrak@gmail.com> | |
29 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
30 | */ | |
31 | ||
32 | defined('MOODLE_INTERNAL') || die(); | |
33 | ||
6e309973 | 34 | require_once(dirname(__FILE__).'/lib.php'); // we extend this library here |
0968b1a3 | 35 | |
6e309973 DM |
36 | define('WORKSHOP_ALLOCATION_EXISTS', -1); // return status of {@link add_allocation} |
37 | define('WORKSHOP_ALLOCATION_WOSUBMISSION', -2); // return status of {@link add_allocation} | |
38 | ||
39 | ||
40 | /** | |
41 | * Full-featured workshop API | |
42 | * | |
43 | * This extends the module base API and adds the internal methods that are called | |
44 | * from the module itself. The class should be initialized right after you get | |
45 | * $workshop and $cm records at the begining of the script. | |
46 | */ | |
47 | class workshop_api extends workshop { | |
48 | ||
49 | /** grading strategy instance */ | |
50 | protected $strategy_api=null; | |
51 | ||
52 | /** | |
53 | * Initialize the object using the data from DB | |
54 | * | |
55 | * @param object $instance The instance data row from {workshop} table | |
56 | * @param object $md Course module record | |
57 | */ | |
58 | public function __construct($instance, $cm) { | |
59 | parent::__construct($instance, $cm); | |
60 | } | |
61 | ||
62 | ||
63 | /** | |
64 | * Fetches all users with the capability mod/workshop:submit in the current context | |
65 | * | |
66 | * Static variable used to cache the results. The returned objects contain id, lastname | |
67 | * and firstname properties and are ordered by lastname,firstname | |
68 | * | |
69 | * @param object $context The context instance where the capability should be checked | |
70 | * @return array Array of '(int)userid => (stdClass)userinfo' | |
71 | */ | |
72 | public function get_peer_authors() { | |
73 | static $users=null; | |
74 | ||
75 | if (is_null($users)) { | |
76 | $context = get_context_instance(CONTEXT_MODULE, $this->cm->id); | |
77 | $users = get_users_by_capability($context, 'mod/workshop:submit', | |
78 | 'u.id, u.lastname, u.firstname', 'u.lastname,u.firstname', '', '', '', '', false, false, true); | |
79 | } | |
80 | return $users; | |
81 | } | |
82 | ||
83 | ||
84 | /** | |
85 | * Fetches all users with the capability mod/workshop:peerassess in the current context | |
86 | * | |
87 | * Static variable used to cache the results. The returned objects contain id, lastname | |
88 | * and firstname properties and are ordered by lastname,firstname | |
89 | * | |
90 | * @param object $context The context instance where the capability should be checked | |
91 | * @return array Array of '(int)userid => (stdClass)userinfo' | |
92 | */ | |
93 | public function get_peer_reviewers() { | |
94 | global $DB; | |
95 | static $users=null; | |
96 | ||
97 | if (is_null($users)) { | |
98 | $context = get_context_instance(CONTEXT_MODULE, $this->cm->id); | |
99 | $users = get_users_by_capability($context, 'mod/workshop:peerassess', | |
100 | 'u.id, u.lastname, u.firstname', 'u.lastname,u.firstname', '', '', '', '', false, false, true); | |
101 | } | |
102 | if (!$this->assesswosubmission) { | |
103 | $userswithsubmission = array(); | |
104 | // users without their own submission can not be reviewers | |
105 | $rs = $DB->get_recordset_list('workshop_submissions', 'userid', array_keys($users),'', 'id,userid'); | |
106 | foreach ($rs as $submission) { | |
107 | if (isset($users[$submission->userid])) { | |
108 | $userswithsubmission[$submission->userid] = 'submission_exists'; | |
109 | } else { | |
110 | // this is a submission by a user who does not have mod/workshop:peerassess | |
111 | // this is either bug or workshop capabilities have been overridden after the submission | |
112 | } | |
113 | } | |
114 | $rs->close(); | |
115 | return array_intersect_key($users, $userswithsubmission); | |
0968b1a3 | 116 | } else { |
6e309973 | 117 | return $users; |
0968b1a3 | 118 | } |
0968b1a3 DM |
119 | } |
120 | ||
6e309973 DM |
121 | |
122 | /** | |
123 | * Returns submissions from this workshop | |
124 | * | |
125 | * Fetches data from {workshop_submissions} and adds some useful information from other | |
126 | * tables. | |
127 | * | |
128 | * @param mixed $userid If set to integer ID, return submission of the given user only | |
129 | * @param mixed $examples false|true|all Only regular submissions, only examples, all submissions | |
130 | * @todo unittest | |
131 | * @return object moodle_recordset | |
132 | */ | |
133 | public function get_submissions($userid='all', $examples=false) { | |
134 | global $DB; | |
135 | ||
136 | $sql = 'SELECT s.*, u.lastname AS authorlastname, u.firstname AS authorfirstname | |
137 | FROM {workshop_submissions} s | |
138 | JOIN {user} u ON (s.userid = u.id) | |
139 | WHERE s.workshopid = ?'; | |
140 | $params[0] = $this->id; | |
141 | ||
142 | if ($examples === false) { | |
143 | $sql .= ' AND example = 0'; | |
144 | } | |
145 | if ($examples === true) { | |
146 | $sql .= ' AND example = 1'; | |
147 | } | |
148 | if (is_int($userid)) { | |
149 | $sql .= ' AND userid = ?'; | |
150 | $params = array_merge($params, array($userid)); | |
151 | } | |
152 | if (is_array($userid)) { | |
153 | list($usql, $uparams) = $DB->get_in_or_equal($userid); | |
154 | $sql .= ' AND userid ' . $usql; | |
155 | $params = array_merge($params, $uparams); | |
156 | } | |
157 | ||
158 | return $DB->get_recordset_sql($sql, $params); | |
159 | } | |
160 | ||
161 | ||
162 | /** | |
163 | * Returns the list of assessments with some data added | |
164 | * | |
165 | * Fetches data from {workshop_assessments} and adds some useful information from other | |
166 | * tables. | |
167 | * | |
168 | * @param mixed $reviewerid 'all'|int|array User ID of the reviewer | |
169 | * @param mixed $id 'all'|int Assessment ID | |
170 | * @return object moodle_recordset | |
171 | */ | |
172 | public function get_assessments($reviewerid='all', $id='all') { | |
173 | global $DB; | |
174 | ||
175 | $sql = 'SELECT a.*, | |
176 | reviewer.id AS reviewerid,reviewer.firstname AS reviewerfirstname,reviewer.lastname as reviewerlastname, | |
177 | s.title, | |
178 | author.id AS authorid, author.firstname AS authorfirstname,author.lastname as authorlastname | |
179 | FROM {workshop_assessments} a | |
180 | LEFT JOIN {user} reviewer ON (a.userid = reviewer.id) | |
181 | LEFT JOIN {workshop_submissions} s ON (a.submissionid = s.id) | |
182 | LEFT JOIN {user} author ON (s.userid = author.id) | |
183 | WHERE s.workshopid = ?'; | |
184 | $params = array($this->id); | |
185 | if (is_int($reviewerid)) { | |
186 | $sql .= ' AND reviewerid = ?'; | |
187 | $params = array_merge($params, array($reviewerid)); | |
188 | } | |
189 | if (is_array($reviewerid)) { | |
190 | list($usql, $uparams) = $DB->get_in_or_equal($reviewerid); | |
191 | $sql .= ' AND reviewerid ' . $usql; | |
192 | $params = array_merge($params, $uparams); | |
193 | } | |
194 | if (is_int($id)) { | |
195 | $sql .= ' AND a.id = ?'; | |
196 | $params = array_merge($params, array($id)); | |
197 | } | |
198 | ||
199 | return $DB->get_recordset_sql($sql, $params); | |
200 | } | |
201 | ||
202 | ||
203 | /** | |
204 | * Returns the list of allocations in the workshop | |
205 | * | |
206 | * This returns the list of all users who can submit their work or review submissions (or both | |
207 | * which is the common case). So basically this is to return list of all students participating | |
208 | * in the workshop. For every participant, it adds information about their submission and their | |
209 | * reviews. This is mainly intended for allocation reports and originally was written for | |
210 | * manula allocation ui. | |
211 | * | |
212 | * The returned structure is recordset of objects with following properties: | |
213 | * [authorid] [authorfirstname] [authorlastname] [authorpicture] [authorimagealt] | |
214 | * [submissionid] [submissiontitle] [submissiongrade] [assessmentid] | |
215 | * [timeallocated] [reviewerid] [reviewerfirstname] [reviewerlastname] | |
216 | * [reviewerpicture] [reviewerimagealt] | |
217 | * | |
218 | * This should be refactored when capability handling proposed by Petr is implemented so that | |
219 | * we can check capabilities directly in SQL joins. | |
220 | * | |
221 | * @return object moodle_recordset | |
222 | */ | |
223 | public function get_allocations() { | |
224 | global $DB; | |
225 | static $users=null; | |
226 | ||
227 | if (is_null($users)) { | |
228 | $context = get_context_instance(CONTEXT_MODULE, $this->cm->id); | |
229 | $users = get_users_by_capability($context, array('mod/workshop:submit', 'mod/workshop:peerassess'), | |
230 | 'u.id', 'u.lastname,u.firstname', '', '', '', '', false, false, true); | |
231 | } | |
232 | ||
233 | list($usql, $params) = $DB->get_in_or_equal(array_keys($users)); | |
234 | $params[] = $this->id; | |
235 | ||
236 | $sql = 'SELECT author.id AS authorid, author.firstname AS authorfirstname, author.lastname AS authorlastname, | |
237 | author.picture AS authorpicture, author.imagealt AS authorimagealt, | |
238 | s.id AS submissionid, s.title AS submissiontitle, s.grade AS submissiongrade, | |
239 | a.id AS assessmentid, a.timecreated AS timeallocated, a.userid AS reviewerid, | |
240 | reviewer.firstname AS reviewerfirstname, reviewer.lastname AS reviewerlastname, | |
241 | reviewer.picture as reviewerpicture, reviewer.imagealt AS reviewerimagealt | |
242 | FROM {user} author | |
243 | LEFT JOIN {workshop_submissions} s ON (s.userid = author.id) | |
244 | LEFT JOIN {workshop_assessments} a ON (s.id = a.submissionid) | |
245 | LEFT JOIN {user} reviewer ON (a.userid = reviewer.id) | |
246 | WHERE author.id ' . $usql . ' AND (s.workshopid = ? OR s.workshopid IS NULL) | |
247 | ORDER BY author.lastname,author.firstname,reviewer.lastname,reviewer.firstname'; | |
248 | return $DB->get_recordset_sql($sql, $params); | |
249 | } | |
250 | ||
251 | ||
252 | /** | |
253 | * Allocate a submission to a user for review | |
254 | * | |
255 | * @param object $submission Submission record | |
256 | * @param int $reviewerid User ID | |
257 | * @return int ID of the new assessment or an error code | |
258 | */ | |
259 | public function add_allocation(stdClass $submission, $reviewerid) { | |
260 | global $DB; | |
261 | ||
262 | if ($DB->record_exists('workshop_assessments', array('submissionid' => $submission->id, 'userid' => $reviewerid))) { | |
263 | return WORKSHOP_ALLOCATION_EXISTS; | |
264 | } | |
265 | ||
266 | if (!$this->assesswosubmission) { | |
267 | // reviewer must have submitted his own work | |
268 | if (!$DB->record_exists('workshop_submissions', array('workshopid' => $this->id, 'userid' => $reviewerid))) { | |
269 | return WORKSHOP_ALLOCATION_WOSUBMISSION; | |
270 | } | |
271 | } | |
272 | ||
273 | $now = time(); | |
274 | $assessment = new stdClass(); | |
275 | $assessment->submissionid = $submission->id; | |
276 | $assessment->userid = $reviewerid; | |
277 | $assessment->timecreated = $now; | |
278 | $assessment->timemodified = $now; | |
279 | ||
280 | return $DB->insert_record('workshop_assessments', $assessment); | |
281 | } | |
282 | ||
283 | ||
284 | /** | |
285 | * delete_assessment | |
286 | * | |
287 | * @todo finish and document this method | |
288 | * | |
289 | */ | |
290 | public function delete_assessment($id) { | |
291 | global $DB; | |
292 | ||
293 | // todo remove all given grades from workshop_grades; | |
294 | return $DB->delete_records('workshop_assessments', array('id' => $id)); | |
295 | } | |
296 | ||
297 | ||
298 | /** | |
299 | * Returns instance of grading strategy class | |
300 | * | |
301 | * @param object $workshop Workshop record | |
302 | * @return object Instance of a grading strategy | |
303 | */ | |
304 | public function grading_strategy_instance() { | |
305 | ||
306 | if (!($this->strategy === clean_param($workshop->strategy, PARAM_ALPHA))) { | |
307 | throw new moodle_workshop_exception($this, 'invalidstrategyname'); | |
308 | } | |
309 | ||
310 | if (is_null($this->strategy_api)) { | |
311 | $strategylib = dirname(__FILE__) . '/grading/' . $workshop->strategy . '/strategy.php'; | |
312 | if (is_readable($strategylib)) { | |
313 | require_once($strategylib); | |
314 | } else { | |
315 | throw new moodle_exception('missingstrategy', 'workshop'); | |
316 | } | |
317 | $classname = 'workshop_' . $workshop->strategy . '_strategy'; | |
318 | $this->strategy_api = new $classname($this); | |
319 | if (!in_array('workshop_strategy', class_implements($this->strategy_api))) { | |
320 | throw new moodle_workshop_exception($this, 'strategynotimplemented'); | |
321 | } | |
322 | } | |
323 | ||
324 | return $this->strategy_api; | |
325 | } | |
326 | ||
327 | ||
328 | ||
0968b1a3 DM |
329 | } |
330 | ||
331 | ||
6e309973 DM |
332 | |
333 | ||
de811c0c | 334 | /** |
6e309973 DM |
335 | * Class for workshop exceptions. Just saves a couple of arguments of the |
336 | * constructor for a moodle_exception. | |
de811c0c | 337 | * |
6e309973 DM |
338 | * @param object $workshop Should be workshop or its subclass |
339 | * @param string $errorcode | |
340 | * @param mixed $a Object/variable to pass to get_string | |
341 | * @param string $link URL to continue after the error notice | |
342 | * @param $debuginfo | |
de811c0c | 343 | */ |
6e309973 | 344 | class moodle_workshop_exception extends moodle_exception { |
de811c0c | 345 | |
6e309973 DM |
346 | function __construct($workshop, $errorcode, $a = NULL, $link = '', $debuginfo = null) { |
347 | global $CFG; | |
348 | ||
349 | if (!$link) { | |
350 | $link = $CFG->wwwroot . '/mod/workshop/view.php?a=' . $workshop->id; | |
351 | } | |
352 | if ('confirmsesskeybad' == $errorcode) { | |
353 | $module = ''; | |
354 | } else { | |
355 | $module = 'workshop'; | |
356 | } | |
357 | parent::__construct($errorcode, $module, $link, $a, $debuginfo); | |
358 | } | |
de811c0c DM |
359 | } |
360 | ||
361 | ||
6e309973 | 362 |