3 require_once $CFG->libdir
.'/gradelib.php';
6 * Print grading plugin selection popup form.
8 * @param int $courseid id of course
9 * @param string $active_type type of plugin on current page - import, export, report or edit
10 * @param string $active_plugin active plugin type - grader, user, cvs, ...
11 * @param boolean $return return as string
12 * @return nothing or string if $return true
14 function print_grade_plugin_selector($courseid, $active_type, $active_plugin, $return=false) {
17 $context = get_context_instance(CONTEXT_COURSE
, $courseid);
23 /// report plugins with its special structure
24 if ($reports = get_list_of_plugins('grade/report', 'CVS')) { // Get all installed reports
25 foreach ($reports as $key => $plugin) { // Remove ones we can't see
26 if (!has_capability('gradereport/'.$plugin.':view', $context)) {
27 unset($reports[$key]);
31 $reportnames = array();
32 if (!empty($reports)) {
33 foreach ($reports as $plugin) {
34 $url = 'report/'.$plugin.'/index.php?id='.$courseid;
35 if ($active_type == 'report' and $active_plugin == $plugin ) {
38 $reportnames[$url] = get_string('modulename', 'gradereport_'.$plugin, NULL, $CFG->dirroot
.'/grade/report/'.$plugin.'lang/');
42 if (!empty($reportnames)) {
43 $menu['reportgroup']='--'.get_string('reportplugins', 'grades');
44 $menu = $menu+
$reportnames;
47 /// standard import plugins
48 if ($imports = get_list_of_plugins('grade/import', 'CVS')) { // Get all installed import plugins
49 foreach ($imports as $key => $plugin) { // Remove ones we can't see
50 if (!has_capability('gradeimport/'.$plugin.':view', $context)) {
51 unset($imports[$key]);
55 $importnames = array();
56 if (!empty($imports)) {
57 foreach ($imports as $plugin) {
58 $url = 'import/'.$plugin.'/index.php?id='.$courseid;
59 if ($active_type == 'import' and $active_plugin == $plugin ) {
62 $importnames[$url] = get_string('modulename', 'gradeimport_'.$plugin, NULL, $CFG->dirroot
.'/grade/import/'.$plugin.'lang/');
66 if (!empty($importnames)) {
67 $menu['importgroup']='--'.get_string('importplugins', 'grades');
68 $menu = $menu+
$importnames;
71 /// standard export plugins
72 if ($exports = get_list_of_plugins('grade/export', 'CVS')) { // Get all installed export plugins
73 foreach ($exports as $key => $plugin) { // Remove ones we can't see
74 if (!has_capability('gradeexport/'.$plugin.':view', $context)) {
75 unset($exports[$key]);
79 $exportnames = array();
80 if (!empty($exports)) {
81 foreach ($exports as $plugin) {
82 $url = 'export/'.$plugin.'/index.php?id='.$courseid;
83 if ($active_type == 'export' and $active_plugin == $plugin ) {
86 $exportnames[$url] = get_string('modulename', 'gradeexport_'.$plugin, NULL, $CFG->dirroot
.'/grade/export/'.$plugin.'lang/');
90 if (!empty($exportnames)) {
91 $menu['exportgroup']='--'.get_string('exportplugins', 'grades');
92 $menu = $menu+
$exportnames;
95 /// editing scripts - not real plugins
96 if (has_capability('moodle/grade:manage', $context)) {
97 $menu['edit']='--'.get_string('edit');
98 $url = 'edit/tree.php?id='.$courseid;
99 if ($active_type == 'edit' and $active_plugin == 'tree' ) {
102 $menu[$url] = get_string('edittree', 'grades');
105 /// finally print/return the popup form
106 return popup_form($CFG->wwwroot
.'/grade/', $menu, 'choosepluginreport', $active, 'choose', '', '', $return, 'self', get_string('view'));
110 * Utility class used for return tracking when using edit and other forms in grade plugins
112 class grade_plugin_return
{
121 * @param array $params - associative array with return parameters, if null parameter are taken from _GET or _POST
123 function grade_plugin_return ($params=null) {
124 if (empty($params)) {
125 $this->type
= optional_param('gpr_type', null, PARAM_SAFEDIR
);
126 $this->plugin
= optional_param('gpr_plugin', null, PARAM_SAFEDIR
);
127 $this->courseid
= optional_param('gpr_courseid', null, PARAM_INT
);
128 $this->userid
= optional_param('gpr_userid', null, PARAM_INT
);
129 $this->page
= optional_param('gpr_page', null, PARAM_INT
);
132 foreach ($params as $key=>$value) {
133 if (array_key_exists($key, $this)) {
134 $this->$key = $value;
141 * Returns return parameters as options array suitable for buttons.
142 * @return array options
144 function get_options() {
145 if (empty($this->type
)) {
151 if (!empty($this->plugin
)) {
152 $params['plugin'] = $this->plugin
;
155 if (!empty($this->courseid
)) {
156 $params['id'] = $this->courseid
;
159 if (!empty($this->userid
)) {
160 $params['userid'] = $this->userid
;
163 if (!empty($this->page
)) {
164 $params['page'] = $this->page
;
172 * @param string $default default url when params not set
175 function get_return_url($default, $extras=null) {
178 if ($this->type
== 'edit') {
179 return $CFG->wwwroot
.'/grade/edit/tree.php?id='.$this->courseid
;
182 if (empty($this->type
) or empty($this->plugin
)) {
186 $url = $CFG->wwwroot
.'/grade/'.$this->type
.'/'.$this->plugin
.'/index.php';
189 if (!empty($this->courseid
)) {
190 $url .= $glue.'id='.$this->courseid
;
194 if (!empty($this->userid
)) {
195 $url .= $glue.'userid='.$this->userid
;
199 if (!empty($this->page
)) {
200 $url .= $glue.'page='.$this->page
;
204 if (!empty($extras)) {
205 foreach($extras as $key=>$value) {
206 $url .= $glue.$key.'='.$value;
215 * Returns string with hidden return tracking form elements.
218 function get_form_fields() {
219 if (empty($this->type
)) {
223 $result = '<input type="hidden" name="gpr_type" value="'.$this->type
.'" />';
225 if (!empty($this->plugin
)) {
226 $result .= '<input type="hidden" name="gpr_plugin" value="'.$this->plugin
.'" />';
229 if (!empty($this->courseid
)) {
230 $result .= '<input type="hidden" name="gpr_courseid" value="'.$this->courseid
.'" />';
233 if (!empty($this->userid
)) {
234 $result .= '<input type="hidden" name="gpr_userid" value="'.$this->userid
.'" />';
237 if (!empty($this->page
)) {
238 $result .= '<input type="hidden" name="gpr_page" value="'.$this->page
.'" />';
243 * Add hidden elements into mform
244 * @param object $mform moodle form object
247 function add_mform_elements(&$mform) {
248 if (empty($this->type
)) {
252 $mform->addElement('hidden', 'gpr_type', $this->type
);
253 $mform->setType('gpr_type', PARAM_SAFEDIR
);
255 if (!empty($this->plugin
)) {
256 $mform->addElement('hidden', 'gpr_plugin', $this->plugin
);
257 $mform->setType('gpr_plugin', PARAM_SAFEDIR
);
260 if (!empty($this->courseid
)) {
261 $mform->addElement('hidden', 'gpr_courseid', $this->courseid
);
262 $mform->setType('gpr_courseid', PARAM_INT
);
265 if (!empty($this->userid
)) {
266 $mform->addElement('hidden', 'gpr_userid', $this->userid
);
267 $mform->setType('gpr_userid', PARAM_INT
);
270 if (!empty($this->page
)) {
271 $mform->addElement('hidden', 'gpr_page', $this->page
);
272 $mform->setType('gpr_page', PARAM_INT
);
277 * Add return tracking params into url
279 * @return string $url with erturn tracking params
281 function add_url_params($url) {
282 if (empty($this->type
)) {
286 if (strpos($url, '?') === false) {
287 $url .= '?gpr_type='.$this->type
;
289 $url .= '&gpr_type='.$this->type
;
292 if (!empty($this->plugin
)) {
293 $url .= '&gpr_plugin='.$this->plugin
;
296 if (!empty($this->courseid
)) {
297 $url .= '&gpr_courseid='.$this->courseid
;
300 if (!empty($this->userid
)) {
301 $url .= '&gpr_userid='.$this->userid
;
304 if (!empty($this->page
)) {
305 $url .= '&gpr_page='.$this->page
;
314 * This class represents a complete tree of categories, grade_items and final grades,
315 * organises as an array primarily, but which can also be converted to other formats.
316 * It has simple method calls with complex implementations, allowing for easy insertion,
317 * deletion and moving of items and categories within the tree.
322 * The basic representation of the tree as a hierarchical, 3-tiered array.
323 * @var object $top_element
328 * A string of GET URL variables, namely courseid and sesskey, used in most URLs built by this class.
329 * @var string $commonvars
334 * 2D array of grade items and categories
344 * Constructor, retrieves and stores a hierarchical array of all grade_category and grade_item
345 * objects for the given courseid. Full objects are instantiated.
347 * @param int $courseid
348 * @param boolean $fillers include fillers and colspans, make the levels var "rectangular"
349 * @param boolean $category_grade_last category grade item is the last child
350 * @param array $collapsed array of collapsed categories
352 function grade_tree($courseid, $fillers=true, $category_grade_last=false, $collapsed=null) {
355 $this->courseid
= $courseid;
356 $this->commonvars
= "&sesskey=$USER->sesskey&id=$this->courseid";
357 $this->levels
= array();
358 $this->context
= get_context_instance(CONTEXT_COURSE
, $courseid);
360 // get course grade tree
361 $this->top_element
= grade_category
::fetch_course_tree($courseid, true);
363 // collapse the categories if requested
364 if (!empty($collapsed)) {
365 grade_tree
::category_collapse($this->top_element
, $collapsed);
368 // move category item to last position in category
369 if ($category_grade_last) {
370 grade_tree
::category_grade_last($this->top_element
);
374 // inject fake categories == fillers
375 grade_tree
::inject_fillers($this->top_element
, 0);
376 // add colspans to categories and fillers
377 grade_tree
::inject_colspans($this->top_element
);
380 grade_tree
::fill_levels($this->levels
, $this->top_element
, 0);
384 * Static recursive helper - removes items from collapsed categories
386 * @param array $element The seed of the recursion
387 * @param array $collapsed array of collapsed categories
390 function category_collapse(&$element, $collapsed) {
391 if ($element['type'] != 'category') {
394 if (empty($element['children']) or count($element['children']) < 2) {
398 if (in_array($element['object']->id
, $collapsed)) {
399 $category_item = reset($element['children']); //keep only category item
400 $element['children'] = array(key($element['children'])=>$category_item);
403 foreach ($element['children'] as $sortorder=>$child) {
404 grade_tree
::category_collapse($element['children'][$sortorder], $collapsed);
410 * Static recursive helper - makes the grade_item for category the last children
412 * @param array $element The seed of the recursion
415 function category_grade_last(&$element) {
416 if (empty($element['children'])) {
419 if (count($element['children']) < 2) {
422 $category_item = reset($element['children']);
423 $order = key($element['children']);
424 unset($element['children'][$order]);
425 $element['children'][$order] =& $category_item;
426 foreach ($element['children'] as $sortorder=>$child) {
427 grade_tree
::category_grade_last($element['children'][$sortorder]);
432 * Static recursive helper - fills the levels array, useful when accessing tree elements of one level
435 * @param array $element The seed of the recursion
439 function fill_levels(&$levels, &$element, $depth) {
440 if (!array_key_exists($depth, $levels)) {
441 $levels[$depth] = array();
444 // prepare unique identifier
445 if ($element['type'] == 'category') {
446 $element['eid'] = 'c'.$element['object']->id
;
447 } else if (in_array($element['type'], array('item', 'courseitem', 'categoryitem'))) {
448 $element['eid'] = 'i'.$element['object']->id
;
451 $levels[$depth][] =& $element;
453 if (empty($element['children'])) {
457 foreach ($element['children'] as $sortorder=>$child) {
458 grade_tree
::fill_levels($levels, $element['children'][$sortorder], $depth);
459 $element['children'][$sortorder]['prev'] = $prev;
460 $element['children'][$sortorder]['next'] = 0;
462 $element['children'][$prev]['next'] = $sortorder;
469 * Static recursive helper - makes full tree (all leafes are at the same level)
471 function inject_fillers(&$element, $depth) {
474 if (empty($element['children'])) {
478 $chids = array_keys($element['children']);
479 $last_child = end($chids);
480 $first_child = reset($chids);
482 foreach ($chids as $chid) {
483 $chdepths[$chid] = grade_tree
::inject_fillers($element['children'][$chid], $depth);
487 $maxdepth = reset($chdepths);
488 foreach ($chdepths as $chid=>$chd) {
489 if ($chd == $maxdepth) {
492 for ($i=0; $i < $maxdepth-$chd; $i++
) {
493 if ($chid == $first_child) {
494 $type = 'fillerfirst';
495 } else if ($chid == $last_child) {
496 $type = 'fillerlast';
500 $oldchild =& $element['children'][$chid];
501 $element['children'][$chid] = array('object'=>'filler', 'type'=>$type, 'eid'=>'', 'depth'=>$element['object']->depth
,'children'=>array($oldchild));
509 * Static recursive helper - add colspan information into categories
511 function inject_colspans(&$element) {
512 if (empty($element['children'])) {
516 foreach ($element['children'] as $key=>$child) {
517 $count +
= grade_tree
::inject_colspans($element['children'][$key]);
519 $element['colspan'] = $count;
524 * Parses the array in search of a given eid and returns a element object with
525 * information about the element it has found.
527 * @return object element
529 function locate_element($eid) {
530 if (strpos($eid, 'g') === 0) {
531 // it is a grade construct a new object
532 $id = (int)substr($eid, 1);
533 if (!$grade = grade_grade
::fetch(array('id'=>$id))) {
536 //extra security check - the grade item must be in this tree
537 if (!$item_el = $this->locate_element('i'.$grade->itemid
)) {
540 $grade->grade_item
=& $item_el['object']; // this may speedup grade_grade methods!
541 return array('eid'=>'g'.$id,'object'=>$grade, 'type'=>'grade');
544 // it is a category or item
545 foreach ($this->levels
as $row) {
546 foreach ($row as $element) {
547 if ($element['type'] == 'filler') {
550 if ($element['eid'] == $eid) {
560 * Return edit icon for give element
561 * @param object $element
564 function get_edit_icon($element, $gpr) {
567 if (!has_capability('moodle/grade:manage', $this->context
)) {
571 static $stredit = null;
572 if (is_null($stredit)) {
573 $stredit = get_string('edit');
576 $object = $element['object'];
579 switch ($element['type']) {
583 if (empty($object->outcomeid
)) {
584 $url = $CFG->wwwroot
.'/grade/edit/item.php?courseid='.$this->courseid
.'&id='.$object->id
;
586 $url = $CFG->wwwroot
.'/grade/edit/outcomeitem.php?courseid='.$this->courseid
.'&id='.$object->id
;
588 $url = $gpr->add_url_params($url);
592 $url = $CFG->wwwroot
.'/grade/edit/category.php?courseid='.$this->courseid
.'&id='.$object->id
;
593 $url = $gpr->add_url_params($url);
597 //TODO: improve dealing with new grades
598 $url = $CFG->wwwroot
.'/grade/edit/grade.php?courseid='.$this->courseid
.'&id='.$object->id
;
599 $url = $gpr->add_url_params($url);
600 if (!empty($object->feedback
)) {
601 $feedback = format_text($object->feedback
, $object->feedbackformat
);
602 $function = "return overlib('".s(ltrim($object->feedback
)."', FULLHTML);");
603 $overlib = 'onmouseover="'.$function.'" onmouseout="return nd();"';
612 return '<a href="'.$url.'"><img '.$overlib.' src="'.$CFG->pixpath
.'/t/edit.gif" class="iconsmall" alt="'.$stredit.'" title="'.$stredit.'"/></a>';
620 * Return hiding icon for give element
621 * @param object $element
624 function get_hiding_icon($element, $gpr) {
627 if (!has_capability('moodle/grade:manage', $this->context
) and !has_capability('moodle/grade:hide', $this->context
)) {
631 static $strshow = null;
632 static $strhide = null;
633 if (is_null($strshow)) {
634 $strshow = get_string('show');
635 $strhide = get_string('hide');
638 if ($element['object']->is_hidden()) {
639 $url = $CFG->wwwroot
.'/grade/edit/action.php?id='.$this->courseid
.'&action=show&sesskey='.sesskey().'&eid='.$element['eid'];
640 $url = $gpr->add_url_params($url);
641 $action = '<a href="'.$url.'"><img src="'.$CFG->pixpath
.'/t/show.gif" class="iconsmall" alt="'.$strshow.'" title="'.$strshow.'"/></a>';
644 $url = $CFG->wwwroot
.'/grade/edit/action.php?id='.$this->courseid
.'&action=hide&sesskey='.sesskey().'&eid='.$element['eid'];
645 $url = $gpr->add_url_params($url);
646 $action = '<a href="'.$url.'"><img src="'.$CFG->pixpath
.'/t/hide.gif" class="iconsmall" alt="'.$strhide.'" title="'.$strhide.'"/></a>';
652 * Return locking icon for give element
653 * @param object $element
656 function get_locking_icon($element, $gpr) {
659 static $strunlock = null;
660 static $strlock = null;
661 if (is_null($strunlock)) {
662 $strunlock = get_string('unlock', 'grades');
663 $strlock = get_string('lock', 'grades');
666 if ($element['object']->is_locked()) {
667 if (!has_capability('moodle/grade:manage', $this->context
) and !has_capability('moodle/grade:unlock', $this->context
)) {
670 $url = $CFG->wwwroot
.'/grade/edit/action.php?id='.$this->courseid
.'&action=unlock&sesskey='.sesskey().'&eid='.$element['eid'];
671 $url = $gpr->add_url_params($url);
672 $action = '<a href="'.$url.'"><img src="'.$CFG->pixpath
.'/t/unlock.gif" class="iconsmall" alt="'.$strunlock.'" title="'.$strunlock.'"/></a>';
675 if (!has_capability('moodle/grade:manage', $this->context
) and !has_capability('moodle/grade:lock', $this->context
)) {
678 $url = $CFG->wwwroot
.'/grade/edit/action.php?id='.$this->courseid
.'&action=lock&sesskey='.sesskey().'&eid='.$element['eid'];
679 $url = $gpr->add_url_params($url);
680 $action = '<a href="'.$url.'"><img src="'.$CFG->pixpath
.'/t/lock.gif" class="iconsmall" alt="'.$strlock.'" title="'.$strlock.'"/></a>';
686 * Return calculation icon for given element
687 * @param object $element
690 function get_calculation_icon($element, $gpr) {
692 if (!has_capability('moodle/grade:manage', $this->context
)) {
696 $calculation_icon = '';
698 $type = $element['type'];
699 $object = $element['object'];
701 if ($type == 'item' or $type == 'courseitem' or $type == 'categoryitem') {
702 $streditcalculation = get_string('editcalculation', 'grades');
704 // show calculation icon only when calculation possible
705 if (!$object->is_normal_item() and ($object->gradetype
== GRADE_TYPE_SCALE
or $object->gradetype
== GRADE_TYPE_VALUE
)) {
706 $url = $CFG->wwwroot
.'/grade/edit/calculation.php?courseid='.$this->courseid
.'&id='.$object->id
;
707 $url = $gpr->add_url_params($url);
708 $calculation_icon = '<a href="'. $url.'"><img src="'.$CFG->pixpath
.'/t/calc.gif" class="iconsmall" alt="'
709 . $streditcalculation.'" title="'.$streditcalculation.'" /></a>'. "\n";
713 return $calculation_icon;