diff --git a/grade/report/grader/lib.php b/grade/report/grader/lib.php index 32577b81c29..26b49332124 100644 --- a/grade/report/grader/lib.php +++ b/grade/report/grader/lib.php @@ -671,15 +671,24 @@ class grade_report_grader extends grade_report { $userrow->cells[] = $usercell; + $userreportcell = new html_table_cell(); + $userreportcell->attributes['class'] = 'userreport'; + $userreportcell->header = true; if (has_capability('gradereport/'.$CFG->grade_profilereport.':view', $this->context)) { - $userreportcell = new html_table_cell(); - $userreportcell->attributes['class'] = 'userreport'; - $userreportcell->header = true; $a = new stdClass(); $a->user = $fullname; $strgradesforuser = get_string('gradesforuser', 'grades', $a); $url = new moodle_url('/grade/report/'.$CFG->grade_profilereport.'/index.php', array('userid' => $user->id, 'id' => $this->course->id)); - $userreportcell->text = $OUTPUT->action_icon($url, new pix_icon('t/grades', $strgradesforuser)); + $userreportcell->text .= $OUTPUT->action_icon($url, new pix_icon('t/grades', $strgradesforuser)); + } + + if (has_capability('gradereport/singleview:view', $this->context)) { + $url = new moodle_url('/grade/report/singleview/index.php', array('id' => $this->course->id, 'itemid' => $user->id, 'item' => 'user')); + $singleview = $OUTPUT->action_icon($url, new pix_icon('t/editstring', get_string('singleview', 'grades', $fullname))); + $userreportcell->text .= $singleview; + } + + if ($userreportcell->text) { $userrow->cells[] = $userreportcell; } @@ -817,8 +826,17 @@ class grade_report_grader extends grade_report { $itemcell->attributes['class'] .= ' dimmed_text'; } + $singleview = ''; + if (has_capability('gradereport/singleview:view', $this->context)) { + $url = new moodle_url('/grade/report/singleview/index.php', array( + 'id' => $this->course->id, + 'item' => 'grade', + 'itemid' => $element['object']->id)); + $singleview = $OUTPUT->action_icon($url, new pix_icon('t/editstring', get_string('singleview', 'grades', $element['object']->itemname))); + } + $itemcell->colspan = $colspan; - $itemcell->text = shorten_text($headerlink) . $arrow; + $itemcell->text = shorten_text($headerlink) . $arrow . $singleview; $itemcell->header = true; $itemcell->scope = 'col'; diff --git a/grade/report/singleview/classes/event/grade_report_viewed.php b/grade/report/singleview/classes/event/grade_report_viewed.php new file mode 100644 index 00000000000..b35c4cb1b13 --- /dev/null +++ b/grade/report/singleview/classes/event/grade_report_viewed.php @@ -0,0 +1,60 @@ +. + +/** + * Single view report viewed event. + * + * @package gradereport_singleview + * @copyright 2014 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace gradereport_singleview\event; + +defined('MOODLE_INTERNAL') || die(); + +/** + * User report viewed event class. + * + * @package gradereport_singleview + * @since Moodle 2.8 + * @copyright 2014 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class grade_report_viewed extends \core\event\grade_report_viewed { + + /** + * Returns localised general event name. + * + * @return string + */ + public static function get_name() { + return get_string('eventgradereportviewed', 'gradereport_singleview'); + } + + /** + * Custom validation. + * + * Throw \coding_exception notice in case of any problems. + */ + protected function validate_data() { + parent::validate_data(); + + if (!isset($this->relateduserid)) { + throw new \coding_exception('The \'relateduserid\' value must be set.'); + } + } +} diff --git a/grade/report/singleview/classes/local/screen/filterable_items.php b/grade/report/singleview/classes/local/screen/filterable_items.php new file mode 100644 index 00000000000..b266cf9bbd8 --- /dev/null +++ b/grade/report/singleview/classes/local/screen/filterable_items.php @@ -0,0 +1,44 @@ +. + +/** + * The gradebook interface for a filterable class. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace gradereport_singleview\local\screen; + +defined('MOODLE_INTERNAL') || die; + +/** + * The gradebook interface for a filterable class. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +interface filterable_items { + + /** + * Return true/false if this item should be filtered. + * @param mixed $item (user or grade_item) + * @return bool + */ + public static function filter($item); +} diff --git a/grade/report/singleview/classes/local/screen/grade.php b/grade/report/singleview/classes/local/screen/grade.php new file mode 100644 index 00000000000..0cb0321b935 --- /dev/null +++ b/grade/report/singleview/classes/local/screen/grade.php @@ -0,0 +1,334 @@ +. + +/** + * The screen with a list of users. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace gradereport_singleview\local\screen; + +use gradereport_singleview\local\ui\range; +use gradereport_singleview\local\ui\bulk_insert; +use grade_grade; +use grade_item; +use moodle_url; +use pix_icon; +use html_writer; +use gradereport_singleview; + +defined('MOODLE_INTERNAL') || die; + +/** + * The screen with a list of users. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class grade extends tablelike implements selectable_items, filterable_items { + + /** @var int $totalitemcount Used for paging */ + private $totalitemcount = 0; + + /** @var bool $requiresextra True if this is a manual grade item */ + private $requiresextra = false; + + /** @var bool $requirepaging True if there are more users than our limit. */ + private $requirespaging = true; + + /** + * True if $CFG->grade_overridecat is true + * + * @return bool + */ + public static function allowcategories() { + return get_config('moodle', 'grade_overridecat'); + } + + /** + * Filter the list excluding category items (if required)? + * @param grade_item $item The grade item. + */ + public static function filter($item) { + return get_config('moodle', 'grade_overridecat') || + !($item->is_course_item() || $item->is_category_item()); + } + + /** + * Get the description of this page + * @return string + */ + public function description() { + return get_string('users'); + } + + /** + * Convert this list of items into an options list + * + * @return array + */ + public function options() { + $options = array(); + foreach ($this->items as $userid => $user) { + $options[$userid] = fullname($user); + } + + return $options; + } + + /** + * Return the type of the things in this list. + * @return string + */ + public function item_type() { + return 'user'; + } + + /** + * Get the original settings for this item + * @return array + */ + public function original_definition() { + $def = array('finalgrade', 'feedback'); + + if ($this->requiresextra) { + $def[] = 'override'; + } + + $def[] = 'exclude'; + + return $def; + } + + /** + * Init this page + * + * @param bool $selfitemisempty True if we have not selected a user. + */ + public function init($selfitemisempty = false) { + $roleids = explode(',', get_config('moodle', 'gradebookroles')); + + $this->items = get_role_users( + $roleids, $this->context, false, '', + 'u.lastname, u.firstname', null, $this->groupid); + + $this->totalitemcount = count_role_users($roleids, $this->context); + + if ($selfitemisempty) { + return; + } + + $params = array( + 'id' => $this->itemid, + 'courseid' => $this->courseid + ); + + $this->item = grade_item::fetch($params); + if (!self::filter($this->item)) { + $this->items = array(); + $this->set_init_error(get_string('gradeitemcannotbeoverridden', 'gradereport_singleview')); + } + + $this->requiresextra = !$this->item->is_manual_item(); + + $this->setup_structure(); + + $this->set_definition($this->original_definition()); + $this->set_headers($this->original_headers()); + } + + /** + * Get the table headers + * + * @return array + */ + public function original_headers() { + return array( + '', // For filter icon. + '', // For user picture. + get_string('firstname') . ' (' . get_string('alternatename') . ') ' . get_string('lastname'), + get_string('range', 'grades'), + get_string('grade', 'grades'), + get_string('feedback', 'grades'), + $this->make_toggle_links('override'), + $this->make_toggle_links('exclude') + ); + } + + /** + * Format a row in the table + * + * @param user $item + * @return string + */ + public function format_line($item) { + global $OUTPUT; + + $grade = $this->fetch_grade_or_default($this->item, $item->id); + + $lockicon = ''; + + $lockedgrade = $lockedgradeitem = 0; + if (!empty($grade->locked)) { + $lockedgrade = 1; + } + if (!empty($grade->grade_item->locked)) { + $lockedgradeitem = 1; + } + // Check both grade and grade item. + if ( $lockedgrade || $lockedgradeitem ) { + $lockicon = $OUTPUT->pix_icon('t/locked', 'grade is locked') . ' '; + } + + if (!empty($item->alternatename)) { + $fullname = $lockicon . $item->alternatename . ' (' . $item->firstname . ') ' . $item->lastname; + } else { + $fullname = $lockicon . fullname($item); + } + + $item->imagealt = $fullname; + $url = new moodle_url("/user/view.php", array('id' => $item->id, 'course' => $this->courseid)); + $iconstring = get_string('filtergrades', 'gradereport_singleview', $fullname); + $grade->label = $fullname; + + $line = array( + $OUTPUT->action_icon($this->format_link('user', $item->id), new pix_icon('t/editstring', $iconstring)), + $OUTPUT->user_picture($item), + html_writer::link($url, $fullname), + $this->item_range() + ); + + return $this->format_definition($line, $grade); + } + + /** + * Get the range ui element for this grade_item + * + * @return element; + */ + public function item_range() { + if (empty($this->range)) { + $this->range = new range($this->item); + } + + return $this->range; + } + + /** + * Does this page require paging? + * + * @return bool + */ + public function supports_paging() { + return $this->requirespaging; + } + + /** + * Get the pager for this page. + * + * @return string + */ + public function pager() { + global $OUTPUT; + + return $OUTPUT->paging_bar( + $this->totalitemcount, $this->page, $this->perpage, + new moodle_url('/grade/report/singleview/index.php', array( + 'perpage' => $this->perpage, + 'id' => $this->courseid, + 'groupid' => $this->groupid, + 'itemid' => $this->itemid, + 'item' => 'grade' + )) + ); + } + + /** + * Get the heading for this page. + * + * @return string + */ + public function heading() { + return $this->item->get_name(); + } + + /** + * Process the data from the form. + * + * @param array $data + * @return array of warnings + */ + public function process($data) { + $bulk = new bulk_insert($this->item); + // Bulk insert messages the data to be passed in + // ie: for all grades of empty grades apply the specified value. + if ($bulk->is_applied($data)) { + $filter = $bulk->get_type($data); + $insertvalue = $bulk->get_insert_value($data); + // Appropriately massage data that may not exist. + if ($this->supports_paging()) { + $gradeitem = grade_item::fetch(array( + 'courseid' => $this->courseid, + 'id' => $this->item->id + )); + + $null = $gradeitem->gradetype == GRADE_TYPE_SCALE ? -1 : ''; + + foreach ($this->items as $itemid => $item) { + $field = "finalgrade_{$gradeitem->id}_{$itemid}"; + if (isset($data->$field)) { + continue; + } + + $grade = grade_grade::fetch(array( + 'itemid' => $gradeitem->id, + 'userid' => $itemid + )); + + $data->$field = empty($grade) ? $null : $grade->finalgrade; + $data->{"old$field"} = $data->$field; + } + } + + foreach ($data as $varname => $value) { + if (!preg_match('/^finalgrade_(\d+)_/', $varname, $matches)) { + continue; + } + + $gradeitem = grade_item::fetch(array( + 'courseid' => $this->courseid, + 'id' => $matches[1] + )); + + $isscale = ($gradeitem->gradetype == GRADE_TYPE_SCALE); + + $empties = (trim($value) === '' or ($isscale and $value == -1)); + + if ($filter == 'all' or $empties) { + $data->$varname = ($isscale and empty($insertvalue)) ? + -1 : $insertvalue; + } + } + } + + return parent::process($data); + } + +} diff --git a/grade/report/singleview/classes/local/screen/screen.php b/grade/report/singleview/classes/local/screen/screen.php new file mode 100644 index 00000000000..fd4fa6d0f91 --- /dev/null +++ b/grade/report/singleview/classes/local/screen/screen.php @@ -0,0 +1,346 @@ +. + +/** + * Abstract class used as a base for the 3 screens. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace gradereport_singleview\local\screen; + +use context_course; +use moodle_url; +use html_writer; +use grade_structure; +use grade_grade; +use grade_item; +use stdClass; + +defined('MOODLE_INTERNAL') || die; + +/** + * Abstract class used as a base for the 3 screens. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +abstract class screen { + + /** @var int $courseid The id of the course */ + protected $courseid; + + /** @var int $itemid Either a user id or a grade_item id */ + protected $itemid; + + /** @var int $groupid The currently set groupid (if set) */ + protected $groupid; + + /** @var course_context $context The course context */ + protected $context; + + /** @var int $page The page number */ + protected $page; + + /** @var int $perpage Results per page */ + protected $perpage; + + /** @var array $items List of items on the page, they could be users or grade_items */ + protected $items; + + /** + * Constructor + * + * @param int $courseid The course id + * @param int $itemid The item id + * @param int $groupid The group id + */ + public function __construct($courseid, $itemid, $groupid = null) { + global $DB; + + $this->courseid = $courseid; + $this->itemid = $itemid; + $this->groupid = $groupid; + + $this->context = context_course::instance($this->courseid); + $this->course = $DB->get_record('course', array('id' => $courseid)); + + $this->page = optional_param('page', 0, PARAM_INT); + $this->perpage = optional_param('perpage', 100, PARAM_INT); + if ($this->perpage > 100) { + $this->perpage = 100; + } + + $this->init(empty($itemid)); + } + + /** + * Cache the grade_structure class + */ + public function setup_structure() { + $this->structure = new grade_structure(); + $this->structure->modinfo = get_fast_modinfo($this->course); + } + + /** + * Create a nice link from a thing (user or grade_item). + * + * @param string $screen + * @param int $itemid + * @param bool $display Should we wrap this in an anchor ? + * @return string The link + */ + public function format_link($screen, $itemid, $display = null) { + $url = new moodle_url('/grade/report/singleview/index.php', array( + 'id' => $this->courseid, + 'item' => $screen, + 'itemid' => $itemid, + 'group' => $this->groupid, + )); + + if ($display) { + return html_writer::link($url, $display); + } else { + return $url; + } + } + + /** + * Get the grade_grade + * + * @param grade_item $item The grade_item + * @param int $userid The user id + * @return grade_grade + */ + public function fetch_grade_or_default($item, $userid) { + $grade = grade_grade::fetch(array( + 'itemid' => $item->id, 'userid' => $userid + )); + + if (!$grade) { + $default = new stdClass; + + $default->userid = $userid; + $default->itemid = $item->id; + $default->feedback = ''; + + $grade = new grade_grade($default, false); + } + + $grade->grade_item = $item; + + return $grade; + } + + /** + * Make the HTML element that toggles all the checkboxes on or off. + * + * @param string $key A unique key for this control - inserted in the classes. + * @return string + */ + public function make_toggle($key) { + $attrs = array('href' => '#'); + + $all = html_writer::tag('a', get_string('all'), $attrs + array( + 'class' => 'include all ' . $key + )); + + $none = html_writer::tag('a', get_string('none'), $attrs + array( + 'class' => 'include none ' . $key + )); + + return html_writer::tag('span', "$all / $none", array( + 'class' => 'inclusion_links' + )); + } + + /** + * Make a toggle link with some text before it. + * + * @param string $key A unique key for this control - inserted in the classes. + * @return string + */ + public function make_toggle_links($key) { + return get_string($key, 'gradereport_singleview') . ' ' . + $this->make_toggle($key); + } + + /** + * Get the default heading for the screen. + * + * @return string + */ + public function heading() { + return get_string('pluginname', 'gradereport_singleview'); + } + + /** + * Override this to init the screen. + * + * @param boolean $selfitemisempty True if no item has been selected yet. + */ + public abstract function init($selfitemisempty = false); + + /** + * Get the type of items in the list. + * + * @return string + */ + public abstract function item_type(); + + /** + * Get the entire screen as a string. + * + * @return string + */ + public abstract function html(); + + /** + * Does this screen support paging? + * + * @return bool + */ + public function supports_paging() { + return true; + } + + /** + * Default pager + * + * @return string + */ + public function pager() { + return ''; + } + + /** + * Initialise the js for this screen. + */ + public function js() { + global $PAGE; + + $module = array( + 'name' => 'gradereport_singleview', + 'fullpath' => '/grade/report/singleview/js/singleview.js', + 'requires' => array('base', 'dom', 'event', 'event-simulate', 'io-base') + ); + + $PAGE->requires->js_init_call('M.gradereport_singleview.init', array(), false, $module); + } + + /** + * Process the data from a form submission. + * + * @param array $data + * @return array of warnings + */ + public function process($data) { + $warnings = array(); + + $fields = $this->definition(); + + foreach ($data as $varname => $throw) { + if (preg_match("/(\w+)_(\d+)_(\d+)/", $varname, $matches)) { + $itemid = $matches[2]; + $userid = $matches[3]; + } else { + continue; + } + + $gradeitem = grade_item::fetch(array( + 'id' => $itemid, 'courseid' => $this->courseid + )); + + if (preg_match('/^old[oe]{1}/', $varname)) { + $elementname = preg_replace('/^old/', '', $varname); + if (!isset($data->$elementname)) { + $data->$elementname = false; + } + } + + if (!in_array($matches[1], $fields)) { + continue; + } + + if (!$gradeitem) { + continue; + } + + $grade = $this->fetch_grade_or_default($gradeitem, $userid); + + $classname = '\\gradereport_singleview\\local\\ui\\' . $matches[1]; + $element = new $classname($grade); + + $name = $element->get_name(); + $oldname = "old$name"; + + $posted = $data->$name; + + $format = $element->determine_format(); + + if ($format->is_textbox() and trim($data->$name) === '') { + $data->$name = null; + } + + // Same value; skip. + if (isset($data->$oldname) && $data->$oldname == $posted) { + continue; + } + + $msg = $element->set($posted); + + // Optional type. + if (!empty($msg)) { + $warnings[] = $msg; + } + } + + // Some post-processing. + $eventdata = new stdClass; + $eventdata->warnings = $warnings; + $eventdata->post_data = $data; + $eventdata->instance = $this; + + return $eventdata->warnings; + } + + /** + * By default there are no options. + * @return array + */ + public function options() { + return array(); + } + + /** + * Should we show the group selector? + * @return bool + */ + public function display_group_selector() { + return true; + } + + /** + * Should we show the next prev selector? + * @return bool + */ + public function supports_next_prev() { + return true; + } +} diff --git a/grade/report/singleview/classes/local/screen/select.php b/grade/report/singleview/classes/local/screen/select.php new file mode 100644 index 00000000000..4554043d70f --- /dev/null +++ b/grade/report/singleview/classes/local/screen/select.php @@ -0,0 +1,121 @@ +. + +/** + * The gradebook simple view - initial view to select your search options + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace gradereport_singleview\local\screen; + +use gradereport_singleview; +use moodle_url; + +defined('MOODLE_INTERNAL') || die; + +/** + * The gradebook simple view - initial view to select your search options + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class select extends screen { + + /** + * Initialise this screen + * + * @param bool $selfitemisempty Has an item been selected (will be false) + */ + public function init($selfitemisempty = false) { + global $DB; + + $roleids = explode(',', get_config('moodle', 'gradebookroles')); + + $this->items = get_role_users( + $roleids, $this->context, false, '', + 'u.id, u.lastname, u.firstname', null, $this->groupid, + $this->perpage * $this->page, $this->perpage + ); + $this->item = $DB->get_record('course', array('id' => $this->courseid)); + } + + /** + * Get the type of items on this screen, not valid so return false. + * + * @return bool + */ + public function item_type() { + return false; + } + + /** + * Return the HTML for the page. + * + * @return string + */ + public function html() { + global $OUTPUT; + + $html = ''; + + $types = gradereport_singleview::valid_screens(); + + foreach ($types as $type) { + $classname = "gradereport_singleview\\local\\screen\\${type}"; + + $screen = new $classname($this->courseid, null, $this->groupid); + + if (!$screen instanceof selectable_items) { + continue; + } + + $options = $screen->options(); + + if (empty($options)) { + continue; + } + + $params = array( + 'id' => $this->courseid, + 'item' => $screen->item_type(), + 'group' => $this->groupid + ); + + $url = new moodle_url('/grade/report/singleview/index.php', $params); + $html .= $OUTPUT->heading($screen->description()); + + $html .= $OUTPUT->single_select($url, 'itemid', $options); + } + + if (empty($html)) { + $OUTPUT->notification(get_string('noscreens', 'gradereport_singleview')); + } + + return $html; + } + + /** + * Should we show the next prev selector? + * @return bool + */ + public function supports_next_prev() { + return false; + } +} diff --git a/grade/report/singleview/classes/local/screen/selectable_items.php b/grade/report/singleview/classes/local/screen/selectable_items.php new file mode 100644 index 00000000000..84967fc0571 --- /dev/null +++ b/grade/report/singleview/classes/local/screen/selectable_items.php @@ -0,0 +1,54 @@ +. + +/** + * Interface for a list of selectable things. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace gradereport_singleview\local\screen; + +defined('MOODLE_INTERNAL') || die; + +/** + * Interface for a list of selectable things. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +interface selectable_items { + /** + * Get the description of this list + * @return string + */ + public function description(); + + /** + * Get the list of options to show. + * @return array + */ + public function options(); + + /** + * Get type of things in the list (gradeitem or user) + * @return string + */ + public function item_type(); +} diff --git a/grade/report/singleview/classes/local/screen/tablelike.php b/grade/report/singleview/classes/local/screen/tablelike.php new file mode 100644 index 00000000000..775f1457100 --- /dev/null +++ b/grade/report/singleview/classes/local/screen/tablelike.php @@ -0,0 +1,225 @@ +. + +/** + * The gradebook simple view - base class for the table + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace gradereport_singleview\local\screen; + +use html_table; +use html_writer; +use stdClass; +use grade_item; +use grade_grade; +use gradereport_singleview\local\ui\bulk_insert; + +defined('MOODLE_INTERNAL') || die; + +/** + * The gradebook simple view - base class for the table + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +abstract class tablelike extends screen { + + /** @var array $headers A list of table headers */ + protected $headers = array(); + + /** @var array $initerrors A list of errors that mean we should not show the table */ + protected $initerrors = array(); + + /** @var array $definition Describes the columns in the table */ + protected $definition = array(); + + /** + * Format a row of the table + * + * @param mixed $item + * @return string + */ + public abstract function format_line($item); + + /** + * Get the table headers + * + * @return array + */ + public function headers() { + return $this->headers; + } + + /** + * Set the table headers + * + * @param array $overwrite New headers + * @return tablelike This + */ + public function set_headers($overwrite) { + $this->headers = $overwrite; + return $this; + } + + /** + * Get the list of errors + * + * @return array + */ + public function init_errors() { + return $this->initerrors; + } + + /** + * Set an error detected while building the page. + * + * @param string $mesg + */ + public function set_init_error($mesg) { + $this->initerrors[] = $mesg; + } + + /** + * Get the table definition + * + * @return array The definition. + */ + public function definition() { + return $this->definition; + } + + /** + * Set the table definition + * + * @param array $overwrite New definition + * @return tablelike This + */ + public function set_definition($overwrite) { + $this->definition = $overwrite; + return $this; + } + + /** + * Get the tabindex for the table in the page. + * + * @return int + */ + public function get_tabindex() { + return (count($this->definition()) * $this->total) + $this->index; + } + + /** + * Get a element to generate the HTML for this table row + * @param array $line This is a list of lines in the table (modified) + * @param grade_grade $grade The grade. + * @return string + */ + public function format_definition($line, $grade) { + foreach ($this->definition() as $i => $field) { + // Table tab index. + $tab = ($i * $this->total) + $this->index; + $classname = '\\gradereport_singleview\\local\\ui\\' . $field; + $html = new $classname($grade, $tab); + + if ($field == 'finalgrade' and !empty($this->structure)) { + $html .= $this->structure->get_grade_analysis_icon($grade); + } + + $line[] = $html; + } + return $line; + } + + /** + * Get the HTML for the whole table + * @return string + */ + public function html() { + global $OUTPUT; + + if (!empty($this->initerrors)) { + $warnings = ''; + foreach ($this->initerrors as $mesg) { + $warnings .= $OUTPUT->notification($mesg); + } + return $warnings; + } + $table = new html_table(); + + $table->head = $this->headers(); + + // To be used for extra formatting. + $this->index = 0; + $this->total = count($this->items); + + foreach ($this->items as $item) { + if ($this->index >= ($this->perpage * $this->page) && + $this->index < ($this->perpage * ($this->page + 1))) { + $table->data[] = $this->format_line($item); + } + $this->index++; + } + + $underlying = get_class($this); + + $data = new stdClass; + $data->table = $table; + $data->instance = $this; + + $buttonattr = array('class' => 'singleview_buttons submit'); + $buttonhtml = implode(' ', $this->buttons()); + + $buttons = html_writer::tag('div', $buttonhtml, $buttonattr); + + return html_writer::tag('form', + $buttons . html_writer::table($table) . $this->bulk_insert() . $buttons, + array('method' => 'POST') + ); + } + + /** + * Get the HTML for the bulk insert form + * + * @return string + */ + public function bulk_insert() { + return html_writer::tag( + 'div', + (new bulk_insert($this->item))->html(), + array('class' => 'singleview_bulk') + ); + } + + /** + * Get the buttons for saving changes. + * + * @return array + */ + public function buttons() { + $save = html_writer::empty_tag('input', array( + 'type' => 'submit', + 'value' => get_string('update'), + 'tabindex' => $this->get_tabindex(), + )); + + return array($save); + } +} diff --git a/grade/report/singleview/classes/local/screen/user.php b/grade/report/singleview/classes/local/screen/user.php new file mode 100644 index 00000000000..ba2f59bd141 --- /dev/null +++ b/grade/report/singleview/classes/local/screen/user.php @@ -0,0 +1,335 @@ +. + +/** + * The user screen. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace gradereport_singleview\local\screen; + +use grade_seq; +use gradereport_singleview; +use moodle_url; +use pix_icon; +use html_writer; +use gradereport_singleview\local\ui\range; +use gradereport_singleview\local\ui\bulk_insert; +use grade_item; +use grade_grade; + +defined('MOODLE_INTERNAL') || die; + +/** + * The user screen. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class user extends tablelike implements selectable_items { + + /** @var array $categories A cache for grade_item categories */ + private $categories = array(); + + /** @var int $requirespaging Do we have more items than the paging limit? */ + private $requirespaging = true; + + /** + * Get the description for the screen. + * + * @return string + */ + public function description() { + return get_string('gradeitems', 'grades'); + } + + /** + * Convert the list of items to a list of options. + * + * @return array + */ + public function options() { + $result = array(); + foreach ($this->items as $itemid => $item) { + $result[$itemid] = $item->get_name(); + } + return $result; + } + + /** + * Get the type of items on this screen. + * + * @return string + */ + public function item_type() { + return 'grade'; + } + + /** + * Should we show the group selector on this screen? + * + * @return bool + */ + public function display_group_selector() { + return false; + } + + /** + * Init the screen + * + * @param bool $selfitemisempty Have we selected an item yet? + */ + public function init($selfitemisempty = false) { + global $DB; + + if (!$selfitemisempty) { + $this->item = $DB->get_record('user', array('id' => $this->itemid)); + } + + $params = array('courseid' => $this->courseid); + + $seq = new grade_seq($this->courseid, true); + foreach ($seq->items as $key => $item) { + if (isset($item->itemmodule)) { + list($courseid, $cmid) = get_course_and_cm_from_instance($item->iteminstance, $item->itemmodule); + $seq->items[$key]->cmid = $cmid->id; + } + } + + $this->items = array(); + foreach ($seq->items as $itemid => $item) { + if (grade::filter($item)) { + $this->items[$itemid] = $item; + } + } + + $this->requirespaging = count($this->items) > $this->perpage; + + unset($seq); + + $this->setup_structure(); + + $this->definition = array( + 'finalgrade', 'feedback', 'override', 'exclude' + ); + $this->set_headers($this->original_headers()); + } + + /** + * Get the list of headers for the table. + * + * @return array List of headers + */ + public function original_headers() { + return array( + '', // For filter icon. + '', // For activity icon. + get_string('assessmentname', 'gradereport_singleview'), + get_string('gradecategory', 'grades'), + get_string('range', 'grades'), + get_string('grade', 'grades'), + get_string('feedback', 'grades'), + $this->make_toggle_links('override'), + $this->make_toggle_links('exclude') + ); + } + + /** + * Format each row of the table. + * + * @param grade_item $item + * @return string + */ + public function format_line($item) { + global $OUTPUT; + + $grade = $this->fetch_grade_or_default($item, $this->item->id); + $lockicon = ''; + + $lockeditem = $lockeditemgrade = 0; + if (!empty($grade->locked)) { + $lockeditem = 1; + } + if (!empty($grade->grade_item->locked)) { + $lockeditemgrade = 1; + } + // Check both grade and grade item. + if ($lockeditem || $lockeditemgrade) { + $lockicon = $OUTPUT->pix_icon('t/locked', 'grade is locked'); + } + + $realmodid = ''; + if (isset($item->cmid)) { + $realmodid = $item->cmid; + } + $url = new moodle_url('/mod/' . $item->itemmodule . '/view.php', array('id' => $realmodid)); + $iconstring = get_string('filtergrades', 'gradereport_singleview', $item->get_name()); + $grade->label = $item->get_name(); + + $line = array( + $OUTPUT->action_icon($this->format_link('grade', $item->id), new pix_icon('t/editstring', $iconstring)), + $this->format_icon($item) . $lockicon, + html_writer::link($url, $item->get_name()), + $this->category($item), + (new range($item)) + ); + return $this->format_definition($line, $grade); + } + + /** + * Helper to get the icon for an item. + * + * @param grade_item $item + * @return string + */ + private function format_icon($item) { + $element = array('type' => 'item', 'object' => $item); + return $this->structure->get_element_icon($element); + } + + /** + * Helper to get the category for an item. + * + * @param grade_item $item + * @return grade_category + */ + private function category($item) { + if (empty($item->categoryid)) { + + if ($item->itemtype == 'course') { + return $this->course->fullname; + } + + global $DB; + + $params = array('id' => $item->iteminstance); + $elem = $DB->get_record('grade_categories', $params); + + return $elem->fullname; + } + + if (!isset($this->categories[$item->categoryid])) { + $category = $item->get_parent_category(); + + $this->categories[$category->id] = $category; + } + + return $this->categories[$item->categoryid]->get_name(); + } + + /** + * Get the heading for the page. + * + * @return string + */ + public function heading() { + return fullname($this->item); + } + + /** + * Default pager + * + * @return string + */ + public function pager() { + if (!$this->supports_paging()) { + return ''; + } + + global $OUTPUT; + + return $OUTPUT->paging_bar( + count($this->items), $this->page, $this->perpage, + new moodle_url('/grade/report/singleview/index.php', array( + 'perpage' => $this->perpage, + 'id' => $this->courseid, + 'groupid' => $this->groupid, + 'itemid' => $this->itemid, + 'item' => 'user' + )) + ); + } + + /** + * Does this page require paging? + * + * @return bool + */ + public function supports_paging() { + return $this->requirespaging; + } + + + /** + * Process the data from the form. + * + * @param array $data + * @return array of warnings + */ + public function process($data) { + $bulk = new bulk_insert($this->item); + // Bulk insert messages the data to be passed in + // ie: for all grades of empty grades apply the specified value. + if ($bulk->is_applied($data)) { + $filter = $bulk->get_type($data); + $insertvalue = $bulk->get_insert_value($data); + // Appropriately massage data that may not exist. + + $userid = $this->item->id; + foreach ($this->items as $gradeitemid => $gradeitem) { + $null = $gradeitem->gradetype == GRADE_TYPE_SCALE ? -1 : ''; + $field = "finalgrade_{$gradeitem->id}_{$gradeitemid}"; + if (isset($data->$field)) { + continue; + } + + $grade = grade_grade::fetch(array( + 'itemid' => $gradeitem->id, + 'userid' => $userid + )); + + $data->$field = empty($grade) ? $null : $grade->finalgrade; + $data->{"old$field"} = $data->$field; + } + + foreach ($data as $varname => $value) { + if (!preg_match('/^finalgrade_(\d+)_/', $varname, $matches)) { + continue; + } + + $gradeitem = grade_item::fetch(array( + 'courseid' => $this->courseid, + 'id' => $matches[1] + )); + + $isscale = ($gradeitem->gradetype == GRADE_TYPE_SCALE); + + $empties = (trim($value) === '' or ($isscale and $value == -1)); + + if ($filter == 'all' or $empties) { + $data->$varname = ($isscale and empty($insertvalue)) ? + -1 : $insertvalue; + } + } + } + + return parent::process($data); + } +} diff --git a/grade/report/singleview/classes/local/ui/attribute_format.php b/grade/report/singleview/classes/local/ui/attribute_format.php new file mode 100644 index 00000000000..bbf96e28cb2 --- /dev/null +++ b/grade/report/singleview/classes/local/ui/attribute_format.php @@ -0,0 +1,52 @@ +. + +/** + * Class that builds an element tree that can be converted to a string + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace gradereport_singleview\local\ui; + +defined('MOODLE_INTERNAL') || die; + +/** + * Class that builds an element tree that can be converted to a string + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +abstract class attribute_format { + + /** + * Used to convert this class to an "element" which can be converted to a string. + * @return element + */ + abstract public function determine_format(); + + /** + * Convert this to an element and then to a string + * @return string + */ + public function __toString() { + return $this->determine_format()->html(); + } +} + diff --git a/grade/report/singleview/classes/local/ui/be_checked.php b/grade/report/singleview/classes/local/ui/be_checked.php new file mode 100644 index 00000000000..03b1bb72b84 --- /dev/null +++ b/grade/report/singleview/classes/local/ui/be_checked.php @@ -0,0 +1,42 @@ +. + +/** + * Is this thing checked? + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace gradereport_singleview\local\ui; + +defined('MOODLE_INTERNAL') || die; + +/** + * Is this thing checked? + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +interface be_checked { + /** + * Return true if this is checked. + * @return bool + */ + public function is_checked(); +} diff --git a/grade/report/singleview/classes/local/ui/be_disabled.php b/grade/report/singleview/classes/local/ui/be_disabled.php new file mode 100644 index 00000000000..28790f37584 --- /dev/null +++ b/grade/report/singleview/classes/local/ui/be_disabled.php @@ -0,0 +1,40 @@ +. + +/** + * be_disabled interface. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace gradereport_singleview\local\ui; + +defined('MOODLE_INTERNAL') || die; + +/** + * Simple interface implemented to add behaviour that an element can be checked to see + * if it should be disabled. + */ +interface be_disabled { + /** + * Am I disabled ? + * + * @return bool + */ + public function is_disabled(); +} diff --git a/grade/report/singleview/classes/local/ui/bulk_insert.php b/grade/report/singleview/classes/local/ui/bulk_insert.php new file mode 100644 index 00000000000..57d45c45a83 --- /dev/null +++ b/grade/report/singleview/classes/local/ui/bulk_insert.php @@ -0,0 +1,116 @@ +. + +/** + * Checkbox element used for bulk inserting values in the gradebook. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace gradereport_singleview\local\ui; + +use html_writer; + +defined('MOODLE_INTERNAL') || die; + +/** + * Checkbox element used for bulk inserting values in the gradebook. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class bulk_insert extends element { + + /** + * Constructor + * + * @param mixed $item The grade item or user. + */ + public function __construct($item) { + $this->name = 'bulk_' . $item->id; + $this->applyname = $this->name_for('apply'); + $this->selectname = $this->name_for('type'); + $this->insertname = $this->name_for('value'); + } + + /** + * Is this checkbox checked? + * + * @param array $data The form data + * @return bool + */ + public function is_applied($data) { + return isset($data->{$this->applyname}); + } + + /** + * Get the type of this input (user or grade) + * + * @param array $data The form data + * @return string + */ + public function get_type($data) { + return $data->{$this->selectname}; + } + + /** + * Get the value from either the user or grade. + * + * @param array $data The form data + * @return string + */ + public function get_insert_value($data) { + return $data->{$this->insertname}; + } + + /** + * Generate the html for this form element. + * + * @return string HTML + */ + public function html() { + $insertgrade = get_string('bulkinsertgrade', 'gradereport_singleview'); + $insertappliesto = get_string('bulkappliesto', 'gradereport_singleview'); + + $apply = html_writer::checkbox($this->applyname, 1, false, $insertgrade); + $insertoptions = array( + 'all' => get_string('all_grades', 'gradereport_singleview'), + 'blanks' => get_string('blanks', 'gradereport_singleview') + ); + + $select = html_writer::select( + $insertoptions, $this->selectname, 'blanks', false + ); + + $label = html_writer::tag('label', $insertappliesto); + $text = new text_attribute($this->insertname, "0", 'bulk'); + return implode(' ', array($apply, $text->html(), $label, $select)); + } + + /** + * This form element has 3 elements with different suffixes. + * Generate the name with the suffix. + * + * @param string $extend The suffix. + * @return string + */ + private function name_for($extend) { + return "{$this->name}_$extend"; + } +} diff --git a/grade/report/singleview/classes/local/ui/checkbox_attribute.php b/grade/report/singleview/classes/local/ui/checkbox_attribute.php new file mode 100644 index 00000000000..a16b08180b4 --- /dev/null +++ b/grade/report/singleview/classes/local/ui/checkbox_attribute.php @@ -0,0 +1,115 @@ +. + +/** + * A checkbox ui element. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +namespace gradereport_singleview\local\ui; + +use html_writer; + +defined('MOODLE_INTERNAL') || die; + +/** + * A checkbox ui element. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class checkbox_attribute extends element { + + /** @var bool $ischecked Is it checked? */ + private $ischecked; + + /** @var int $tabindex The tab index for this form element. */ + private $tabindex; + + /** + * Constructor + * + * @param string $name The element name + * @param string $label The label for the form element + * @param bool $ischecked Is this thing on? + * @param int $tabindex The tab index for the form element. + * @param int $locked Is this element locked either 0 or a time. + */ + public function __construct($name, $label, $ischecked = false, $tabindex = null, $locked=0) { + $this->ischecked = $ischecked; + $this->tabindex = $tabindex; + $this->locked = $locked; + parent::__construct($name, 1, $label); + } + + /** + * Nasty function allowing checkbox logic to escape the class. + * @return bool + */ + public function is_checkbox() { + return true; + } + + /** + * Generate the HTML + * + * @return string + */ + public function html() { + + $attributes = array( + 'type' => 'checkbox', + 'name' => $this->name, + 'value' => 1, + 'id' => $this->name + ); + + // UCSB fixed user should not be able to override locked grade. + if ( $this->locked) { + $attributes['disabled'] = 'DISABLED'; + } + + $hidden = array( + 'type' => 'hidden', + 'name' => 'old' . $this->name + ); + + if (!empty($this->tabindex)) { + $attributes['tabindex'] = $this->tabindex; + } + + if ($this->ischecked) { + $attributes['checked'] = 'CHECKED'; + $hidden['value'] = 1; + } + + $type = "override"; + if (preg_match("/^exclude/", $this->name)) { + $type = "exclude"; + } + + return ( + html_writer::tag('label', + get_string($type . 'for', 'gradereport_singleview', $this->label), + array('for' => $this->name, 'class' => 'accesshide')) . + html_writer::empty_tag('input', $attributes) . + html_writer::empty_tag('input', $hidden) + ); + } +} diff --git a/grade/report/singleview/classes/local/ui/dropdown_attribute.php b/grade/report/singleview/classes/local/ui/dropdown_attribute.php new file mode 100644 index 00000000000..af70f02b4c0 --- /dev/null +++ b/grade/report/singleview/classes/local/ui/dropdown_attribute.php @@ -0,0 +1,101 @@ +. + +/** + * Drop down list (select list) element + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace gradereport_singleview\local\ui; + +defined('MOODLE_INTERNAL') || die; + +/** + * Drop down list (select list) element + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class dropdown_attribute extends element { + + /** @var string $selected Who is selected ? */ + private $selected; + + /** @var array $options List of options ? */ + private $options; + + /** @var bool $isdisabled Is this input disabled. */ + private $isdisabled; + + /** + * Constructor + * + * @param string $name The first bit of the name of this input. + * @param array $options The options list for this select. + * @param string $label The form label for this input. + * @param string $selected The name of the selected item in this input. + * @param bool $isdisabled Are we disabled? + * @param int $tabindex The tab index for this field + */ + public function __construct($name, $options, $label, $selected = '', $isdisabled = false, $tabindex = null) { + $this->selected = $selected; + $this->options = $options; + $this->tabindex = $tabindex; + $this->isdisabled = $isdisabled; + parent::__construct($name, $selected, $label); + } + + /** + * Nasty function spreading dropdown logic around. + * + * @return bool + */ + public function is_dropdown() { + return true; + } + + /** + * Render this element as html. + * + * @return string + */ + public function html() { + $old = array( + 'type' => 'hidden', + 'name' => 'old' . $this->name, + 'value' => $this->selected + ); + + $attributes = array(); + if (!empty($this->tabindex)) { + $attributes['tabindex'] = $this->tabindex; + } + + if (!empty($this->isdisabled)) { + $attributes['disabled'] = 'DISABLED'; + } + + $select = html_writer::select( + $this->options, $this->name, $this->selected, false, $attributes + ); + + return ($select . html_writer::empty_tag('input', $old)); + } +} diff --git a/grade/report/singleview/classes/local/ui/element.php b/grade/report/singleview/classes/local/ui/element.php new file mode 100644 index 00000000000..22b901b2588 --- /dev/null +++ b/grade/report/singleview/classes/local/ui/element.php @@ -0,0 +1,89 @@ +. + +/** + * UI Element for an excluded grade_grade. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace gradereport_singleview\local\ui; + +defined('MOODLE_INTERNAL') || die; + +/** + * UI Element for an excluded grade_grade. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +abstract class element { + + /** @var string $name The first bit of the name for this input. */ + public $name; + + /** @var string $value The value for this input. */ + public $value; + + /** @var string $label The form label for this input. */ + public $label; + + /** + * Constructor + * + * @param string $name The first bit of the name for this input + * @param string $value The value for this input + * @param string $label The label for this form field + */ + public function __construct($name, $value, $label) { + $this->name = $name; + $this->value = $value; + $this->label = $label; + } + + /** + * Nasty function used for spreading checkbox logic all around + * @return bool + */ + public function is_checkbox() { + return false; + } + + /** + * Nasty function used for spreading textbox logic all around + * @return bool + */ + public function is_textbox() { + return false; + } + + /** + * Nasty function used for spreading dropdown logic all around + * @return bool + */ + public function is_dropdown() { + return false; + } + + /** + * Return the HTML + * @return string + */ + abstract public function html(); +} diff --git a/grade/report/singleview/classes/local/ui/empty_element.php b/grade/report/singleview/classes/local/ui/empty_element.php new file mode 100644 index 00000000000..32ff3ce57fb --- /dev/null +++ b/grade/report/singleview/classes/local/ui/empty_element.php @@ -0,0 +1,59 @@ +. + +/** + * Element that just generates some text. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace gradereport_singleview\local\ui; + +defined('MOODLE_INTERNAL') || die; + +/** + * Element that just generates some text. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class empty_element extends element { + + /** + * Constructor + * + * @param string $msg The text + */ + public function __construct($msg = null) { + if (is_null($msg)) { + $this->text = ' '; + } else { + $this->text = $msg; + } + } + + /** + * Generate the html (simple case) + * + * @return string HTML + */ + public function html() { + return $this->text; + } +} diff --git a/grade/report/singleview/classes/local/ui/exclude.php b/grade/report/singleview/classes/local/ui/exclude.php new file mode 100644 index 00000000000..03e4f6ee14a --- /dev/null +++ b/grade/report/singleview/classes/local/ui/exclude.php @@ -0,0 +1,110 @@ +. + +/** + * Class that represents the exclude checkbox on a grade_grade. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace gradereport_singleview\local\ui; + +defined('MOODLE_INTERNAL') || die; + +/** + * Class that represents the exclude checkbox on a grade_grade. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class exclude extends grade_attribute_format implements be_checked { + + /** @var string $name The name of the input */ + public $name = 'exclude'; + + /** + * Is it checked? + * + * @return bool + */ + public function is_checked() { + return $this->grade->is_excluded(); + } + + /** + * Generate the element used to render the UI + * + * @return element + */ + public function determine_format() { + return new checkbox_attribute( + $this->get_name(), + $this->get_label(), + $this->is_checked() + ); + } + + /** + * Get the label for the form input + * + * @return string + */ + public function get_label() { + if (!isset($this->grade->label)) { + $this->grade->label = ''; + } + return $this->grade->label; + } + + /** + * Set the value that was changed in the form. + * + * @param string $value The value to set. + * @return mixed string|bool An error message or false. + */ + public function set($value) { + if (empty($this->grade->id)) { + if (empty($value)) { + return false; + } + + $gradeitem = $this->grade->grade_item; + + // Fill in arbitrary grade to be excluded. + $gradeitem->update_final_grade( + $this->grade->userid, null, 'singleview', null, FORMAT_MOODLE + ); + + $gradeparams = array( + 'userid' => $this->grade->userid, + 'itemid' => $this->grade->itemid + ); + + $this->grade = grade_grade::fetch($gradeparams); + $this->grade->grade_item = $gradeitem; + } + + $state = $value == 0 ? false : true; + + $this->grade->set_excluded($state); + + $this->grade->grade_item->get_parent_category()->force_regrading(); + return false; + } +} diff --git a/grade/report/singleview/classes/local/ui/feedback.php b/grade/report/singleview/classes/local/ui/feedback.php new file mode 100644 index 00000000000..a41e0f20d71 --- /dev/null +++ b/grade/report/singleview/classes/local/ui/feedback.php @@ -0,0 +1,123 @@ +. + +/** + * Class used to render a feedback input box. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace gradereport_singleview\local\ui; + +defined('MOODLE_INTERNAL') || die; + +/** + * Class used to render a feedback input box. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class feedback extends grade_attribute_format implements unique_value, be_disabled { + + /** @var string $name Name of this input */ + public $name = 'feedback'; + + /** + * Get the value for this input. + * + * @return string The value + */ + public function get_value() { + return $this->grade->feedback ? $this->grade->feedback : ''; + } + + /** + * Get the string to use in the label for this input. + * + * @return string The label text + */ + public function get_label() { + if (!isset($this->grade->label)) { + $this->grade->label = ''; + } + return $this->grade->label; + } + + /** + * Determine if this input should be disabled based on the other settings. + * + * @return boolean Should this input be disabled when the page loads. + */ + public function is_disabled() { + $locked = 0; + $gradeitemlocked = 0; + $overridden = 0; + /* Disable editing if grade item or grade score is locked + * if any of these items are set, then we will disable editing + * at some point, we might want to show the reason for the lock + * this code could be simplified, but its more readable for steve's little mind + */ + if (!empty($this->grade->locked)) { + $locked = 1; + } + if (!empty($this->grade->grade_item->locked)) { + $gradeitemlocked = 1; + } + if ($this->grade->grade_item->is_overridable_item() and !$this->grade->is_overridden()) { + $overridden = 1; + } + return ($locked || $gradeitemlocked || $overridden); + } + + /** + * Create a text_attribute for this ui element. + * + * @return text_attribute + */ + public function determine_format() { + return new text_attribute( + $this->get_name(), + $this->get_value(), + $this->get_label(), + $this->is_disabled(), + $this->get_tabindex() + ); + } + + /** + * Update the value for this input. + * + * @param string $value The new feedback value. + * @return string Any error message + */ + public function set($value) { + $finalgrade = false; + $trimmed = trim($value); + if (empty($trimmed)) { + $feedback = null; + } else { + $feedback = $value; + } + + $this->grade->grade_item->update_final_grade( + $this->grade->userid, $finalgrade, 'singleview', + $feedback, FORMAT_MOODLE + ); + } +} diff --git a/grade/report/singleview/classes/local/ui/finalgrade.php b/grade/report/singleview/classes/local/ui/finalgrade.php new file mode 100644 index 00000000000..03b7bf0c43f --- /dev/null +++ b/grade/report/singleview/classes/local/ui/finalgrade.php @@ -0,0 +1,181 @@ +. + +/** + * UI element representing the finalgrade column. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace gradereport_singleview\local\ui; + +defined('MOODLE_INTERNAL') || die; + +/** + * UI element representing the finalgrade column. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class finalgrade extends grade_attribute_format implements unique_value, be_disabled { + + /** @var string $name Name of this input */ + public $name = 'finalgrade'; + + /** + * Get the value for this input. + * + * @return string The value based on the grade_grade. + */ + public function get_value() { + $this->label = $this->grade->grade_item->itemname; + // Manual item raw grade support. + $val = $this->grade->grade_item->is_manual_item() && (!is_null($this->grade->rawgrade)) ? + $this->grade->rawgrade : $this->grade->finalgrade; + + if ($this->grade->grade_item->scaleid) { + return $val ? (int)$val : -1; + } else { + return $val ? format_float($val, $this->grade->grade_item->get_decimals()) : ''; + } + } + + /** + * Get the label for this input. + * + * @return string The label for this form input. + */ + public function get_label() { + if (!isset($this->grade->label)) { + $this->grade->label = ''; + } + return $this->grade->label; + } + + /** + * Is this input field disabled. + * + * @return bool Set disabled on this input or not. + */ + public function is_disabled() { + $locked = 0; + $gradeitemlocked = 0; + $overridden = 0; + + // Disable editing if grade item or grade score is locked + // if any of these items are set, then we will disable editing + // at some point, we might want to show the reason for the lock + // this code could be simplified, but its more readable for steve's little mind. + + if (!empty($this->grade->locked)) { + $locked = 1; + } + if (!empty($this->grade->grade_item->locked)) { + $gradeitemlocked = 1; + } + if ($this->grade->grade_item->is_overridable_item() and !$this->grade->is_overridden()) { + $overridden = 1; + } + return ($locked || $gradeitemlocked || $overridden); + } + + /** + * Create the element for this column. + * + * @return element + */ + public function determine_format() { + if ($this->grade->grade_item->load_scale()) { + $scale = $this->grade->grade_item->load_scale(); + + $options = array(-1 => get_string('nograde')); + + foreach ($scale->scale_items as $i => $name) { + $options[$i + 1] = $name; + } + + return new dropdown_attribute( + $this->get_name(), + $options, + $this->get_value(), + $this->get_label(), + $this->is_disabled(), + $this->get_tabindex() + ); + } else { + return new text_attribute( + $this->get_name(), + $this->get_value(), + $this->get_label(), + $this->is_disabled(), + $this->get_tabindex() + ); + } + } + + /** + * Save the altered value for this field. + * + * @param string $value The new value. + * @return string Any error string + */ + public function set($value) { + global $DB; + + $userid = $this->grade->userid; + $gradeitem = $this->grade->grade_item; + + $feedback = false; + $feedbackformat = false; + if ($gradeitem->gradetype == GRADE_TYPE_SCALE) { + if ($value == -1) { + $finalgrade = null; + } else { + $finalgrade = $value; + } + } else { + $finalgrade = unformat_float($value); + } + + $errorstr = ''; + if ($finalgrade) { + $bounded = $gradeitem->bounded_grade($finalgrade); + if ($bounded > $finalgrade) { + $errorstr = 'lessthanmin'; + } else if ($bounded < $finalgrade) { + $errorstr = 'morethanmax'; + } + } + + if ($errorstr) { + $user = $DB->get_record('user', array('id' => $userid), 'id, firstname, alternatename, lastname'); + $gradestr = new stdClass; + if (!empty($user->alternatename)) { + $gradestr->username = $user->alternatename . ' (' . $user->firstname . ') ' . $user->lastname; + } else { + $gradestr->username = $user->firstname . ' ' . $user->lastname; + } + $gradestr->itemname = $this->grade->grade_item->get_name(); + $errorstr = get_string($errorstr, 'grades', $gradestr); + } + + $gradeitem->update_final_grade($userid, $finalgrade, 'singleview', $feedback, FORMAT_MOODLE); + return $errorstr; + } +} diff --git a/grade/report/singleview/classes/local/ui/grade_attribute_format.php b/grade/report/singleview/classes/local/ui/grade_attribute_format.php new file mode 100644 index 00000000000..dcaebcd576f --- /dev/null +++ b/grade/report/singleview/classes/local/ui/grade_attribute_format.php @@ -0,0 +1,87 @@ +. + +/** + * Abstract class for a form element representing something about a grade_grade. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace gradereport_singleview\local\ui; + +defined('MOODLE_INTERNAL') || die; + +/** + * Abstract class for a form element representing something about a grade_grade. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +abstract class grade_attribute_format extends attribute_format implements unique_name, tabbable { + + /** @var string $name The first part of the name attribute of the form input */ + public $name; + + /** @var string $label The label of the input */ + public $label; + + /** @var grade_grade $grade The grade_grade of the input */ + public $grade; + + /** @var int $tabindex The tabindex of the input */ + public $tabindex; + + /** + * Constructor + * + * @param grade_grade $grade The grade_grade we are editing. + * @param int $tabindex The tabindex for the input. + */ + public function __construct($grade = 0, $tabindex = 1) { + + $this->grade = $grade; + $this->tabindex = $tabindex; + } + + /** + * Get a unique name for this form input + * + * @return string The form input name attribute. + */ + public function get_name() { + return "{$this->name}_{$this->grade->itemid}_{$this->grade->userid}"; + } + + /** + * Get the tabindex for this form input + * + * @return int The tab index + */ + public function get_tabindex() { + return isset($this->tabindex) ? $this->tabindex : null; + } + + /** + * Should be overridden by the child class to save the value returned in this input. + * + * @param string $value The value from the form. + * @return string Any error message + */ + public abstract function set($value); +} diff --git a/grade/report/singleview/classes/local/ui/override.php b/grade/report/singleview/classes/local/ui/override.php new file mode 100644 index 00000000000..64b829251fe --- /dev/null +++ b/grade/report/singleview/classes/local/ui/override.php @@ -0,0 +1,113 @@ +. + +/** + * An override grade checkbox element + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace gradereport_singleview\local\ui; + +defined('MOODLE_INTERNAL') || die; + +/** + * An override grade checkbox element + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class override extends grade_attribute_format implements be_checked, be_disabled { + + /** @var string $name The name for this input */ + public $name = 'override'; + + /** + * Is this input checked + * + * @return bool + */ + public function is_checked() { + return $this->grade->is_overridden(); + } + + /** + * Is this input disabled + * + * @return bool + */ + public function is_disabled() { + $lockedgrade = $lockedgradeitem = 0; + if (!empty($this->grade->locked)) { + $lockedgrade = 1; + } + if (!empty($this->grade->grade_item->locked)) { + $lockedgradeitem = 1; + } + return ($lockedgrade || $lockedgradeitem); + } + + /** + * Get the label for this form element. + * + * @return string + */ + public function get_label() { + if (!isset($this->grade->label)) { + $this->grade->label = ''; + } + return $this->grade->label; + } + + /** + * Generate the element for this form input. + * + * @return element + */ + public function determine_format() { + if (!$this->grade->grade_item->is_overridable_item()) { + return new empty_element(); + } + return new checkbox_attribute( + $this->get_name(), + $this->get_label(), + $this->is_checked(), + null, + $this->is_disabled() + ); + } + + /** + * Save the modified value of this form element. + * + * @param string $value The new value to set + * @return mixed string|false Any error message + */ + public function set($value) { + if (empty($this->grade->id)) { + return false; + } + + $state = $value == 0 ? false : true; + + $this->grade->set_overridden($state); + $this->grade->grade_item->get_parent_category()->force_regrading(); + return false; + } +} diff --git a/grade/report/singleview/classes/local/ui/range.php b/grade/report/singleview/classes/local/ui/range.php new file mode 100644 index 00000000000..aa77f2e56da --- /dev/null +++ b/grade/report/singleview/classes/local/ui/range.php @@ -0,0 +1,59 @@ +. + +/** + * UI element that generates a min/max range (text only). + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace gradereport_singleview\local\ui; + +defined('MOODLE_INTERNAL') || die; + +/** + * UI element that generates a grade_item min/max range (text only). + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class range extends attribute_format { + + /** + * Constructor + * @param grade_item $item The grade item + */ + public function __construct($item) { + $this->item = $item; + } + + /** + * Build this UI element. + * + * @return element + */ + public function determine_format() { + $decimals = $this->item->get_decimals(); + + $min = format_float($this->item->grademin, $decimals); + $max = format_float($this->item->grademax, $decimals); + + return new empty_element("$min - $max"); + } +} diff --git a/grade/report/singleview/classes/local/ui/tabbable.php b/grade/report/singleview/classes/local/ui/tabbable.php new file mode 100644 index 00000000000..d90493150a2 --- /dev/null +++ b/grade/report/singleview/classes/local/ui/tabbable.php @@ -0,0 +1,43 @@ +. + +/** + * The gradebook simple view - UI factory + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace gradereport_singleview\local\ui; + +defined('MOODLE_INTERNAL') || die; + +/** + * This element has a tabindex + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +interface tabbable { + /** + * Get the tabindex for this element + * @return int + */ + public function get_tabindex(); +} + diff --git a/grade/report/singleview/classes/local/ui/text_attribute.php b/grade/report/singleview/classes/local/ui/text_attribute.php new file mode 100644 index 00000000000..8c86349de4d --- /dev/null +++ b/grade/report/singleview/classes/local/ui/text_attribute.php @@ -0,0 +1,108 @@ +. + +/** + * UI element for a text input field. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace gradereport_singleview\local\ui; + +use html_writer; +defined('MOODLE_INTERNAL') || die; + +/** + * UI element for a text input field. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class text_attribute extends element { + + /** @var bool $isdisabled Is this input disabled? */ + private $isdisabled; + + /** @var int $tabindex Tabindex value for this input. */ + private $tabindex; + + /** + * Constructor + * + * @param string $name The input name (the first bit) + * @param string $value The input initial value. + * @param string $label The label for this input field. + * @param bool $isdisabled Is this input disabled. + * @param int $tabindex Tab index for this input. + */ + public function __construct($name, $value, $label, $isdisabled = false, $tabindex = null) { + $this->isdisabled = $isdisabled; + $this->tabindex = $tabindex; + parent::__construct($name, $value, $label); + } + + /** + * Nasty function allowing custom textbox behaviour outside the class. + * @return bool Is this a textbox. + */ + public function is_textbox() { + return true; + } + + /** + * Render the html for this field. + * @return string The HTML. + */ + public function html() { + $attributes = array( + 'type' => 'text', + 'name' => $this->name, + 'value' => $this->value, + 'id' => $this->name + ); + + if (!empty($this->tabindex)) { + $attributes['tabindex'] = $this->tabindex; + } + if ($this->isdisabled) { + $attributes['disabled'] = 'DISABLED'; + } + + $hidden = array( + 'type' => 'hidden', + 'name' => 'old' . $this->name, + 'value' => $this->value + ); + + $label = ''; + if (preg_match("/^feedback/", $this->name)) { + $labeltitle = get_string('feedbackfor', 'gradereport_singleview', $this->label); + $label = html_writer::tag('label', $labeltitle, array('for' => $this->name, 'class' => 'accesshide')); + } else if (preg_match("/^finalgrade/", $this->name)) { + $labeltitle = get_string('gradefor', 'gradereport_singleview', $this->label); + $label = html_writer::tag('label', $labeltitle, array('for' => $this->name, 'class' => 'accesshide')); + } + + return ( + $label . + html_writer::empty_tag('input', $attributes) . + html_writer::empty_tag('input', $hidden) + ); + } +} diff --git a/grade/report/singleview/classes/local/ui/unique_name.php b/grade/report/singleview/classes/local/ui/unique_name.php new file mode 100644 index 00000000000..00b7d7345c0 --- /dev/null +++ b/grade/report/singleview/classes/local/ui/unique_name.php @@ -0,0 +1,43 @@ +. + +/** + * A form element with a name field. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace gradereport_singleview\local\ui; + +defined('MOODLE_INTERNAL') || die; + +/** + * A form element with a name field. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +interface unique_name { + + /** + * Get the name for this form element + * @return string + */ + public function get_name(); +} diff --git a/grade/report/singleview/classes/local/ui/unique_value.php b/grade/report/singleview/classes/local/ui/unique_value.php new file mode 100644 index 00000000000..a3e3ffd2dfa --- /dev/null +++ b/grade/report/singleview/classes/local/ui/unique_value.php @@ -0,0 +1,42 @@ +. + +/** + * The gradebook simple view - UI factory + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace gradereport_singleview\local\ui; + +defined('MOODLE_INTERNAL') || die; + +/** + * Simple interface for an item with a value. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +interface unique_value { + /** + * Get the value for this item. + * @return string + */ + public function get_value(); +} diff --git a/grade/report/singleview/db/access.php b/grade/report/singleview/db/access.php new file mode 100755 index 00000000000..2d7326145bf --- /dev/null +++ b/grade/report/singleview/db/access.php @@ -0,0 +1,36 @@ +. + +/** + * The gradebook simple view - Database file + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +$capabilities = array( + + 'gradereport/singleview:view' => array( + 'riskbitmask' => RISK_PERSONAL, + 'captype' => 'read', + 'contextlevel' => CONTEXT_COURSE, + 'archetypes' => array( + 'editingteacher' => CAP_ALLOW, + 'manager' => CAP_ALLOW, + ) + ) +); diff --git a/grade/report/singleview/index.php b/grade/report/singleview/index.php new file mode 100755 index 00000000000..2b347fa89f9 --- /dev/null +++ b/grade/report/singleview/index.php @@ -0,0 +1,187 @@ +. + +/** + * Displays the Single view + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once('../../../config.php'); +require_once($CFG->dirroot.'/lib/gradelib.php'); +require_once($CFG->dirroot.'/grade/lib.php'); +require_once($CFG->dirroot.'/grade/report/singleview/lib.php'); + +$courseid = required_param('id', PARAM_INT); +$groupid = optional_param('group', null, PARAM_INT); + +// Making this work with profile reports. +$userid = optional_param('userid', null, PARAM_INT); + +$defaulttype = $userid ? 'user' : 'select'; + +$itemid = optional_param('itemid', $userid, PARAM_INT); +$itemtype = optional_param('item', $defaulttype, PARAM_TEXT); +$page = optional_param('page', 0, PARAM_INT); +$perpage = optional_param('perpage', 100, PARAM_INT); + +$courseparams = array('id' => $courseid); +$PAGE->set_url(new moodle_url('/grade/report/singleview/index.php', $courseparams)); + +if (!$course = $DB->get_record('course', $courseparams)) { + print_error('nocourseid'); +} + +require_login($course); +$PAGE->set_pagelayout('report'); + +if (!in_array($itemtype, gradereport_singleview::valid_screens())) { + print_error('notvalid', 'gradereport_singleview', '', $itemtype); +} + +$context = context_course::instance($course->id); + +// This is the normal requirements. +require_capability('gradereport/singleview:view', $context); +require_capability('moodle/grade:viewall', $context); +require_capability('moodle/grade:edit', $context); + +$gpr = new grade_plugin_return(array( + 'type' => 'report', + 'plugin' => 'singleview', + 'courseid' => $courseid +)); + +// Last selected report session tracking. +if (!isset($USER->grade_last_report)) { + $USER->grade_last_report = array(); +} +$USER->grade_last_report[$course->id] = 'singleview'; + +// First make sure we have proper final grades - +// this must be done before constructing of the grade tree. +grade_regrade_final_grades($courseid); + +$report = new gradereport_singleview( + $courseid, $gpr, $context, + $itemtype, $itemid, $groupid +); + +$reportname = $report->screen->heading(); + +$pluginname = get_string('pluginname', 'gradereport_singleview'); + +$pageparams = array( + 'id' => $courseid, + 'itemid' => $itemid, + 'item' => $itemtype, + 'userid' => $userid, + 'group' => $groupid, + 'page' => $page, + 'perpage' => $perpage +); + +$currentpage = new moodle_url('/grade/report/singleview/index.php', $pageparams); + +if ($data = data_submitted()) { + $warnings = $report->process_data($data); + + if (empty($warnings)) { + redirect($currentpage); + die(); + } +} + +print_grade_page_head($course->id, 'report', 'singleview', $reportname); + +$graderrightnav = $graderleftnav = null; + +$options = $report->screen->options(); + +if (!empty($options)) { + + $optionkeys = array_keys($options); + $optionitemid = array_shift($optionkeys); + + $relreport = new gradereport_singleview( + $courseid, $gpr, $context, + $report->screen->item_type(), $optionitemid, $groupid + ); + $reloptions = $relreport->screen->options(); + $reloptionssorting = array_keys($relreport->screen->options()); + + $i = array_search($itemid, $reloptionssorting); + $navparams = array('item' => $itemtype, 'id' => $courseid, 'group' => $groupid); + if ($i > 0) { + $navparams['itemid'] = $reloptionssorting[$i - 1]; + $link = new moodle_url('/grade/report/singleview/index.php', $navparams); + $navprev = html_writer::link($link, $OUTPUT->larrow() . ' ' . $reloptions[$reloptionssorting[$i - 1]]); + $graderleftnav = html_writer::tag('small', $navprev, array('class' => 'itemnav previtem')); + } + if ($i < count($reloptionssorting) - 1) { + $navparams['itemid'] = $reloptionssorting[$i + 1]; + $link = new moodle_url('/grade/report/singleview/index.php', $navparams); + $navnext = html_writer::link($link, $reloptions[$reloptionssorting[$i + 1]] . ' ' . $OUTPUT->rarrow()); + $graderrightnav = html_writer::tag('small', $navnext, array('class' => 'itemnav nextitem')); + } +} + +if (!is_null($graderleftnav)) { + echo $graderleftnav; +} +if (!is_null($graderrightnav)) { + echo $graderrightnav; +} + +if ($report->screen->supports_paging()) { + echo $report->screen->pager(); +} + +if ($report->screen->display_group_selector()) { + echo $report->group_selector; +} + +if (!empty($warnings)) { + foreach ($warnings as $warning) { + echo $OUTPUT->notification($warning); + } +} + +echo $report->output(); + +if ($report->screen->supports_paging()) { + echo $report->screen->pager(); +} + +if (!is_null($graderleftnav)) { + echo $graderleftnav; +} +if (!is_null($graderrightnav)) { + echo $graderrightnav; +} + +$event = \gradereport_singleview\event\grade_report_viewed::create( + array( + 'context' => $context, + 'courseid' => $courseid, + 'relateduserid' => $USER->id, + ) +); +$event->trigger(); + +echo $OUTPUT->footer(); diff --git a/grade/report/singleview/js/singleview.js b/grade/report/singleview/js/singleview.js new file mode 100644 index 00000000000..02d7be898c9 --- /dev/null +++ b/grade/report/singleview/js/singleview.js @@ -0,0 +1,41 @@ +M.gradereport_singleview = {}; + +M.gradereport_singleview.init = function(Y) { + // Make toggle links + Y.all('.include').each(function(link) { + var type = link.getAttribute('class').split(" ")[2]; + + var toggle = function(checked) { + return function(input) { + input.getDOMNode().checked = checked; + Y.Event.simulate(input.getDOMNode(), 'change'); + }; + }; + + link.on('click', function() { + Y.all('input[name^=' + type + ']').each(toggle(link.hasClass('all'))); + return false; + }); + }); + + // Override Toggle + Y.all('input[name^=override_]').each(function(input) { + input.on('change', function() { + var checked = input.getDOMNode().checked; + var names = input.getAttribute('name').split("_"); + + var itemid = names[1]; + var userid = names[2]; + + var interest = '_' + itemid + '_' + userid; + + Y.all('input[name$=' + interest + ']').filter('input[type=text]').each(function(text) { + text.getDOMNode().disabled = !checked; + }); + // deal with scales that are not text... UCSB + Y.all('select[name$=' + interest + ']').each(function(select) { + select.getDOMNode().disabled = !checked; + }); + }); + }); +}; diff --git a/grade/report/singleview/lang/en/gradereport_singleview.php b/grade/report/singleview/lang/en/gradereport_singleview.php new file mode 100755 index 00000000000..a4d354524da --- /dev/null +++ b/grade/report/singleview/lang/en/gradereport_singleview.php @@ -0,0 +1,47 @@ +. + +/** + * Strings for Single view + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +// General Strings. +$string['all_grades'] = 'All grades'; +$string['assessmentname'] = 'Assessment Name'; +$string['blanks'] = 'Empty grades'; +$string['bulkappliesto'] = 'for'; +$string['bulkinsertgrade'] = 'Bulk insert'; +$string['bulkfor'] = 'Grades for {$a}'; +$string['exclude'] = 'Exclude'; +$string['excludeall'] = 'Exclude all grades'; +$string['excludefor'] = 'Exclude for {$a}'; +$string['excludenone'] = 'Exclude no grades'; +$string['feedbackfor'] = 'Feedback for {$a}'; +$string['filtergrades'] = 'Show grades for {$a}.'; +$string['gradefor'] = 'Grade for {$a}'; +$string['noscreens'] = 'Could not find a suitable single view screen.'; +$string['gradeitemcannotbeoverridden'] = 'This grade item cannot be overridden.'; +$string['notvalid'] = 'Not a valid Single view screen: {$a}'; +$string['override'] = 'Override'; +$string['overrideall'] = 'Override all grades'; +$string['overridefor'] = 'Override for {$a}'; +$string['overridenone'] = 'Override no grades'; +$string['pluginname'] = 'Single view'; +$string['singleview:view'] = 'View report'; diff --git a/grade/report/singleview/lib.php b/grade/report/singleview/lib.php new file mode 100755 index 00000000000..5382c6cec50 --- /dev/null +++ b/grade/report/singleview/lib.php @@ -0,0 +1,112 @@ +. + +/** + * Base lib class for singleview functionality. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die; + +require_once($CFG->dirroot . '/grade/report/lib.php'); + +/** + * This class is the main class that must be implemented by a grade report plugin. + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class gradereport_singleview extends grade_report { + + /** + * Return the list of valid screens, used to validate the input. + * + * @return array List of screens. + */ + public static function valid_screens() { + // This is a list of all the known classes representing a screen in this plugin. + return array('user', 'select', 'grade'); + } + + /** + * Process data from a form submission. Delegated to the current screen. + * + * @param array $data The data from the form + * @return array List of warnings + */ + public function process_data($data) { + if (has_capability('moodle/grade:manage', $this->context)) { + return $this->screen->process($data); + } + } + + /** + * Unused - abstract function declared in the parent class. + * + * @param string $target + * @param string $action + */ + public function process_action($target, $action) { + } + + /** + * Constructor for this report. Creates the appropriate screen class based on itemtype. + * + * @param int $courseid The course id. + * @param object $gpr grade plugin return tracking object + * @param context_course $context + * @param string $itemtype Should be user, select or grade + * @param int $itemid The id of the user or grade item + * @param int $groupid (optional) The current groupid. + */ + public function __construct($courseid, $gpr, $context, $itemtype, $itemid, $groupid=null) { + parent::__construct($courseid, $gpr, $context); + + $screenclass = "\\gradereport_singleview\\local\\screen\\${itemtype}"; + + $this->screen = new $screenclass($courseid, $itemid, $groupid); + + // Load custom or predifined js. + $this->screen->js(); + + $base = '/grade/report/singleview/index.php'; + + $idparams = array('id' => $courseid); + + $this->baseurl = new moodle_url($base, $idparams); + + $this->pbarurl = new moodle_url($base, $idparams + array( + 'item' => $itemtype, + 'itemid' => $itemid + )); + + $this->setup_groups(); + } + + /** + * Build the html for the screen. + * @return string HTML to display + */ + public function output() { + global $OUTPUT; + return $OUTPUT->box($this->screen->html()); + } +} + diff --git a/grade/report/singleview/styles.css b/grade/report/singleview/styles.css new file mode 100644 index 00000000000..3c2cca7bc43 --- /dev/null +++ b/grade/report/singleview/styles.css @@ -0,0 +1,47 @@ +.path-grade-report-singleview div.generalbox { + margin: 0 20px 15px 20px; +} + +.path-grade-report-singleview div.generalbox div.singleselect form div { + text-align: center; +} + +.path-grade-report-singleview div.generalbox table.generaltable { + margin: 0 auto 15px auto; +} + +.path-grade-report-singleview div.generalbox form div { + text-align: center; +} + +.path-grade-report-singleview .singleview_buttons { + padding: 10px 0; +} + +.path-grade-report-singleview div.generalbox h2 { + text-align: center; +} + +.path-grade-report-singleview input[name^="finalgrade"] { + width: 50px; +} + +.path-grade-report-singleview #region-main h2, .paging{ + text-align: center; +} + +.path-grade-report-singleview itemnav.previtem { + float:left; +} +.path-grade-report-singleview itemnav.previtem { + float:left; +} +.path-grade-report-singleview.dir-rtl small.previtem { + float:right; +} +.path-grade-report-singleview small.nextitem { + float:right; +} +.path-grade-report-singleview.dir-rtl small.nextitem { + float:left; +} diff --git a/grade/report/singleview/tests/behat/singleview.feature b/grade/report/singleview/tests/behat/singleview.feature new file mode 100644 index 00000000000..6ea012463fd --- /dev/null +++ b/grade/report/singleview/tests/behat/singleview.feature @@ -0,0 +1,88 @@ +@core @core_grades @gradereport_singleview +Feature: We can use Single view + As a teacher + In order to view and edit grades + For users and activities for a course. + + Background: + Given the following "courses" exist: + | fullname | shortname | category | + | Course 1 | C1 | 0 | + And the following "users" exist: + | username | firstname | lastname | email | idnumber | alternatename | + | teacher1 | Teacher | 1 | teacher1@asd.com | t1 | teacherbro | + | student1 | Student | 1 | student1@asd.com | s1 | studentbro | + | student2 | Student | 2 | student1@asd.com | s2 | studentjo | + | student3 | Student | 3 | student1@asd.com | s3 | studentlo | + | student4 | Student | 4 | student1@asd.com | s4 | studentawesemo | + And the following "course enrolments" exist: + | user | course | role | + | teacher1 | C1 | editingteacher | + | student1 | C1 | student | + | student2 | C1 | student | + | student3 | C1 | student | + | student4 | C1 | student | + And the following "grade categories" exist: + | fullname | course | + | Sub category 1 | C1| + | Sub category 2 | C1| + And the following "activities" exist: + | activity | course | idnumber | name | intro | grade | + | assign | C1 | a1 | Test assignment one | Submit something! | 300 | + | assign | C1 | a2 | Test assignment two | Submit something! | 100 | + | assign | C1 | a3 | Test assignment three | Submit something! | 150 | + | assign | C1 | a4 | Test assignment four | Submit nothing! | 150 | + And I log in as "teacher1" + And I follow "Course 1" + And I follow "Grades" + + @javascript + Scenario: I can update grades, add feedback and exclude grades. + Given I click on "Single view" "option" + And I click on "studentawesemo (Student) 4" "option" + And I click on "Override for Test assignment one" "checkbox" + When I set the following fields to these values: + | Grade for Test assignment one | 10.00 | + | Feedback for Test assignment one | test data | + And I click on "Exclude for Test assignment four" "checkbox" + And I press "Update" + Then the following should exist in the "user-grades" table: + | Test assignment four | + | excluded | + And the following should exist in the "user-grades" table: + | Test assignment one | + | 10.00 | + And I click on "Single view for Test assignment three" "link" + And I click on "Override for studentbro (Student) 1" "checkbox" + And I set the following fields to these values: + | Grade for studentbro (Student) 1 | 12.05 | + | Feedback for studentbro (Student) 1 | test data2 | + And I click on "Exclude for studentjo (Student) 2" "checkbox" + And I press "Update" + And the following should exist in the "user-grades" table: + | Test assignment three | + | 12.05 | + | Excluded | + + Scenario: Single view links work on grade report. + Given I follow "Single view for Test assignment one" + Then I should see "Test assignment one" + Then I follow "Grader report" + And I follow "Single view for Student 1" + Then I should see "Student 1" + + @javascript + Scenario: Navigation works in the Single view. + Given I click on "Single view" "option" + Then I click on "studentbro (Student) 1" "option" + Then I should see "Student 1" + And I follow "studentjo (Student) 2" + Then I should see "Student 2" + And I follow "studentbro (Student) 1" + Then I should see "Student 1" + And I click on "Show grades for Test assignment four" "link" + Then I should see "Test assignment four" + And I follow "Test assignment three" + Then I should see "Test assignment three" + And I follow "Test assignment four" + Then I should see "Test assignment four" diff --git a/grade/report/singleview/version.php b/grade/report/singleview/version.php new file mode 100755 index 00000000000..cf792d4e6b4 --- /dev/null +++ b/grade/report/singleview/version.php @@ -0,0 +1,27 @@ +. + +/** + * Standard version file + * + * @package gradereport_singleview + * @copyright 2014 Moodle Pty Ltd (http://moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +$plugin->component = 'gradereport_singleview'; // Full name of the plugin (used for diagnostics). +$plugin->version = 2013081002; +$plugin->requires = 2010090501; diff --git a/lang/en/grades.php b/lang/en/grades.php index 16212a9b8b5..5fb642629c2 100644 --- a/lang/en/grades.php +++ b/lang/en/grades.php @@ -309,6 +309,7 @@ $string['gradereport'] = 'Grade report'; $string['graderreport'] = 'Grader report'; $string['grades'] = 'Grades'; $string['gradesforuser'] = 'Grades for {$a->user}'; +$string['singleview'] = 'Single view for {$a}'; $string['gradesonly'] = 'Grades only'; $string['gradessettings'] = 'Grade settings'; $string['gradetype'] = 'Grade type'; @@ -655,6 +656,7 @@ $string['showrank_help'] = 'Show the position of the student in relation to the $string['showuserimage'] = 'Show user profile images'; $string['showuserimage_help'] = 'Whether to show the user\'s profile image next to the name in the grader report.'; $string['showverbose'] = 'Show {$a->category} {$a->itemmodule} {$a->itemname}'; +$string['simpleview'] = 'Single view'; $string['sitewide'] = 'Site-wide'; $string['sort'] = 'sort'; $string['sortasc'] = 'Sort in ascending order'; diff --git a/lib/classes/plugin_manager.php b/lib/classes/plugin_manager.php index 818832dc85d..a6c409ea316 100644 --- a/lib/classes/plugin_manager.php +++ b/lib/classes/plugin_manager.php @@ -1038,7 +1038,7 @@ class core_plugin_manager { ), 'gradereport' => array( - 'grader', 'history', 'outcomes', 'overview', 'user' + 'grader', 'history', 'outcomes', 'overview', 'user', 'singleview' ), 'gradingform' => array(