3af29899 |
1 | <?php //$Id$ |
cbff94ba |
2 | |
8ad36f4c |
3 | /////////////////////////////////////////////////////////////////////////// |
4 | // // |
5 | // NOTICE OF COPYRIGHT // |
6 | // // |
7 | // Moodle - Modular Object-Oriented Dynamic Learning Environment // |
8 | // http://moodle.com // |
9 | // // |
10 | // Copyright (C) 1999 onwards Martin Dougiamas http://moodle.com // |
11 | // // |
12 | // This program is free software; you can redistribute it and/or modify // |
13 | // it under the terms of the GNU General Public License as published by // |
14 | // the Free Software Foundation; either version 2 of the License, or // |
15 | // (at your option) any later version. // |
16 | // // |
17 | // This program is distributed in the hope that it will be useful, // |
18 | // but WITHOUT ANY WARRANTY; without even the implied warranty of // |
19 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // |
20 | // GNU General Public License for more details: // |
21 | // // |
22 | // http://www.gnu.org/copyleft/gpl.html // |
23 | // // |
24 | /////////////////////////////////////////////////////////////////////////// |
25 | |
7a6b7acf |
26 | require_once $CFG->libdir.'/gradelib.php'; |
27 | |
0f5660f7 |
28 | /** |
29 | * This class iterates over all users that are graded in a course. |
b9f49659 |
30 | * Returns detailed info about users and their grades. |
0f5660f7 |
31 | */ |
32 | class graded_users_iterator { |
33 | var $course; |
34 | var $grade_items; |
35 | var $groupid; |
36 | var $users_rs; |
37 | var $grades_rs; |
38 | var $gradestack; |
d08bba83 |
39 | var $sortfield1; |
40 | var $sortorder1; |
41 | var $sortfield2; |
42 | var $sortorder2; |
0f5660f7 |
43 | |
44 | /** |
45 | * Constructor |
d08bba83 |
46 | * @param $course object |
0f5660f7 |
47 | * @param array grade_items array of grade items, if not specified only user info returned |
48 | * @param int $groupid iterate only group users if present |
d08bba83 |
49 | * @param string $sortfield1 The first field of the users table by which the array of users will be sorted |
50 | * @param string $sortorder1 The order in which the first sorting field will be sorted (ASC or DESC) |
51 | * @param string $sortfield2 The second field of the users table by which the array of users will be sorted |
52 | * @param string $sortorder2 The order in which the second sorting field will be sorted (ASC or DESC) |
0f5660f7 |
53 | */ |
d08bba83 |
54 | function graded_users_iterator($course, $grade_items=null, $groupid=0, $sortfield1='lastname', $sortorder1='ASC', $sortfield2='firstname', $sortorder2='ASC') { |
0f5660f7 |
55 | $this->course = $course; |
56 | $this->grade_items = $grade_items; |
57 | $this->groupid = $groupid; |
d08bba83 |
58 | $this->sortfield1 = $sortfield1; |
59 | $this->sortorder1 = $sortorder1; |
60 | $this->sortfield2 = $sortfield2; |
61 | $this->sortorder2 = $sortorder2; |
0f5660f7 |
62 | |
63 | $this->gradestack = array(); |
64 | } |
65 | |
66 | /** |
67 | * Initialise the iterator |
68 | * @return boolean success |
69 | */ |
70 | function init() { |
71 | global $CFG; |
72 | |
73 | $this->close(); |
74 | |
75 | grade_regrade_final_grades($this->course->id); |
76 | $course_item = grade_item::fetch_course_item($this->course->id); |
77 | if ($course_item->needsupdate) { |
78 | // can not calculate all final grades - sorry |
79 | return false; |
80 | } |
81 | |
c0e9f877 |
82 | if (strpos($CFG->gradebookroles, ',') === false) { |
0f5660f7 |
83 | $gradebookroles = " = {$CFG->gradebookroles}"; |
84 | } else { |
85 | $gradebookroles = " IN ({$CFG->gradebookroles})"; |
86 | } |
87 | |
88 | $relatedcontexts = get_related_contexts_string(get_context_instance(CONTEXT_COURSE, $this->course->id)); |
89 | |
90 | if ($this->groupid) { |
91 | $groupsql = "INNER JOIN {$CFG->prefix}groups_members gm ON gm.userid = u.id"; |
92 | $groupwheresql = "AND gm.groupid = {$this->groupid}"; |
93 | } else { |
94 | $groupsql = ""; |
95 | $groupwheresql = ""; |
96 | } |
97 | |
345674ca |
98 | if (empty($this->sortfield1)) { |
99 | // we must do some sorting even if not specified |
100 | $ofields = ", u.id AS usrt"; |
101 | $order = "usrt ASC"; |
102 | |
103 | } else { |
104 | $ofields = ", u.$this->sortfield1 AS usrt1"; |
105 | $order = "usrt1 $this->sortorder1"; |
106 | if (!empty($this->sortfield2)) { |
107 | $ofields .= ", u.$this->sortfield1 AS usrt2"; |
108 | $order .= ", usrt2 $this->sortorder2"; |
109 | } |
110 | if ($this->sortfield1 != 'id' and $this->sortfield2 != 'id') { |
111 | // user order MUST be the same in both queries, must include the only unique user->id if not already present |
112 | $ofields .= ", u.id AS usrt"; |
113 | $order .= ", usrt ASC"; |
114 | } |
115 | } |
116 | |
117 | $users_sql = "SELECT u.* $ofields |
0f5660f7 |
118 | FROM {$CFG->prefix}user u |
119 | INNER JOIN {$CFG->prefix}role_assignments ra ON u.id = ra.userid |
120 | $groupsql |
121 | WHERE ra.roleid $gradebookroles |
122 | AND ra.contextid $relatedcontexts |
345674ca |
123 | $groupwheresql |
124 | ORDER BY $order"; |
d08bba83 |
125 | |
345674ca |
126 | $this->users_rs = get_recordset_sql($users_sql); |
0f5660f7 |
127 | |
128 | if (!empty($this->grade_items)) { |
129 | $itemids = array_keys($this->grade_items); |
130 | $itemids = implode(',', $itemids); |
131 | |
345674ca |
132 | $grades_sql = "SELECT g.* $ofields |
0f5660f7 |
133 | FROM {$CFG->prefix}grade_grades g |
0f5660f7 |
134 | INNER JOIN {$CFG->prefix}user u ON g.userid = u.id |
135 | INNER JOIN {$CFG->prefix}role_assignments ra ON u.id = ra.userid |
136 | $groupsql |
137 | WHERE ra.roleid $gradebookroles |
138 | AND ra.contextid $relatedcontexts |
139 | AND g.itemid IN ($itemids) |
140 | $groupwheresql |
345674ca |
141 | ORDER BY $order, g.itemid ASC"; |
caffc55a |
142 | $this->grades_rs = get_recordset_sql($grades_sql); |
345674ca |
143 | } else { |
144 | $this->grades_rs = false; |
0f5660f7 |
145 | } |
345674ca |
146 | |
0f5660f7 |
147 | return true; |
148 | } |
149 | |
150 | /** |
151 | * Returns information about the next user |
152 | * @return mixed array of user info, all grades and feedback or null when no more users found |
153 | */ |
154 | function next_user() { |
03cedd62 |
155 | if (!$this->users_rs) { |
0f5660f7 |
156 | return false; // no users present |
157 | } |
158 | |
caffc55a |
159 | if (!$user = rs_fetch_next_record($this->users_rs)) { |
345674ca |
160 | if ($current = $this->_pop()) { |
161 | // this is not good - user or grades updated between the two reads above :-( |
162 | } |
163 | |
0f5660f7 |
164 | return false; // no more users |
165 | } |
166 | |
345674ca |
167 | // find grades of this user |
0f5660f7 |
168 | $grade_records = array(); |
169 | while (true) { |
170 | if (!$current = $this->_pop()) { |
171 | break; // no more grades |
172 | } |
173 | |
345674ca |
174 | if ($current->userid != $user->id) { |
175 | // grade of the next user, we have all for this user |
0f5660f7 |
176 | $this->_push($current); |
177 | break; |
178 | } |
179 | |
180 | $grade_records[$current->itemid] = $current; |
181 | } |
182 | |
183 | $grades = array(); |
184 | $feedbacks = array(); |
185 | |
d08bba83 |
186 | if (!empty($this->grade_items)) { |
345674ca |
187 | foreach ($this->grade_items as $grade_item) { |
188 | if (array_key_exists($grade_item->id, $grade_records)) { |
189 | $feedbacks[$grade_item->id]->feedback = $grade_records[$grade_item->id]->feedback; |
190 | $feedbacks[$grade_item->id]->feedbackformat = $grade_records[$grade_item->id]->feedbackformat; |
191 | unset($grade_records[$grade_item->id]->feedback); |
192 | unset($grade_records[$grade_item->id]->feedbackformat); |
193 | $grades[$grade_item->id] = new grade_grade($grade_records[$grade_item->id], false); |
194 | } else { |
195 | $feedbacks[$grade_item->id]->feedback = ''; |
196 | $feedbacks[$grade_item->id]->feedbackformat = FORMAT_MOODLE; |
197 | $grades[$grade_item->id] = new grade_grade(array('userid'=>$user->id, 'itemid'=>$grade_item->id), false); |
198 | } |
0f5660f7 |
199 | } |
200 | } |
201 | |
202 | $result = new object(); |
203 | $result->user = $user; |
204 | $result->grades = $grades; |
205 | $result->feedbacks = $feedbacks; |
206 | |
207 | return $result; |
208 | } |
209 | |
210 | /** |
211 | * Close the iterator, do not forget to call this function. |
212 | * @return void |
213 | */ |
214 | function close() { |
caffc55a |
215 | if ($this->users_rs) { |
216 | rs_close($this->users_rs); |
217 | $this->users_rs = null; |
0f5660f7 |
218 | } |
caffc55a |
219 | if ($this->grades_rs) { |
220 | rs_close($this->grades_rs); |
221 | $this->grades_rs = null; |
0f5660f7 |
222 | } |
223 | $this->gradestack = array(); |
224 | } |
225 | |
226 | /** |
227 | * Internal function |
228 | */ |
229 | function _push($grade) { |
230 | array_push($this->gradestack, $grade); |
231 | } |
232 | |
233 | /** |
234 | * Internal function |
235 | */ |
236 | function _pop() { |
237 | if (empty($this->gradestack)) { |
03cedd62 |
238 | if (!$this->grades_rs) { |
0f5660f7 |
239 | return NULL; // no grades present |
240 | } |
241 | |
caffc55a |
242 | if (!$grade = rs_fetch_next_record($this->grades_rs)) { |
0f5660f7 |
243 | return NULL; // no more grades |
244 | } |
245 | |
246 | return $grade; |
247 | } else { |
248 | return array_pop($this->gradestack); |
249 | } |
250 | } |
251 | } |
252 | |
d08bba83 |
253 | /** |
254 | * Print a selection popup form of the graded users in a course. |
255 | * |
256 | * @param int $courseid id of the course |
257 | * @param string $actionpage The page receiving the data from the popoup form |
258 | * @param int $userid id of the currently selected user (or 'all' if they are all selected) |
259 | * @param bool $return If true, will return the HTML, otherwise, will print directly |
260 | * @return null |
261 | */ |
879c99bb |
262 | function print_graded_users_selector($course, $actionpage, $userid=null, $return=false) { |
263 | global $CFG, $USER; |
345674ca |
264 | |
879c99bb |
265 | if (is_null($userid)) { |
266 | $userid = $USER->id; |
267 | } |
d08bba83 |
268 | |
269 | $context = get_context_instance(CONTEXT_COURSE, $course->id); |
270 | |
271 | $menu = array(); // Will be a list of userid => user name |
272 | |
273 | $gui = new graded_users_iterator($course); |
274 | $gui->init(); |
345674ca |
275 | |
879c99bb |
276 | if ($userid !== 0) { |
277 | $menu[0] = get_string('allusers', 'grades'); |
d08bba83 |
278 | } |
345674ca |
279 | |
d08bba83 |
280 | while ($userdata = $gui->next_user()) { |
281 | $user = $userdata->user; |
282 | $menu[$user->id] = fullname($user); |
283 | } |
284 | |
285 | $gui->close(); |
286 | |
879c99bb |
287 | if ($userid !== 0) { |
288 | $menu[0] .= " (" . (count($menu) - 1) . ")"; |
289 | } |
290 | |
345674ca |
291 | return popup_form($CFG->wwwroot.'/grade/' . $actionpage . '&userid=', $menu, 'choosegradeduser', $userid, 'choose', '', '', |
292 | $return, 'self', get_string('selectalloroneuser', 'grades')); |
d08bba83 |
293 | } |
294 | |
0610812a |
295 | /** |
296 | * Print grading plugin selection popup form. |
297 | * |
298 | * @param int $courseid id of course |
299 | * @param string $active_type type of plugin on current page - import, export, report or edit |
300 | * @param string $active_plugin active plugin type - grader, user, cvs, ... |
301 | * @param boolean $return return as string |
302 | * @return nothing or string if $return true |
303 | */ |
3af29899 |
304 | function print_grade_plugin_selector($courseid, $active_type, $active_plugin, $return=false) { |
cbff94ba |
305 | global $CFG; |
cbff94ba |
306 | |
3af29899 |
307 | $context = get_context_instance(CONTEXT_COURSE, $courseid); |
cbff94ba |
308 | |
3af29899 |
309 | $menu = array(); |
6e2f3121 |
310 | $count = 0; |
3af29899 |
311 | $active = ''; |
cbff94ba |
312 | |
3af29899 |
313 | /// report plugins with its special structure |
314 | if ($reports = get_list_of_plugins('grade/report', 'CVS')) { // Get all installed reports |
315 | foreach ($reports as $key => $plugin) { // Remove ones we can't see |
316 | if (!has_capability('gradereport/'.$plugin.':view', $context)) { |
317 | unset($reports[$key]); |
cbff94ba |
318 | } |
319 | } |
04678d8e |
320 | } |
3af29899 |
321 | $reportnames = array(); |
322 | if (!empty($reports)) { |
323 | foreach ($reports as $plugin) { |
65dd61bd |
324 | $url = 'report/'.$plugin.'/index.php?id='.$courseid; |
3af29899 |
325 | if ($active_type == 'report' and $active_plugin == $plugin ) { |
326 | $active = $url; |
cbff94ba |
327 | } |
6e2f3121 |
328 | $reportnames[$url] = get_string('modulename', 'gradereport_'.$plugin); |
329 | $count++; |
cbff94ba |
330 | } |
3af29899 |
331 | asort($reportnames); |
cbff94ba |
332 | } |
3af29899 |
333 | if (!empty($reportnames)) { |
4d526fb1 |
334 | $menu['reportgroup']='--'.get_string('view'); |
3af29899 |
335 | $menu = $menu+$reportnames; |
cbff94ba |
336 | } |
cbff94ba |
337 | |
3af29899 |
338 | /// standard import plugins |
e2008be2 |
339 | if ($imports = get_list_of_plugins('grade/import', 'CVS')) { // Get all installed import plugins |
3af29899 |
340 | foreach ($imports as $key => $plugin) { // Remove ones we can't see |
341 | if (!has_capability('gradeimport/'.$plugin.':view', $context)) { |
342 | unset($imports[$key]); |
cbff94ba |
343 | } |
344 | } |
345 | } |
3af29899 |
346 | $importnames = array(); |
347 | if (!empty($imports)) { |
348 | foreach ($imports as $plugin) { |
349 | $url = 'import/'.$plugin.'/index.php?id='.$courseid; |
65dd61bd |
350 | if ($active_type == 'import' and $active_plugin == $plugin ) { |
3af29899 |
351 | $active = $url; |
352 | } |
6e2f3121 |
353 | $importnames[$url] = get_string('modulename', 'gradeimport_'.$plugin); |
354 | $count++; |
281ffa4a |
355 | } |
3af29899 |
356 | asort($importnames); |
281ffa4a |
357 | } |
3af29899 |
358 | if (!empty($importnames)) { |
4d526fb1 |
359 | $menu['importgroup']='--'.get_string('importfrom', 'grades'); |
3af29899 |
360 | $menu = $menu+$importnames; |
281ffa4a |
361 | } |
281ffa4a |
362 | |
3af29899 |
363 | /// standard export plugins |
e2008be2 |
364 | if ($exports = get_list_of_plugins('grade/export', 'CVS')) { // Get all installed export plugins |
3af29899 |
365 | foreach ($exports as $key => $plugin) { // Remove ones we can't see |
366 | if (!has_capability('gradeexport/'.$plugin.':view', $context)) { |
367 | unset($exports[$key]); |
281ffa4a |
368 | } |
369 | } |
cbff94ba |
370 | } |
3af29899 |
371 | $exportnames = array(); |
372 | if (!empty($exports)) { |
373 | foreach ($exports as $plugin) { |
374 | $url = 'export/'.$plugin.'/index.php?id='.$courseid; |
65dd61bd |
375 | if ($active_type == 'export' and $active_plugin == $plugin ) { |
3af29899 |
376 | $active = $url; |
377 | } |
6e2f3121 |
378 | $exportnames[$url] = get_string('modulename', 'gradeexport_'.$plugin); |
379 | $count++; |
281ffa4a |
380 | } |
3af29899 |
381 | asort($exportnames); |
cbff94ba |
382 | } |
3af29899 |
383 | if (!empty($exportnames)) { |
4d526fb1 |
384 | $menu['exportgroup']='--'.get_string('exportto', 'grades'); |
3af29899 |
385 | $menu = $menu+$exportnames; |
281ffa4a |
386 | } |
cbff94ba |
387 | |
3af29899 |
388 | /// editing scripts - not real plugins |
78ad5f3f |
389 | if (has_capability('moodle/grade:manage', $context) |
9376f5a6 |
390 | or has_capability('moodle/grade:manageletters', $context) |
04259694 |
391 | or has_capability('moodle/course:managescales', $context) |
392 | or has_capability('moodle/course:update', $context)) { |
3af29899 |
393 | $menu['edit']='--'.get_string('edit'); |
78ad5f3f |
394 | |
395 | if (has_capability('moodle/grade:manage', $context)) { |
396 | $url = 'edit/tree/index.php?id='.$courseid; |
397 | if ($active_type == 'edit' and $active_plugin == 'tree' ) { |
398 | $active = $url; |
399 | } |
400 | $menu[$url] = get_string('edittree', 'grades'); |
6e2f3121 |
401 | $count++; |
78ad5f3f |
402 | } |
403 | |
404 | if (has_capability('moodle/course:managescales', $context)) { |
405 | $url = 'edit/scale/index.php?id='.$courseid; |
406 | if ($active_type == 'edit' and $active_plugin == 'scale' ) { |
407 | $active = $url; |
408 | } |
409 | $menu[$url] = get_string('scales'); |
6e2f3121 |
410 | $count++; |
78ad5f3f |
411 | } |
412 | |
2b0f65e2 |
413 | if (!empty($CFG->enableoutcomes) && (has_capability('moodle/grade:manage', $context) or |
2a598439 |
414 | has_capability('moodle/course:update', $context))) { |
415 | if (has_capability('moodle/course:update', $context)) { // Default to course assignment |
04259694 |
416 | $url = 'edit/outcome/course.php?id='.$courseid; |
2a598439 |
417 | } else { |
418 | $url = 'edit/outcome/index.php?id='.$courseid; |
04259694 |
419 | } |
78ad5f3f |
420 | if ($active_type == 'edit' and $active_plugin == 'outcome' ) { |
421 | $active = $url; |
422 | } |
423 | $menu[$url] = get_string('outcomes', 'grades'); |
6e2f3121 |
424 | $count++; |
cbff94ba |
425 | } |
284abb09 |
426 | |
9376f5a6 |
427 | if (has_capability('moodle/grade:manage', $context) or has_capability('moodle/grade:manageletters', $context)) { |
284abb09 |
428 | $url = 'edit/letter/index.php?id='.$courseid; |
429 | if ($active_type == 'edit' and $active_plugin == 'letter' ) { |
430 | $active = $url; |
431 | } |
432 | $menu[$url] = get_string('letters', 'grades'); |
6e2f3121 |
433 | $count++; |
284abb09 |
434 | } |
435 | |
e0724506 |
436 | if (has_capability('moodle/grade:manage', $context)) { |
437 | $url = 'edit/settings/index.php?id='.$courseid; |
438 | if ($active_type == 'edit' and $active_plugin == 'settings' ) { |
439 | $active = $url; |
440 | } |
441 | $menu[$url] = get_string('coursesettings', 'grades'); |
6e2f3121 |
442 | $count++; |
e0724506 |
443 | } |
444 | |
281ffa4a |
445 | } |
446 | |
3af29899 |
447 | /// finally print/return the popup form |
6e2f3121 |
448 | if ($count > 1) { |
4d526fb1 |
449 | return popup_form($CFG->wwwroot.'/grade/', $menu, 'choosepluginreport', '', |
450 | get_string('chooseaction', 'grades'), '', '', $return, 'self'); |
6e2f3121 |
451 | } else { |
452 | // only one option - no plugin selector needed |
453 | return ''; |
454 | } |
cbff94ba |
455 | } |
456 | |
0610812a |
457 | /** |
7a6b7acf |
458 | * Utility class used for return tracking when using edit and other forms in grade plugins |
0610812a |
459 | */ |
3af29899 |
460 | class grade_plugin_return { |
461 | var $type; |
462 | var $plugin; |
463 | var $courseid; |
464 | var $userid; |
465 | var $page; |
281ffa4a |
466 | |
0610812a |
467 | /** |
468 | * Constructor |
469 | * @param array $params - associative array with return parameters, if null parameter are taken from _GET or _POST |
470 | */ |
3af29899 |
471 | function grade_plugin_return ($params=null) { |
472 | if (empty($params)) { |
473 | $this->type = optional_param('gpr_type', null, PARAM_SAFEDIR); |
474 | $this->plugin = optional_param('gpr_plugin', null, PARAM_SAFEDIR); |
475 | $this->courseid = optional_param('gpr_courseid', null, PARAM_INT); |
476 | $this->userid = optional_param('gpr_userid', null, PARAM_INT); |
477 | $this->page = optional_param('gpr_page', null, PARAM_INT); |
a983b6ec |
478 | |
a983b6ec |
479 | } else { |
3af29899 |
480 | foreach ($params as $key=>$value) { |
481 | if (array_key_exists($key, $this)) { |
482 | $this->$key = $value; |
483 | } |
cbff94ba |
484 | } |
485 | } |
6cd8c592 |
486 | } |
487 | |
0610812a |
488 | /** |
489 | * Returns return parameters as options array suitable for buttons. |
490 | * @return array options |
491 | */ |
3af29899 |
492 | function get_options() { |
7a6b7acf |
493 | if (empty($this->type)) { |
3af29899 |
494 | return array(); |
865e9a82 |
495 | } |
6cd8c592 |
496 | |
3af29899 |
497 | $params = array(); |
6cd8c592 |
498 | |
7a6b7acf |
499 | if (!empty($this->plugin)) { |
500 | $params['plugin'] = $this->plugin; |
501 | } |
6cd8c592 |
502 | |
3af29899 |
503 | if (!empty($this->courseid)) { |
504 | $params['id'] = $this->courseid; |
6cd8c592 |
505 | } |
9c61ba4d |
506 | |
3af29899 |
507 | if (!empty($this->userid)) { |
508 | $params['userid'] = $this->userid; |
9c61ba4d |
509 | } |
9c61ba4d |
510 | |
3af29899 |
511 | if (!empty($this->page)) { |
512 | $params['page'] = $this->page; |
cbff94ba |
513 | } |
865e9a82 |
514 | |
3af29899 |
515 | return $params; |
cbff94ba |
516 | } |
cbff94ba |
517 | |
0610812a |
518 | /** |
519 | * Returns return url |
520 | * @param string $default default url when params not set |
521 | * @return string url |
522 | */ |
65dd61bd |
523 | function get_return_url($default, $extras=null) { |
3af29899 |
524 | global $CFG; |
cbff94ba |
525 | |
3af29899 |
526 | if (empty($this->type) or empty($this->plugin)) { |
527 | return $default; |
cbff94ba |
528 | } |
529 | |
65dd61bd |
530 | $url = $CFG->wwwroot.'/grade/'.$this->type.'/'.$this->plugin.'/index.php'; |
531 | $glue = '?'; |
cbff94ba |
532 | |
3af29899 |
533 | if (!empty($this->courseid)) { |
534 | $url .= $glue.'id='.$this->courseid; |
535 | $glue = '&'; |
cbff94ba |
536 | } |
cbff94ba |
537 | |
3af29899 |
538 | if (!empty($this->userid)) { |
539 | $url .= $glue.'userid='.$this->userid; |
540 | $glue = '&'; |
cbff94ba |
541 | } |
7e2d7c92 |
542 | |
3af29899 |
543 | if (!empty($this->page)) { |
544 | $url .= $glue.'page='.$this->page; |
65dd61bd |
545 | $glue = '&'; |
546 | } |
547 | |
548 | if (!empty($extras)) { |
549 | foreach($extras as $key=>$value) { |
550 | $url .= $glue.$key.'='.$value; |
551 | $glue = '&'; |
7a6b7acf |
552 | } |
cbff94ba |
553 | } |
cbff94ba |
554 | |
3af29899 |
555 | return $url; |
cbff94ba |
556 | } |
cbff94ba |
557 | |
0610812a |
558 | /** |
559 | * Returns string with hidden return tracking form elements. |
560 | * @return string |
561 | */ |
3af29899 |
562 | function get_form_fields() { |
7a6b7acf |
563 | if (empty($this->type)) { |
3af29899 |
564 | return ''; |
cbff94ba |
565 | } |
cbff94ba |
566 | |
3af29899 |
567 | $result = '<input type="hidden" name="gpr_type" value="'.$this->type.'" />'; |
7a6b7acf |
568 | |
569 | if (!empty($this->plugin)) { |
570 | $result .= '<input type="hidden" name="gpr_plugin" value="'.$this->plugin.'" />'; |
571 | } |
0ca5abd6 |
572 | |
3af29899 |
573 | if (!empty($this->courseid)) { |
574 | $result .= '<input type="hidden" name="gpr_courseid" value="'.$this->courseid.'" />'; |
cbff94ba |
575 | } |
cbff94ba |
576 | |
3af29899 |
577 | if (!empty($this->userid)) { |
578 | $result .= '<input type="hidden" name="gpr_userid" value="'.$this->userid.'" />'; |
cbff94ba |
579 | } |
cbff94ba |
580 | |
3af29899 |
581 | if (!empty($this->page)) { |
582 | $result .= '<input type="hidden" name="gpr_page" value="'.$this->page.'" />'; |
cbff94ba |
583 | } |
584 | } |
cbff94ba |
585 | |
0610812a |
586 | /** |
587 | * Add hidden elements into mform |
588 | * @param object $mform moodle form object |
589 | * @return void |
590 | */ |
3af29899 |
591 | function add_mform_elements(&$mform) { |
7a6b7acf |
592 | if (empty($this->type)) { |
3af29899 |
593 | return; |
cbff94ba |
594 | } |
cbff94ba |
595 | |
3af29899 |
596 | $mform->addElement('hidden', 'gpr_type', $this->type); |
597 | $mform->setType('gpr_type', PARAM_SAFEDIR); |
cbff94ba |
598 | |
7a6b7acf |
599 | if (!empty($this->plugin)) { |
600 | $mform->addElement('hidden', 'gpr_plugin', $this->plugin); |
601 | $mform->setType('gpr_plugin', PARAM_SAFEDIR); |
602 | } |
97033c86 |
603 | |
3af29899 |
604 | if (!empty($this->courseid)) { |
605 | $mform->addElement('hidden', 'gpr_courseid', $this->courseid); |
606 | $mform->setType('gpr_courseid', PARAM_INT); |
cbff94ba |
607 | } |
cbff94ba |
608 | |
3af29899 |
609 | if (!empty($this->userid)) { |
610 | $mform->addElement('hidden', 'gpr_userid', $this->userid); |
611 | $mform->setType('gpr_userid', PARAM_INT); |
cbff94ba |
612 | } |
cbff94ba |
613 | |
3af29899 |
614 | if (!empty($this->page)) { |
615 | $mform->addElement('hidden', 'gpr_page', $this->page); |
616 | $mform->setType('gpr_page', PARAM_INT); |
cbff94ba |
617 | } |
618 | } |
281ffa4a |
619 | |
0610812a |
620 | /** |
621 | * Add return tracking params into url |
622 | * @param string $url |
623 | * @return string $url with erturn tracking params |
624 | */ |
3af29899 |
625 | function add_url_params($url) { |
7a6b7acf |
626 | if (empty($this->type)) { |
3af29899 |
627 | return $url; |
cbff94ba |
628 | } |
5609f9e6 |
629 | |
3af29899 |
630 | if (strpos($url, '?') === false) { |
631 | $url .= '?gpr_type='.$this->type; |
632 | } else { |
633 | $url .= '&gpr_type='.$this->type; |
cbff94ba |
634 | } |
cbff94ba |
635 | |
7a6b7acf |
636 | if (!empty($this->plugin)) { |
637 | $url .= '&gpr_plugin='.$this->plugin; |
638 | } |
cbff94ba |
639 | |
3af29899 |
640 | if (!empty($this->courseid)) { |
641 | $url .= '&gpr_courseid='.$this->courseid; |
cbff94ba |
642 | } |
cbff94ba |
643 | |
3af29899 |
644 | if (!empty($this->userid)) { |
645 | $url .= '&gpr_userid='.$this->userid; |
cbff94ba |
646 | } |
0a8a95c9 |
647 | |
3af29899 |
648 | if (!empty($this->page)) { |
649 | $url .= '&gpr_page='.$this->page; |
0a8a95c9 |
650 | } |
5a412dbf |
651 | |
3af29899 |
652 | return $url; |
5a412dbf |
653 | } |
5a412dbf |
654 | } |
7a6b7acf |
655 | |
826c5f86 |
656 | /** |
657 | * Function central to gradebook for building and printing the navigation (breadcrumb trail). |
658 | * @param string $path The path of the calling script (using __FILE__?) |
659 | * @param string $pagename The language string to use as the last part of the navigation (non-link) |
660 | * @param mixed $id Either a plain integer (assuming the key is 'id') or an array of keys and values (e.g courseid => $courseid, itemid...) |
661 | * @return string |
662 | */ |
663 | function grade_build_nav($path, $pagename=null, $id=null) { |
664 | global $CFG, $COURSE; |
665 | |
666 | $strgrades = get_string('grades', 'grades'); |
667 | |
668 | // Parse the path and build navlinks from its elements |
669 | $dirroot_length = strlen($CFG->dirroot) + 1; // Add 1 for the first slash |
670 | $path = substr($path, $dirroot_length); |
671 | $path = str_replace('\\', '/', $path); |
672 | |
673 | $path_elements = explode('/', $path); |
674 | |
675 | $path_elements_count = count($path_elements); |
676 | |
826c5f86 |
677 | // First link is always 'grade' |
678 | $navlinks = array(); |
679 | $navlinks[] = array('name' => $strgrades, |
680 | 'link' => $CFG->wwwroot.'/grade/index.php?id='.$COURSE->id, |
681 | 'type' => 'misc'); |
682 | |
683 | $link = ''; |
684 | $numberofelements = 3; |
685 | |
686 | // Prepare URL params string |
687 | $id_string = '?'; |
688 | if (!is_null($id)) { |
689 | if (is_array($id)) { |
690 | foreach ($id as $idkey => $idvalue) { |
691 | $id_string .= "$idkey=$idvalue&"; |
692 | } |
693 | } else { |
694 | $id_string .= "id=$id"; |
695 | } |
696 | } |
697 | |
698 | $navlink4 = null; |
699 | |
0f78c4de |
700 | // Remove file extensions from filenames |
701 | foreach ($path_elements as $key => $filename) { |
702 | $path_elements[$key] = str_replace('.php', '', $filename); |
703 | } |
704 | |
826c5f86 |
705 | // Second level links |
706 | switch ($path_elements[1]) { |
707 | case 'edit': // No link |
708 | if ($path_elements[3] != 'index.php') { |
709 | $numberofelements = 4; |
710 | } |
711 | break; |
712 | case 'import': // No link |
713 | break; |
714 | case 'export': // No link |
715 | break; |
716 | case 'report': |
717 | // $id is required for this link. Do not print it if $id isn't given |
718 | if (!is_null($id)) { |
719 | $link = $CFG->wwwroot . '/grade/report/index.php' . $id_string; |
720 | } |
721 | |
722 | if ($path_elements[2] == 'grader') { |
723 | $numberofelements = 4; |
724 | } |
725 | break; |
726 | |
727 | default: |
728 | // If this element isn't among the ones already listed above, it isn't supported, throw an error. |
729 | debugging("grade_build_nav() doesn't support ". $path_elements[1] . " as the second path element after 'grade'."); |
730 | return false; |
731 | } |
732 | |
733 | $navlinks[] = array('name' => get_string($path_elements[1], 'grades'), 'link' => $link, 'type' => 'misc'); |
734 | |
735 | // Third level links |
736 | if (empty($pagename)) { |
737 | $pagename = get_string($path_elements[2], 'grades'); |
738 | } |
739 | |
740 | switch ($numberofelements) { |
741 | case 3: |
742 | $navlinks[] = array('name' => $pagename, 'link' => $link, 'type' => 'misc'); |
743 | break; |
744 | case 4: |
745 | |
746 | if ($path_elements[2] == 'grader' AND $path_elements[3] != 'index.php') { |
3cf6a6d5 |
747 | $navlinks[] = array('name' => get_string('modulename', 'gradereport_grader'), |
826c5f86 |
748 | 'link' => "$CFG->wwwroot/grade/report/grader/index.php$id_string", |
749 | 'type' => 'misc'); |
750 | } |
751 | $navlinks[] = array('name' => $pagename, 'link' => '', 'type' => 'misc'); |
752 | break; |
753 | } |
754 | $navigation = build_navigation($navlinks); |
755 | |
756 | return $navigation; |
d4795a07 |
757 | } |
7a6b7acf |
758 | |
e98871a2 |
759 | /** |
6cc3e350 |
760 | * General structure representing grade items in course |
e98871a2 |
761 | */ |
6cc3e350 |
762 | class grade_structure { |
763 | var $context; |
e98871a2 |
764 | |
6cc3e350 |
765 | var $courseid; |
e98871a2 |
766 | |
767 | /** |
768 | * 1D array of grade items only |
769 | */ |
770 | var $items; |
771 | |
6391ebe7 |
772 | /** |
6cc3e350 |
773 | * Returns icon of element |
774 | * @param object $element |
775 | * @param bool $spacerifnone return spacer if no icon found |
776 | * @return string icon or spacer |
6391ebe7 |
777 | */ |
6cc3e350 |
778 | function get_element_icon(&$element, $spacerifnone=false) { |
779 | global $CFG; |
780 | |
781 | switch ($element['type']) { |
782 | case 'item': |
783 | case 'courseitem': |
784 | case 'categoryitem': |
785 | if ($element['object']->is_calculated()) { |
786 | return '<img src="'.$CFG->pixpath.'/i/calc.gif" class="icon itemicon" alt="'.get_string('calculation', 'grades').'"/>'; |
787 | |
788 | } else if (($element['object']->is_course_item() or $element['object']->is_category_item()) |
789 | and ($element['object']->gradetype == GRADE_TYPE_SCALE or $element['object']->gradetype == GRADE_TYPE_VALUE)) { |
790 | if ($category = $element['object']->get_item_category()) { |
791 | switch ($category->aggregation) { |
792 | case GRADE_AGGREGATE_MEAN: |
793 | case GRADE_AGGREGATE_MEDIAN: |
794 | case GRADE_AGGREGATE_WEIGHTED_MEAN: |
1426edac |
795 | case GRADE_AGGREGATE_WEIGHTED_MEAN2: |
6cc3e350 |
796 | case GRADE_AGGREGATE_EXTRACREDIT_MEAN: |
797 | return '<img src="'.$CFG->pixpath.'/i/agg_mean.gif" class="icon itemicon" alt="'.get_string('aggregation', 'grades').'"/>'; |
0758a08e |
798 | case GRADE_AGGREGATE_SUM: |
799 | return '<img src="'.$CFG->pixpath.'/i/agg_sum.gif" class="icon itemicon" alt="'.get_string('aggregation', 'grades').'"/>'; |
6cc3e350 |
800 | } |
801 | } |
802 | |
803 | } else if ($element['object']->itemtype == 'mod') { |
804 | return '<img src="'.$CFG->modpixpath.'/'.$element['object']->itemmodule.'/icon.gif" class="icon itemicon" alt="' |
805 | .get_string('modulename', $element['object']->itemmodule).'"/>'; |
806 | |
807 | } else if ($element['object']->itemtype == 'manual') { |
808 | if ($element['object']->is_outcome_item()) { |
809 | return '<img src="'.$CFG->pixpath.'/i/outcomes.gif" class="icon itemicon" alt="'.get_string('outcome', 'grades').'"/>'; |
810 | } else { |
a1f21902 |
811 | return '<img src="'.$CFG->pixpath.'/t/manual_item.gif" class="icon itemicon" alt="'.get_string('manualitem', 'grades').'"/>'; |
6cc3e350 |
812 | } |
813 | } |
814 | break; |
815 | |
816 | case 'category': |
817 | return '<img src="'.$CFG->pixpath.'/f/folder.gif" class="icon itemicon" alt="'.get_string('category', 'grades').'"/>'; |
818 | } |
819 | |
820 | if ($spacerifnone) { |
821 | return '<img src="'.$CFG->wwwroot.'/pix/spacer.gif" class="icon itemicon" alt=""/>'; |
822 | } else { |
823 | return ''; |
824 | } |
825 | } |
6391ebe7 |
826 | |
e98871a2 |
827 | /** |
6cc3e350 |
828 | * Returns name of element optionally with icon and link |
829 | * @param object $element |
830 | * @param bool $withlinks |
831 | * @param bool $icons |
832 | * @param bool $spacerifnone return spacer if no icon found |
833 | * @return header string |
e98871a2 |
834 | */ |
6cc3e350 |
835 | function get_element_header(&$element, $withlink=false, $icon=true, $spacerifnone=false) { |
836 | global $CFG; |
837 | |
838 | $header = ''; |
839 | |
840 | if ($icon) { |
841 | $header .= $this->get_element_icon($element, $spacerifnone); |
842 | } |
843 | |
844 | $header .= $element['object']->get_name(); |
845 | |
846 | if ($element['type'] != 'item' and $element['type'] != 'categoryitem' and $element['type'] != 'courseitem') { |
847 | return $header; |
848 | } |
849 | |
850 | $itemtype = $element['object']->itemtype; |
851 | $itemmodule = $element['object']->itemmodule; |
852 | $iteminstance = $element['object']->iteminstance; |
853 | |
854 | if ($withlink and $itemtype=='mod' and $iteminstance and $itemmodule) { |
46d1af82 |
855 | if ($cm = get_coursemodule_from_instance($itemmodule, $iteminstance, $this->courseid)) { |
6cc3e350 |
856 | |
46d1af82 |
857 | $dir = $CFG->dirroot.'/mod/'.$itemmodule; |
6cc3e350 |
858 | |
46d1af82 |
859 | if (file_exists($dir.'/grade.php')) { |
860 | $url = $CFG->wwwroot.'/mod/'.$itemmodule.'/grade.php?id='.$cm->id; |
861 | } else { |
862 | $url = $CFG->wwwroot.'/mod/'.$itemmodule.'/view.php?id='.$cm->id; |
863 | } |
6cc3e350 |
864 | |
46d1af82 |
865 | $header = '<a href="'.$url.'">'.$header.'</a>'; |
866 | } |
6cc3e350 |
867 | } |
868 | |
869 | return $header; |
870 | } |
871 | |
872 | /** |
873 | * Returns the grade eid - the grade may not exist yet. |
874 | * @param $grade_grade object |
875 | * @return string eid |
876 | */ |
877 | function get_grade_eid($grade_grade) { |
878 | if (empty($grade_grade->id)) { |
879 | return 'n'.$grade_grade->itemid.'u'.$grade_grade->userid; |
880 | } else { |
881 | return 'g'.$grade_grade->id; |
882 | } |
883 | } |
884 | |
885 | /** |
886 | * Returns the grade_item eid |
887 | * @param $grade_item object |
888 | * @return string eid |
889 | */ |
890 | function get_item_eid($grade_item) { |
891 | return 'i'.$grade_item->id; |
892 | } |
893 | |
9ecd4386 |
894 | function get_params_for_iconstr($element) { |
895 | $strparams = new stdClass(); |
896 | $strparams->category = ''; |
897 | $strparams->itemname = ''; |
898 | $strparams->itemmodule = ''; |
899 | if (!method_exists($element['object'], 'get_name')) { |
900 | return $strparams; |
901 | } |
345674ca |
902 | |
9ecd4386 |
903 | $strparams->itemname = $element['object']->get_name(); |
904 | |
905 | // If element name is categorytotal, get the name of the parent category |
906 | if ($strparams->itemname == get_string('categorytotal', 'grades')) { |
907 | $parent = $element['object']->get_parent_category(); |
908 | $strparams->category = $parent->get_name() . ' '; |
909 | } else { |
910 | $strparams->category = ''; |
911 | } |
912 | |
913 | $strparams->itemmodule = null; |
914 | if (isset($element['object']->itemmodule)) { |
915 | $strparams->itemmodule = $element['object']->itemmodule; |
345674ca |
916 | } |
9ecd4386 |
917 | return $strparams; |
918 | } |
919 | |
6cc3e350 |
920 | /** |
921 | * Return edit icon for give element |
922 | * @param object $element |
923 | * @return string |
924 | */ |
925 | function get_edit_icon($element, $gpr) { |
926 | global $CFG; |
927 | |
928 | if (!has_capability('moodle/grade:manage', $this->context)) { |
929 | if ($element['type'] == 'grade' and has_capability('moodle/grade:edit', $this->context)) { |
930 | // oki - let them override grade |
931 | } else { |
932 | return ''; |
933 | } |
934 | } |
935 | |
05aba805 |
936 | static $strfeedback = null; |
937 | static $streditgrade = null; |
938 | if (is_null($streditgrade)) { |
939 | $streditgrade = get_string('editgrade', 'grades'); |
940 | $strfeedback = get_string('feedback'); |
6cc3e350 |
941 | } |
942 | |
9ecd4386 |
943 | $strparams = $this->get_params_for_iconstr($element); |
345674ca |
944 | if ($element['type'] == 'item' or $element['type'] == 'category') { |
9ecd4386 |
945 | } |
345674ca |
946 | |
6cc3e350 |
947 | $object = $element['object']; |
948 | $overlib = ''; |
949 | |
950 | switch ($element['type']) { |
951 | case 'item': |
952 | case 'categoryitem': |
953 | case 'courseitem': |
9ecd4386 |
954 | $stredit = get_string('editverbose', 'grades', $strparams); |
6cc3e350 |
955 | if (empty($object->outcomeid) || empty($CFG->enableoutcomes)) { |
956 | $url = $CFG->wwwroot.'/grade/edit/tree/item.php?courseid='.$this->courseid.'&id='.$object->id; |
957 | } else { |
958 | $url = $CFG->wwwroot.'/grade/edit/tree/outcomeitem.php?courseid='.$this->courseid.'&id='.$object->id; |
959 | } |
960 | $url = $gpr->add_url_params($url); |
961 | break; |
962 | |
963 | case 'category': |
9ecd4386 |
964 | $stredit = get_string('editverbose', 'grades', $strparams); |
6cc3e350 |
965 | $url = $CFG->wwwroot.'/grade/edit/tree/category.php?courseid='.$this->courseid.'&id='.$object->id; |
966 | $url = $gpr->add_url_params($url); |
967 | break; |
968 | |
969 | case 'grade': |
05aba805 |
970 | $stredit = $streditgrade; |
6cc3e350 |
971 | if (empty($object->id)) { |
972 | $url = $CFG->wwwroot.'/grade/edit/tree/grade.php?courseid='.$this->courseid.'&itemid='.$object->itemid.'&userid='.$object->userid; |
973 | } else { |
974 | $url = $CFG->wwwroot.'/grade/edit/tree/grade.php?courseid='.$this->courseid.'&id='.$object->id; |
975 | } |
976 | $url = $gpr->add_url_params($url); |
977 | if (!empty($object->feedback)) { |
978 | $feedback = addslashes_js(trim(format_string($object->feedback, $object->feedbackformat))); |
979 | $function = "return overlib('$feedback', BORDER, 0, FGCLASS, 'feedback', " |
980 | ."CAPTIONFONTCLASS, 'caption', CAPTION, '$strfeedback');"; |
981 | $overlib = 'onmouseover="'.s($function).'" onmouseout="return nd();"'; |
982 | } |
983 | break; |
984 | |
985 | default: |
986 | $url = null; |
987 | } |
988 | |
989 | if ($url) { |
b244b9b7 |
990 | return '<a href="'.$url.'" class="edit"><img '.$overlib.' src="'.$CFG->pixpath.'/t/edit.gif" class="iconsmall" alt="'.$stredit.'" title="'.$stredit.'"/></a>'; |
6cc3e350 |
991 | |
992 | } else { |
993 | return ''; |
994 | } |
995 | } |
996 | |
997 | /** |
998 | * Return hiding icon for give element |
999 | * @param object $element |
1000 | * @return string |
1001 | */ |
1002 | function get_hiding_icon($element, $gpr) { |
1003 | global $CFG; |
1004 | |
1005 | if (!has_capability('moodle/grade:manage', $this->context) and !has_capability('moodle/grade:hide', $this->context)) { |
1006 | return ''; |
1007 | } |
1008 | |
345674ca |
1009 | $strparams = $this->get_params_for_iconstr($element); |
9ecd4386 |
1010 | $strshow = get_string('showverbose', 'grades', $strparams); |
345674ca |
1011 | $strhide = get_string('hideverbose', 'grades', $strparams); |
6cc3e350 |
1012 | |
1013 | if ($element['object']->is_hidden()) { |
1014 | $icon = 'show'; |
1015 | $tooltip = $strshow; |
1016 | |
1017 | if ($element['type'] != 'category' and $element['object']->get_hidden() > 1) { // Change the icon and add a tooltip showing the date |
1018 | $icon = 'hiddenuntil'; |
1019 | $tooltip = get_string('hiddenuntildate', 'grades', userdate($element['object']->get_hidden())); |
1020 | } |
1021 | |
1022 | $url = $CFG->wwwroot.'/grade/edit/tree/action.php?id='.$this->courseid.'&action=show&sesskey='.sesskey() |
1023 | . '&eid='.$element['eid']; |
1024 | $url = $gpr->add_url_params($url); |
b244b9b7 |
1025 | $action = '<a href="'.$url.'" class="hide"><img alt="'.$strshow.'" src="'.$CFG->pixpath.'/t/'.$icon.'.gif" class="iconsmall" title="'.$tooltip.'"/></a>'; |
6cc3e350 |
1026 | |
1027 | } else { |
1028 | $url = $CFG->wwwroot.'/grade/edit/tree/action.php?id='.$this->courseid.'&action=hide&sesskey='.sesskey() |
1029 | . '&eid='.$element['eid']; |
1030 | $url = $gpr->add_url_params($url); |
b244b9b7 |
1031 | $action = '<a href="'.$url.'" class="hide"><img src="'.$CFG->pixpath.'/t/hide.gif" class="iconsmall" alt="'.$strhide.'" title="'.$strhide.'"/></a>'; |
6cc3e350 |
1032 | } |
1033 | return $action; |
1034 | } |
1035 | |
1036 | /** |
2673c733 |
1037 | * Return locking icon for given element |
6cc3e350 |
1038 | * @param object $element |
1039 | * @return string |
1040 | */ |
1041 | function get_locking_icon($element, $gpr) { |
1042 | global $CFG; |
1043 | |
345674ca |
1044 | $strparams = $this->get_params_for_iconstr($element); |
9ecd4386 |
1045 | $strunlock = get_string('unlockverbose', 'grades', $strparams); |
1046 | $strlock = get_string('lockverbose', 'grades', $strparams); |
2673c733 |
1047 | |
1048 | // Don't allow an unlocking action for a grade whose grade item is locked: just print a state icon |
1049 | if ($element['type'] == 'grade' && $element['object']->grade_item->is_locked()) { |
1050 | $strparamobj = new stdClass(); |
1051 | $strparamobj->itemname = $element['object']->grade_item->itemname; |
1052 | $strnonunlockable = get_string('nonunlockableverbose', 'grades', $strparamobj); |
1053 | $action = '<img src="'.$CFG->pixpath.'/t/unlock_gray.gif" alt="'.$strnonunlockable.'" class="iconsmall" title="'.$strnonunlockable.'"/>'; |
1054 | } elseif ($element['object']->is_locked()) { |
6cc3e350 |
1055 | $icon = 'unlock'; |
1056 | $tooltip = $strunlock; |
1057 | |
1058 | if ($element['type'] != 'category' and $element['object']->get_locktime() > 1) { // Change the icon and add a tooltip showing the date |
1059 | $icon = 'locktime'; |
1060 | $tooltip = get_string('locktimedate', 'grades', userdate($element['object']->get_locktime())); |
1061 | } |
1062 | |
1063 | if (!has_capability('moodle/grade:manage', $this->context) and !has_capability('moodle/grade:unlock', $this->context)) { |
1064 | return ''; |
1065 | } |
1066 | $url = $CFG->wwwroot.'/grade/edit/tree/action.php?id='.$this->courseid.'&action=unlock&sesskey='.sesskey() |
1067 | . '&eid='.$element['eid']; |
1068 | $url = $gpr->add_url_params($url); |
b244b9b7 |
1069 | $action = '<a href="'.$url.'" class="lock"><img src="'.$CFG->pixpath.'/t/'.$icon.'.gif" alt="'.$strunlock.'" class="iconsmall" title="'.$tooltip.'"/></a>'; |
6cc3e350 |
1070 | |
1071 | } else { |
1072 | if (!has_capability('moodle/grade:manage', $this->context) and !has_capability('moodle/grade:lock', $this->context)) { |
1073 | return ''; |
1074 | } |
1075 | $url = $CFG->wwwroot.'/grade/edit/tree/action.php?id='.$this->courseid.'&action=lock&sesskey='.sesskey() |
1076 | . '&eid='.$element['eid']; |
1077 | $url = $gpr->add_url_params($url); |
b244b9b7 |
1078 | $action = '<a href="'.$url.'" class="lock"><img src="'.$CFG->pixpath.'/t/lock.gif" class="iconsmall" alt="'.$strlock.'" title="' |
6cc3e350 |
1079 | . $strlock.'"/></a>'; |
1080 | } |
1081 | return $action; |
1082 | } |
1083 | |
1084 | /** |
1085 | * Return calculation icon for given element |
1086 | * @param object $element |
1087 | * @return string |
1088 | */ |
1089 | function get_calculation_icon($element, $gpr) { |
1090 | global $CFG; |
1091 | if (!has_capability('moodle/grade:manage', $this->context)) { |
1092 | return ''; |
1093 | } |
1094 | |
1095 | $calculation_icon = ''; |
1096 | |
1097 | $type = $element['type']; |
1098 | $object = $element['object']; |
1099 | |
9ecd4386 |
1100 | |
6cc3e350 |
1101 | if ($type == 'item' or $type == 'courseitem' or $type == 'categoryitem') { |
345674ca |
1102 | $strparams = $this->get_params_for_iconstr($element); |
9ecd4386 |
1103 | $streditcalculation = get_string('editcalculationverbose', 'grades', $strparams); |
6cc3e350 |
1104 | |
1105 | // show calculation icon only when calculation possible |
ff1bc603 |
1106 | if (!$object->is_external_item() and ($object->gradetype == GRADE_TYPE_SCALE or $object->gradetype == GRADE_TYPE_VALUE)) { |
6cc3e350 |
1107 | if ($object->is_calculated()) { |
1108 | $icon = 'calc.gif'; |
1109 | } else { |
1110 | $icon = 'calc_off.gif'; |
1111 | } |
1112 | $url = $CFG->wwwroot.'/grade/edit/tree/calculation.php?courseid='.$this->courseid.'&id='.$object->id; |
1113 | $url = $gpr->add_url_params($url); |
b244b9b7 |
1114 | $calculation_icon = '<a href="'. $url.'" class="calculation"><img src="'.$CFG->pixpath.'/t/'.$icon.'" class="iconsmall" alt="' |
6cc3e350 |
1115 | . $streditcalculation.'" title="'.$streditcalculation.'" /></a>'. "\n"; |
1116 | } |
1117 | } |
1118 | |
1119 | return $calculation_icon; |
1120 | } |
1121 | } |
1122 | |
1123 | /** |
1124 | * Flat structure similar to grade tree. |
1125 | */ |
1126 | class grade_seq extends grade_structure { |
1127 | |
1128 | /** |
1129 | * A string of GET URL variables, namely courseid and sesskey, used in most URLs built by this class. |
1130 | * @var string $commonvars |
1131 | */ |
1132 | var $commonvars; |
1133 | |
1134 | /** |
1135 | * 1D array of elements |
1136 | */ |
1137 | var $elements; |
e98871a2 |
1138 | |
1139 | /** |
1140 | * Constructor, retrieves and stores array of all grade_category and grade_item |
1141 | * objects for the given courseid. Full objects are instantiated. Ordering sequence is fixed if needed. |
1142 | * @param int $courseid |
1143 | * @param boolean $category_grade_last category grade item is the last child |
1144 | * @param array $collapsed array of collapsed categories |
1145 | */ |
1146 | function grade_seq($courseid, $category_grade_last=false, $nooutcomes=false) { |
1147 | global $USER, $CFG; |
1148 | |
1149 | $this->courseid = $courseid; |
1150 | $this->commonvars = "&sesskey=$USER->sesskey&id=$this->courseid"; |
1151 | $this->context = get_context_instance(CONTEXT_COURSE, $courseid); |
1152 | |
1153 | // get course grade tree |
1154 | $top_element = grade_category::fetch_course_tree($courseid, true); |
1155 | |
6391ebe7 |
1156 | $this->elements = grade_seq::flatten($top_element, $category_grade_last, $nooutcomes); |
1157 | |
1158 | foreach ($this->elements as $key=>$unused) { |
b89a70ce |
1159 | $this->items[$this->elements[$key]['object']->id] =& $this->elements[$key]['object']; |
6391ebe7 |
1160 | } |
e98871a2 |
1161 | } |
1162 | |
1163 | /** |
1164 | * Static recursive helper - makes the grade_item for category the last children |
1165 | * @static |
1166 | * @param array $element The seed of the recursion |
1167 | * @return void |
1168 | */ |
1169 | function flatten(&$element, $category_grade_last, $nooutcomes) { |
1170 | if (empty($element['children'])) { |
1171 | return array(); |
1172 | } |
1173 | $children = array(); |
1174 | |
1175 | foreach ($element['children'] as $sortorder=>$unused) { |
1176 | if ($nooutcomes and $element['type'] != 'category' and $element['children'][$sortorder]['object']->is_outcome_item()) { |
1177 | continue; |
1178 | } |
1179 | $children[] = $element['children'][$sortorder]; |
1180 | } |
1181 | unset($element['children']); |
1182 | |
1183 | if ($category_grade_last and count($children) > 1) { |
1184 | $cat_item = array_shift($children); |
1185 | array_push($children, $cat_item); |
1186 | } |
1187 | |
1188 | $result = array(); |
1189 | foreach ($children as $child) { |
e0724506 |
1190 | if ($child['type'] == 'category') { |
6391ebe7 |
1191 | $result = $result + grade_seq::flatten($child, $category_grade_last, $nooutcomes); |
e98871a2 |
1192 | } else { |
1193 | $child['eid'] = 'i'.$child['object']->id; |
6391ebe7 |
1194 | $result[$child['object']->id] = $child; |
e98871a2 |
1195 | } |
1196 | } |
1197 | |
1198 | return $result; |
1199 | } |
1200 | |
1201 | /** |
1202 | * Parses the array in search of a given eid and returns a element object with |
1203 | * information about the element it has found. |
1204 | * @param int $eid |
1205 | * @return object element |
1206 | */ |
1207 | function locate_element($eid) { |
1208 | // it is a grade - construct a new object |
1209 | if (strpos($eid, 'n') === 0) { |
1210 | if (!preg_match('/n(\d+)u(\d+)/', $eid, $matches)) { |
1211 | return null; |
1212 | } |
1213 | |
1214 | $itemid = $matches[1]; |
1215 | $userid = $matches[2]; |
1216 | |
1217 | //extra security check - the grade item must be in this tree |
1218 | if (!$item_el = $this->locate_element('i'.$itemid)) { |
1219 | return null; |
1220 | } |
1221 | |
1222 | // $gradea->id may be null - means does not exist yet |
1223 | $grade = new grade_grade(array('itemid'=>$itemid, 'userid'=>$userid)); |
1224 | |
1225 | $grade->grade_item =& $item_el['object']; // this may speedup grade_grade methods! |
1226 | return array('eid'=>'n'.$itemid.'u'.$userid,'object'=>$grade, 'type'=>'grade'); |
1227 | |
1228 | } else if (strpos($eid, 'g') === 0) { |
1229 | $id = (int)substr($eid, 1); |
1230 | if (!$grade = grade_grade::fetch(array('id'=>$id))) { |
1231 | return null; |
1232 | } |
1233 | //extra security check - the grade item must be in this tree |
1234 | if (!$item_el = $this->locate_element('i'.$grade->itemid)) { |
1235 | return null; |
1236 | } |
1237 | $grade->grade_item =& $item_el['object']; // this may speedup grade_grade methods! |
1238 | return array('eid'=>'g'.$id,'object'=>$grade, 'type'=>'grade'); |
1239 | } |
1240 | |
1241 | // it is a category or item |
6391ebe7 |
1242 | foreach ($this->elements as $element) { |
6cc3e350 |
1243 | if ($element['eid'] == $eid) { |
1244 | return $element; |
1245 | } |
e98871a2 |
1246 | } |
6cc3e350 |
1247 | |
1248 | return null; |
e98871a2 |
1249 | } |
e98871a2 |
1250 | } |
1251 | |
7a6b7acf |
1252 | /** |
1253 | * This class represents a complete tree of categories, grade_items and final grades, |
1254 | * organises as an array primarily, but which can also be converted to other formats. |
1255 | * It has simple method calls with complex implementations, allowing for easy insertion, |
1256 | * deletion and moving of items and categories within the tree. |
1257 | */ |
6cc3e350 |
1258 | class grade_tree extends grade_structure { |
7a6b7acf |
1259 | |
1260 | /** |
1261 | * The basic representation of the tree as a hierarchical, 3-tiered array. |
1262 | * @var object $top_element |
1263 | */ |
1264 | var $top_element; |
1265 | |
1266 | /** |
1267 | * A string of GET URL variables, namely courseid and sesskey, used in most URLs built by this class. |
1268 | * @var string $commonvars |
1269 | */ |
1270 | var $commonvars; |
1271 | |
1272 | /** |
1273 | * 2D array of grade items and categories |
1274 | */ |
1275 | var $levels; |
1276 | |
b89a70ce |
1277 | /** |
1278 | * Grade items |
1279 | */ |
1280 | var $items; |
1281 | |
7a6b7acf |
1282 | /** |
1283 | * Constructor, retrieves and stores a hierarchical array of all grade_category and grade_item |
e98871a2 |
1284 | * objects for the given courseid. Full objects are instantiated. Ordering sequence is fixed if needed. |
7a6b7acf |
1285 | * @param int $courseid |
1286 | * @param boolean $fillers include fillers and colspans, make the levels var "rectangular" |
1287 | * @param boolean $category_grade_last category grade item is the last child |
4faf5f99 |
1288 | * @param array $collapsed array of collapsed categories |
7a6b7acf |
1289 | */ |
aea4df41 |
1290 | function grade_tree($courseid, $fillers=true, $category_grade_last=false, $collapsed=null, $nooutcomes=false) { |
7a6b7acf |
1291 | global $USER, $CFG; |
1292 | |
1293 | $this->courseid = $courseid; |
1294 | $this->commonvars = "&sesskey=$USER->sesskey&id=$this->courseid"; |
1295 | $this->levels = array(); |
2cc773f5 |
1296 | $this->context = get_context_instance(CONTEXT_COURSE, $courseid); |
7a6b7acf |
1297 | |
1298 | // get course grade tree |
1299 | $this->top_element = grade_category::fetch_course_tree($courseid, true); |
1300 | |
4faf5f99 |
1301 | // collapse the categories if requested |
1302 | if (!empty($collapsed)) { |
1303 | grade_tree::category_collapse($this->top_element, $collapsed); |
1304 | } |
1305 | |
aea4df41 |
1306 | // no otucomes if requested |
1307 | if (!empty($nooutcomes)) { |
1308 | grade_tree::no_outcomes($this->top_element); |
1309 | } |
1310 | |
4faf5f99 |
1311 | // move category item to last position in category |
7a6b7acf |
1312 | if ($category_grade_last) { |
1313 | grade_tree::category_grade_last($this->top_element); |
1314 | } |
1315 | |
1316 | if ($fillers) { |
1317 | // inject fake categories == fillers |
1318 | grade_tree::inject_fillers($this->top_element, 0); |
1319 | // add colspans to categories and fillers |
1320 | grade_tree::inject_colspans($this->top_element); |
1321 | } |
1322 | |
1323 | grade_tree::fill_levels($this->levels, $this->top_element, 0); |
d297269d |
1324 | |
7a6b7acf |
1325 | } |
1326 | |
4faf5f99 |
1327 | /** |
1328 | * Static recursive helper - removes items from collapsed categories |
1329 | * @static |
1330 | * @param array $element The seed of the recursion |
1331 | * @param array $collapsed array of collapsed categories |
1332 | * @return void |
1333 | */ |
1334 | function category_collapse(&$element, $collapsed) { |
1335 | if ($element['type'] != 'category') { |
1336 | return; |
1337 | } |
1338 | if (empty($element['children']) or count($element['children']) < 2) { |
1339 | return; |
1340 | } |
1341 | |
384960dd |
1342 | if (in_array($element['object']->id, $collapsed['aggregatesonly'])) { |
4faf5f99 |
1343 | $category_item = reset($element['children']); //keep only category item |
1344 | $element['children'] = array(key($element['children'])=>$category_item); |
1345 | |
1346 | } else { |
384960dd |
1347 | if (in_array($element['object']->id, $collapsed['gradesonly'])) { // Remove category item |
1348 | reset($element['children']); |
1349 | $first_key = key($element['children']); |
1350 | unset($element['children'][$first_key]); |
1351 | } |
1352 | foreach ($element['children'] as $sortorder=>$child) { // Recurse through the element's children |
4faf5f99 |
1353 | grade_tree::category_collapse($element['children'][$sortorder], $collapsed); |
1354 | } |
1355 | } |
1356 | } |
7a6b7acf |
1357 | |
aea4df41 |
1358 | /** |
1359 | * Static recursive helper - removes all outcomes |
1360 | * @static |
1361 | * @param array $element The seed of the recursion |
1362 | * @return void |
1363 | */ |
1364 | function no_outcomes(&$element) { |
1365 | if ($element['type'] != 'category') { |
1366 | return; |
1367 | } |
1368 | foreach ($element['children'] as $sortorder=>$child) { |
1369 | if ($element['children'][$sortorder]['type'] == 'item' |
1370 | and $element['children'][$sortorder]['object']->is_outcome_item()) { |
1371 | unset($element['children'][$sortorder]); |
1372 | |
d4795a07 |
1373 | } else if ($element['children'][$sortorder]['type'] == 'category') { |
aea4df41 |
1374 | grade_tree::no_outcomes($element['children'][$sortorder]); |
1375 | } |
1376 | } |
1377 | } |
1378 | |
7a6b7acf |
1379 | /** |
1380 | * Static recursive helper - makes the grade_item for category the last children |
1381 | * @static |
1382 | * @param array $element The seed of the recursion |
1383 | * @return void |
1384 | */ |
1385 | function category_grade_last(&$element) { |
1386 | if (empty($element['children'])) { |
1387 | return; |
1388 | } |
1389 | if (count($element['children']) < 2) { |
1390 | return; |
1391 | } |
3e0e2436 |
1392 | $first_item = reset($element['children']); |
4a3dfd9a |
1393 | if ($first_item['type'] == 'categoryitem' or $first_item['type'] == 'courseitem') { |
3e0e2436 |
1394 | // the category item might have been already removed |
1395 | $order = key($element['children']); |
1396 | unset($element['children'][$order]); |
1397 | $element['children'][$order] =& $first_item; |
1398 | } |
206f9953 |
1399 | foreach ($element['children'] as $sortorder => $child) { |
7a6b7acf |
1400 | grade_tree::category_grade_last($element['children'][$sortorder]); |
1401 | } |
1402 | } |
1403 | |
1404 | /** |
1405 | * Static recursive helper - fills the levels array, useful when accessing tree elements of one level |
1406 | * @static |
1407 | * @param int $levels |
1408 | * @param array $element The seed of the recursion |
1409 | * @param int $depth |
1410 | * @return void |
1411 | */ |
1412 | function fill_levels(&$levels, &$element, $depth) { |
1413 | if (!array_key_exists($depth, $levels)) { |
1414 | $levels[$depth] = array(); |
1415 | } |
1416 | |
1417 | // prepare unique identifier |
1418 | if ($element['type'] == 'category') { |
1419 | $element['eid'] = 'c'.$element['object']->id; |
1420 | } else if (in_array($element['type'], array('item', 'courseitem', 'categoryitem'))) { |
1421 | $element['eid'] = 'i'.$element['object']->id; |
b89a70ce |
1422 | $this->items[$element['object']->id] =& $element['object']; |
7a6b7acf |
1423 | } |
1424 | |
1425 | $levels[$depth][] =& $element; |
1426 | $depth++; |
1427 | if (empty($element['children'])) { |
1428 | return; |
1429 | } |
1430 | $prev = 0; |
1431 | foreach ($element['children'] as $sortorder=>$child) { |
1432 | grade_tree::fill_levels($levels, $element['children'][$sortorder], $depth); |
1433 | $element['children'][$sortorder]['prev'] = $prev; |
1434 | $element['children'][$sortorder]['next'] = 0; |
1435 | if ($prev) { |
1436 | $element['children'][$prev]['next'] = $sortorder; |
1437 | } |
1438 | $prev = $sortorder; |
1439 | } |
1440 | } |
1441 | |
1442 | /** |
1443 | * Static recursive helper - makes full tree (all leafes are at the same level) |
1444 | */ |
1445 | function inject_fillers(&$element, $depth) { |
1446 | $depth++; |
1447 | |
1448 | if (empty($element['children'])) { |
1449 | return $depth; |
1450 | } |
1451 | $chdepths = array(); |
1452 | $chids = array_keys($element['children']); |
1453 | $last_child = end($chids); |
1454 | $first_child = reset($chids); |
1455 | |
1456 | foreach ($chids as $chid) { |
1457 | $chdepths[$chid] = grade_tree::inject_fillers($element['children'][$chid], $depth); |
1458 | } |
1459 | arsort($chdepths); |
1460 | |
1461 | $maxdepth = reset($chdepths); |
1462 | foreach ($chdepths as $chid=>$chd) { |
1463 | if ($chd == $maxdepth) { |
1464 | continue; |
1465 | } |
1466 | for ($i=0; $i < $maxdepth-$chd; $i++) { |
1467 | if ($chid == $first_child) { |
1468 | $type = 'fillerfirst'; |
1469 | } else if ($chid == $last_child) { |
1470 | $type = 'fillerlast'; |
1471 | } else { |
1472 | $type = 'filler'; |
1473 | } |
1474 | $oldchild =& $element['children'][$chid]; |
1475 | $element['children'][$chid] = array('object'=>'filler', 'type'=>$type, 'eid'=>'', 'depth'=>$element['object']->depth,'children'=>array($oldchild)); |
1476 | } |
1477 | } |
1478 | |
1479 | return $maxdepth; |
1480 | } |
1481 | |
1482 | /** |
1483 | * Static recursive helper - add colspan information into categories |
1484 | */ |
1485 | function inject_colspans(&$element) { |
1486 | if (empty($element['children'])) { |
1487 | return 1; |
1488 | } |
1489 | $count = 0; |
1490 | foreach ($element['children'] as $key=>$child) { |
1491 | $count += grade_tree::inject_colspans($element['children'][$key]); |
1492 | } |
1493 | $element['colspan'] = $count; |
1494 | return $count; |
1495 | } |
1496 | |
1497 | /** |
1498 | * Parses the array in search of a given eid and returns a element object with |
1499 | * information about the element it has found. |
1500 | * @param int $eid |
1501 | * @return object element |
1502 | */ |
1503 | function locate_element($eid) { |
d3c3da1b |
1504 | // it is a grade - construct a new object |
1505 | if (strpos($eid, 'n') === 0) { |
1506 | if (!preg_match('/n(\d+)u(\d+)/', $eid, $matches)) { |
1507 | return null; |
1508 | } |
1509 | |
1510 | $itemid = $matches[1]; |
1511 | $userid = $matches[2]; |
1512 | |
1513 | //extra security check - the grade item must be in this tree |
1514 | if (!$item_el = $this->locate_element('i'.$itemid)) { |
1515 | return null; |
1516 | } |
1517 | |
1518 | // $gradea->id may be null - means does not exist yet |
1519 | $grade = new grade_grade(array('itemid'=>$itemid, 'userid'=>$userid)); |
1520 | |
1521 | $grade->grade_item =& $item_el['object']; // this may speedup grade_grade methods! |
1522 | return array('eid'=>'n'.$itemid.'u'.$userid,'object'=>$grade, 'type'=>'grade'); |
1523 | |
1524 | } else if (strpos($eid, 'g') === 0) { |
7a6b7acf |
1525 | $id = (int)substr($eid, 1); |
1526 | if (!$grade = grade_grade::fetch(array('id'=>$id))) { |
1527 | return null; |
1528 | } |
1529 | //extra security check - the grade item must be in this tree |
1530 | if (!$item_el = $this->locate_element('i'.$grade->itemid)) { |
1531 | return null; |
1532 | } |
1533 | $grade->grade_item =& $item_el['object']; // this may speedup grade_grade methods! |
1534 | return array('eid'=>'g'.$id,'object'=>$grade, 'type'=>'grade'); |
1535 | } |
1536 | |
1537 | // it is a category or item |
1538 | foreach ($this->levels as $row) { |
1539 | foreach ($row as $element) { |
1540 | if ($element['type'] == 'filler') { |
1541 | continue; |
1542 | } |
1543 | if ($element['eid'] == $eid) { |
1544 | return $element; |
1545 | } |
1546 | } |
1547 | } |
1548 | |
1549 | return null; |
1550 | } |
b244b9b7 |
1551 | |
1552 | /** |
1553 | * Returns a well-formed XML representation of the grade-tree using recursion. |
1554 | * @param array $root The current element in the recursion. If null, starts at the top of the tree. |
1555 | * @return string $xml |
1556 | */ |
1557 | function exportToXML($root=null, $tabs="\t") { |
1558 | $xml = null; |
1559 | $first = false; |
1560 | if (is_null($root)) { |
1561 | $root = $this->top_element; |
1562 | $xml = '<?xml version="1.0" encoding="UTF-8" ?>' . "\n"; |
1563 | $xml .= "<gradetree>\n"; |
1564 | $first = true; |
1565 | } |
1566 | |
1567 | $type = 'undefined'; |
1568 | if (strpos($root['object']->table, 'grade_categories') !== false) { |
1569 | $type = 'category'; |
1570 | } elseif (strpos($root['object']->table, 'grade_items') !== false) { |
1571 | $type = 'item'; |
1572 | } elseif (strpos($root['object']->table, 'grade_outcomes') !== false) { |
1573 | $type = 'outcome'; |
1574 | } |
1575 | |
1576 | $xml .= "$tabs<element type=\"$type\">\n"; |
1577 | foreach ($root['object'] as $var => $value) { |
1578 | if (!is_object($value) && !is_array($value) && !empty($value)) { |
1579 | $xml .= "$tabs\t<$var>$value</$var>\n"; |
1580 | } |
1581 | } |
1582 | |
1583 | if (!empty($root['children'])) { |
1584 | $xml .= "$tabs\t<children>\n"; |
1585 | foreach ($root['children'] as $sortorder => $child) { |
1586 | $xml .= $this->exportToXML($child, $tabs."\t\t"); |
1587 | } |
1588 | $xml .= "$tabs\t</children>\n"; |
1589 | } |
1590 | |
1591 | $xml .= "$tabs</element>\n"; |
1592 | |
1593 | if ($first) { |
1594 | $xml .= "</gradetree>"; |
1595 | } |
1596 | |
1597 | return $xml; |
1598 | } |
1599 | |
1600 | /** |
1601 | * Returns a JSON representation of the grade-tree using recursion. |
1602 | * @param array $root The current element in the recursion. If null, starts at the top of the tree. |
1603 | * @param string $tabs Tab characters used to indent the string nicely for humans to enjoy |
1604 | * @param int $switch The position (first or last) of the aggregations |
1605 | * @return string $xml |
1606 | */ |
1607 | function exportToJSON($root=null, $tabs="\t") { |
1608 | $json = null; |
1609 | $first = false; |
1610 | if (is_null($root)) { |
1611 | $root = $this->top_element; |
1612 | $first = true; |
1613 | } |
1614 | |
1615 | $name = ''; |
1616 | |
1617 | |
1618 | if (strpos($root['object']->table, 'grade_categories') !== false) { |
1619 | $name = $root['object']->fullname; |
1620 | if ($name == '?') { |
1621 | $name = $root['object']->get_name(); |
1622 | } |
1623 | } elseif (strpos($root['object']->table, 'grade_items') !== false) { |
1624 | $name = $root['object']->itemname; |
1625 | } elseif (strpos($root['object']->table, 'grade_outcomes') !== false) { |
1626 | $name = $root['object']->itemname; |
1627 | } |
1628 | |
1629 | $json .= "$tabs {\n"; |
1630 | $json .= "$tabs\t \"type\": \"{$root['type']}\",\n"; |
1631 | $json .= "$tabs\t \"name\": \"$name\",\n"; |
1632 | |
1633 | foreach ($root['object'] as $var => $value) { |
1634 | if (!is_object($value) && !is_array($value) && !empty($value)) { |
1635 | $json .= "$tabs\t \"$var\": \"$value\",\n"; |
1636 | } |
1637 | } |
1638 | |
1639 | $json = substr($json, 0, strrpos($json, ',')); |
1640 | |
1641 | if (!empty($root['children'])) { |
1642 | $json .= ",\n$tabs\t\"children\": [\n"; |
1643 | foreach ($root['children'] as $sortorder => $child) { |
1644 | $json .= $this->exportToJSON($child, $tabs."\t\t"); |
1645 | } |
1646 | $json = substr($json, 0, strrpos($json, ',')); |
1647 | $json .= "\n$tabs\t]\n"; |
1648 | } |
1649 | |
1650 | if ($first) { |
1651 | $json .= "\n}"; |
1652 | } else { |
1653 | $json .= "\n$tabs},\n"; |
1654 | } |
1655 | |
1656 | return $json; |
1657 | } |
7a6b7acf |
1658 | } |
1659 | |
e2008be2 |
1660 | ?> |