From 46795afcbdd7fe43de03afb6b2b87eb5c21ea691 Mon Sep 17 00:00:00 2001 From: tjhunt Date: Tue, 20 Jan 2009 10:11:23 +0000 Subject: [PATCH] question bank: MDL-17871 refactor the code that displays particular information about each question into classes. Work in progress. Most required classes are writted, but nothing uses them yet, and I have not dealt with column headers yet. --- question/editlib.php | 391 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 391 insertions(+) diff --git a/question/editlib.php b/question/editlib.php index c21b1eabbb..7c02d41217 100644 --- a/question/editlib.php +++ b/question/editlib.php @@ -127,6 +127,397 @@ function question_can_delete_cat($todelete) { } } +abstract class question_bank_column_base { + + /** + * Output the column header cell. + * @param integer $currentsort 0 for none. 1 for normal sort, -1 for reverse sort. + */ + public function display_header($currentsort) { + // TODO. + } + + /** + * Output this column. + * @param object $question the row from the $question table, augmented with extra information. + * @param string $rowclasses CSS class names that should be applied to this row of output. + */ + public function display($question, $rowclasses) { + $this->display_start($question, $rowclasses); + $this->display_content($question, $rowclasses); + $this->display_end($question, $rowclasses); + } + + protected function display_start($question, $rowclasses) { + echo ''; + } + + /** + * @param object $question the row from the $question table, augmented with extra information. + * @param string $rowclasses CSS class names that should be applied to this row of output. + * @return string CSS class names that should be applied to this column. Should normally be + * a simple work relating the the column type like 'questionname'. + */ + abstract protected function get_css_classes($question, $rowclasses); + + /** + * Output the contents of this column. + * @param object $question the row from the $question table, augmented with extra information. + * @param string $rowclasses CSS class names that should be applied to this row of output. + */ + abstract protected function display_content($question, $rowclasses); + + protected function display_end($question, $rowclasses) { + echo ''; + } + + /** + * Return an array 'table_alias' => 'JOIN clause' to bring in any data that + * this column required. + * + * The return values for all the columns will be checked. It is OK if two + * columns join in the same table with the same alias and identical JOIN clauses. + * If to columns try to use the same alias with different joins, you get an error. + * The only table included by default is the question table, which is aliased to 'q'. + * + * @return array 'table_alias' => 'JOIN clause' + */ + public function get_extra_joins() { + return array(); + } + + /** + * @return array fields required. use table alias 'q' for the question table, or one of the + * ones from get_extra_joins. Every field requested must specify a table prefix. + */ + public function get_required_fields() { + return array(); + } + + /** + * Can this column be sorted on? You can return either: + * + false for no (the default), + * + true for yes, or + * + an array of subnames to sort on. E.g. 'firstname', 'lastname'. + * If this method returns true, sort_expression must be implemented. + * @return mixed + */ + public function is_sortable() { + return false; + } + + /** + * Helper method for building sort clauses. + * @param boolean $reverse whether the normal direction should be reversed. + * @param string $normaldir 'ASC' or 'DESC' + * @return string 'ASC' or 'DESC' + */ + protected function sortorder($reverse, $normaldir = 'ASC') { + switch ($normaldir) { + case 'ASC': + if ($reverse) { + return ' DESC'; + } else { + return ' ASC'; + } + case 'DESC': + if ($reverse) { + return ' ASC'; + } else { + return ' DESC'; + } + default: + throw new coding_exception('$normaldir should be ASC or DESC.'); + } + } + + /** + * @param $reverse Whether to sort in the reverse of the default sort order. + * @param $subsort if is_sortable returns an array of subnames, then this will be + * one of those. Otherwise will be empty. + * @return string some SQL to go in the order by clause. + */ + public function sort_expression($reverse, $subsort) { + throw new coding_exception('A subclass of question_bank_column_base must implement sort_expression if is_sortable returns true.'); + } +} + +/** + * A column with a checkbox for each question with name q{questionid}. + */ +class question_bank_checkbox_column extends question_bank_column_base { + protected $strselect; + + public function __construct() { + parent::__construct(); + $this->strselect = get_string('select', 'quiz'); + } + + protected function get_css_classes($question, $rowclasses) { + return 'checkbox'; + } + + protected function display_content($question, $rowclasses) { + echo ''; + } + + public function get_required_fields() { + return array('q.id'); + } +} + +/** + * A column type for the name of the question type. + */ +class question_bank_question_type_column extends question_bank_column_base { + protected function get_css_classes($question, $rowclasses) { + return 'qtype'; + } + + protected function display_content($question, $rowclasses) { + echo print_question_icon($question); + } + + public function get_required_fields() { + return array('q.qtype'); + } + + public function is_sortable() { + return true; + } + + public function sort_expression($reverse, $subsort) { + return 'q.qtype' . $this->sortorder($reverse); + } +} + +/** + * A column type for the name of the question name. + */ +class question_bank_question_name_column extends question_bank_column_base { + protected function get_css_classes($question, $rowclasses) { + return 'questionname'; + } + + protected function display_content($question, $rowclasses) { + echo format_string($question->name); + } + + public function get_required_fields() { + return array('q.name'); + } + + public function is_sortable() { + return true; + } + + public function sort_expression($reverse, $subsort) { + return 'q.name' . $this->sortorder($reverse); + } +} + +/** + * A column type for the name of the question creator. + */ +class question_bank_creator_name_column extends question_bank_column_base { + protected function get_css_classes($question, $rowclasses) { + return 'creatorname'; + } + + protected function display_content($question, $rowclasses) { + if (!empty($question->creatorfirstname) && !empty($question->creatorlastname)) { + $u = new stdClass; + $u->firstname = $question->creatorfirstname; + $u->lastname = $question->creatorlastname; + echo fullname($u); + } + } + + public function get_extra_joins() { + return array('uc' => 'LEFT JOIN {user} uc ON uc.id = q.createdby'); + } + + public function get_required_fields() { + return array('uc.firstname AS creatorfirstname', 'uc.lastname AS creatorlastname'); + } + + public function is_sortable() { + return array('firstname', 'lastname'); + } + + public function sort_expression($reverse, $subsort) { + if (in_array($subsort, $this->is_sortable())) { + return 'uc.' . $subsort . $this->sortorder($reverse); + } else { + throw new coding_exception('Unexpected $subsort type.'); + } + } +} + +/** + * A column type for the name of the question last modifier. + */ +class question_bank_modifier_name_column extends question_bank_column_base { + protected function get_css_classes($question, $rowclasses) { + return 'modifiername'; + } + + protected function display_content($question, $rowclasses) { + if (!empty($question->modifierfirstname) && !empty($question->modifierlastname)) { + $u = new stdClass; + $u->firstname = $question->modifierfirstname; + $u->lastname = $question->modifierlastname; + echo fullname($u); + } + } + + public function get_extra_joins() { + return array('um' => 'LEFT JOIN {user} um ON um.id = q.modifiedby'); + } + + public function get_required_fields() { + return array('um.firstname AS modifierfirstname', 'um.lastname AS modifierlastname'); + } + + public function is_sortable() { + return array('firstname', 'lastname'); + } + + public function sort_expression($reverse, $subsort) { + if (in_array($subsort, $this->is_sortable())) { + return 'um.' . $subsort . $this->sortorder($reverse); + } else { + throw new coding_exception('Unexpected $subsort type.'); + } + } +} + +/** + * A base class for actions that are an icon that lets you manipulate the question in some way. + */ +abstract class question_bank_action_column_base extends question_bank_column_base { + protected function print_icon($icon, $title, $url) { + global $CFG; + echo ' + ' . $title . ''; + } + + public function get_required_fields() { + return array('q.id'); + } +} + +class question_bank_edit_action_column extends question_bank_action_column_base { + protected $stredit; + protected $strview; + protected $questionurl; + + public function __construct($questionurl) { + parent::__construct(); + $this->questionurl = $questionurl; + $this->stredit = get_string('edit'); + $this->strview = get_string('view'); + } + + protected function get_css_classes($question, $rowclasses) { + return 'editaction'; + } + + protected function display_content($question, $rowclasses) { + if (question_has_capability_on($question, 'edit') || + question_has_capability_on($question, 'move')) { + $this->print_icon('edit', $this->stredit, $this->questionurl->out(false, array('id' => $question->id))); + } else { + $this->print_icon('info', $this->strview, $this->questionurl->out(false, array('id' => $question->id))); + } + } +} + +class question_bank_preview_action_column extends question_bank_action_column_base { + protected $strpreview; + protected $quizorcourseid; + + public function __construct($quizorcourseid) { + parent::__construct(); + $this->quizorcourseid = $quizorcourseid; + $this->stredit = get_string('preview'); + } + + protected function get_css_classes($question, $rowclasses) { + return 'previewaction'; + } + + protected function display_content($question, $rowclasses) { + if (question_has_capability_on($question, 'use')) { + link_to_popup_window('/question/preview.php?id=' . $question->id . + $this->quizorcourseid, 'questionpreview', + ' ' . $this->strpreview . '', + 0, 0, $this->strpreview, QUESTION_PREVIEW_POPUP_OPTIONS); + } + } + + public function get_required_fields() { + return array('q.id'); + } +} + +class question_bank_move_action_column extends question_bank_action_column_base { + protected $strmove; + protected $questionurl; + + public function __construct($questionurl) { + parent::__construct(); + $this->questionurl = $questionurl; + $this->strmove = get_string('move'); + } + + protected function get_css_classes($question, $rowclasses) { + return 'editaction'; + } + + protected function display_content($question, $rowclasses) { + if (question_has_capability_on($question, 'move')) { + $this->print_icon('move', $this->strmove, $this->questionurl->out(false, array('id' => $question->id))); + } + } +} + +/** + * action to delete (or hide) a question, or restore a previously hidden question. + */ +class question_bank_delete_action_column extends question_bank_action_column_base { + protected $strdelete; + protected $strrestore; + protected $questionurl; + + public function __construct($questionurl) { + parent::__construct(); + $this->questionurl = $questionurl; + $this->strdelete = get_string('delete'); + $this->strrestore = get_string('restore'); + } + + protected function get_css_classes($question, $rowclasses) { + return 'deleteaction'; + } + + protected function display_content($question, $rowclasses) { + if (question_has_capability_on($question, 'edit')) { + if ($question->hidden) { + $this->print_icon('restore', $this->strrestore, $pageurl->out(false, array('unhide' => $question->id))); + } else { + $this->print_icon('restore', $this->strrestore, + $pageurl->out(false, array('deleteselected' => $question->id, 'q' . $question->id => 1))); + } + } + } + + public function get_required_fields() { + return array('q.id', 'q.hidden'); + } +} + /** * This class prints a view of the question bank, including * + Some controls to allow users to to select what is displayed. -- 2.39.5