diff --git a/config-dist.php b/config-dist.php index 4928d85a93e..a16bebef298 100644 --- a/config-dist.php +++ b/config-dist.php @@ -639,9 +639,10 @@ $CFG->admin = 'admin'; // to check the latest default in question/classes/bank/view.php before setting this. // // $CFG->questionbankcolumns = 'checkbox_column,question_type_column,' -// . 'question_name_idnumber_tags_column,tags_action_column,edit_action_column,' -// . 'copy_action_column,preview_action_column,delete_action_column,' -// . 'creator_name_column,modifier_name_column'; +// . 'question_name_idnumber_tags_column,' +// . 'tags_action_column,edit_action_column,copy_action_column,' +// . 'preview_action_column,delete_action_column,export_xml_action_column,' +// . 'creator_name_column,modifier_name_column,edit_menu_column'; // // Forum summary report // diff --git a/lang/en/question.php b/lang/en/question.php index 8da8aa4e891..94cc9df9879 100644 --- a/lang/en/question.php +++ b/lang/en/question.php @@ -157,6 +157,7 @@ $string['eventquestionsexported'] = 'Questions exported'; $string['eventquestionsimported'] = 'Questions imported'; $string['eventquestionupdated'] = 'Question updated'; $string['export'] = 'Export'; +$string['exportasxml'] = 'Export as Moodle XML'; $string['exportcategory'] = 'Export category'; $string['exportcategory_help'] = 'This setting determines the category from which the exported questions will be taken. diff --git a/lib/outputcomponents.php b/lib/outputcomponents.php index dd0834b3a31..3a450b83ce9 100644 --- a/lib/outputcomponents.php +++ b/lib/outputcomponents.php @@ -4200,32 +4200,32 @@ class action_menu implements renderable, templatable { /** * An icon to use for the toggling the secondary menu (dropdown). - * @var actionicon + * @var pix_icon */ public $actionicon; /** * Any text to use for the toggling the secondary menu (dropdown). - * @var menutrigger + * @var string */ public $menutrigger = ''; /** * Any extra classes for toggling to the secondary menu. - * @var triggerextraclasses + * @var string */ public $triggerextraclasses = ''; /** * Place the action menu before all other actions. - * @var prioritise + * @var bool */ public $prioritise = false; /** * Constructs the action menu with the given items. * - * @param array $actions An array of actions. + * @param array $actions An array of actions (action_menu_link|pix_icon|string). */ public function __construct(array $actions = array()) { static $initialised = 0; @@ -4259,7 +4259,6 @@ class action_menu implements renderable, templatable { * Sets the label for the menu trigger. * * @param string $label The text - * @return null */ public function set_action_label($label) { $this->actionlabel = $label; @@ -4270,7 +4269,6 @@ class action_menu implements renderable, templatable { * * @param string $trigger The text * @param string $extraclasses Extra classes to style the secondary menu toggle. - * @return null */ public function set_menu_trigger($trigger, $extraclasses = '') { $this->menutrigger = $trigger; diff --git a/mod/quiz/styles.css b/mod/quiz/styles.css index ca307702967..6fb2035c811 100644 --- a/mod/quiz/styles.css +++ b/mod/quiz/styles.css @@ -986,7 +986,6 @@ table.quizreviewsummary td.cell { table#categoryquestions { width: 100%; - overflow: hidden; table-layout: fixed; } @@ -1002,6 +1001,10 @@ table#categoryquestions { padding: 0; } +#categoryquestions .editmenu { + width: 5em; +} + #categoryquestions .qtype { text-align: center; } diff --git a/question/classes/bank/action_column_base.php b/question/classes/bank/action_column_base.php index 865a88b2fdf..ab48c8150f4 100644 --- a/question/classes/bank/action_column_base.php +++ b/question/classes/bank/action_column_base.php @@ -14,15 +14,24 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +/** + * A base class for actions that are an icon that lets you manipulate the question in some way. + * + * @package core_question + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + namespace core_question\bank; +defined('MOODLE_INTERNAL') || die(); + /** * A base class for actions that are an icon that lets you manipulate the question in some way. * - * @copyright 2009 Tim Hunt - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ - abstract class action_column_base extends column_base { protected function get_title() { diff --git a/question/classes/bank/checkbox_column.php b/question/classes/bank/checkbox_column.php index 4c0e6ca5d6c..9b2f628b4fe 100644 --- a/question/classes/bank/checkbox_column.php +++ b/question/classes/bank/checkbox_column.php @@ -14,15 +14,24 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +/** + * A column with a checkbox for each question with name q{questionid}. + * + * @package core_question + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ namespace core_question\bank; +defined('MOODLE_INTERNAL') || die(); use core\output\checkbox_toggleall; + /** * A column with a checkbox for each question with name q{questionid}. * - * @copyright 2009 Tim Hunt - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class checkbox_column extends column_base { diff --git a/question/classes/bank/column_base.php b/question/classes/bank/column_base.php index 58f892f8829..8bacb784a38 100644 --- a/question/classes/bank/column_base.php +++ b/question/classes/bank/column_base.php @@ -15,23 +15,23 @@ // along with Moodle. If not, see . /** + * Base class for representing a column in a {@link question_bank_view}. * - * @package moodlecore - * @subpackage questionbank - * @copyright 1999 onwards Martin Dougiamas and others {@link http://moodle.com} - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @package core_question + * @copyright 1999 onwards Martin Dougiamas and others {@link http://moodle.com} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ - namespace core_question\bank; +defined('MOODLE_INTERNAL') || die(); + /** * Base class for representing a column in a {@link question_bank_view}. * - * @copyright 2009 Tim Hunt - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ - abstract class column_base { /** * @var view $qbank the question bank view we are helping to render. diff --git a/question/classes/bank/copy_action_column.php b/question/classes/bank/copy_action_column.php index 9d0d890e64c..4c15c1e77fd 100644 --- a/question/classes/bank/copy_action_column.php +++ b/question/classes/bank/copy_action_column.php @@ -14,16 +14,25 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +/** + * Question bank column for the duplicate action icon. + * + * @package core_question + * @copyright 2013 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + namespace core_question\bank; +defined('MOODLE_INTERNAL') || die(); + /** * Question bank column for the duplicate action icon. * - * @copyright 2013 The Open University - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @copyright 2013 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ - -class copy_action_column extends action_column_base { +class copy_action_column extends menu_action_column_base { /** @var string avoids repeated calls to get_string('duplicate'). */ protected $strcopy; @@ -36,13 +45,14 @@ class copy_action_column extends action_column_base { return 'copyaction'; } - protected function display_content($question, $rowclasses) { + protected function get_url_icon_and_label(\stdClass $question): array { // To copy a question, you need permission to add a question in the same // category as the existing question, and ability to access the details of // the question being copied. if (question_has_capability_on($question, 'add') && (question_has_capability_on($question, 'edit') || question_has_capability_on($question, 'view'))) { - $this->print_icon('t/copy', $this->strcopy, $this->qbank->copy_question_url($question->id)); + return [$this->qbank->copy_question_moodle_url($question->id), 't/copy', $this->strcopy]; } + return [null, null, null]; } } diff --git a/question/classes/bank/creator_name_column.php b/question/classes/bank/creator_name_column.php index 9110a857f6c..f217286d7b9 100644 --- a/question/classes/bank/creator_name_column.php +++ b/question/classes/bank/creator_name_column.php @@ -14,16 +14,24 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +/** + * A column type for the name of the question creator. + * + * @package core_question + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ namespace core_question\bank; +defined('MOODLE_INTERNAL') || die(); + /** * A column type for the name of the question creator. * - * @copyright 2009 Tim Hunt - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ - class creator_name_column extends column_base { public function get_name() { return 'creatorname'; diff --git a/question/classes/bank/delete_action_column.php b/question/classes/bank/delete_action_column.php index ad17bb3e55c..3e5419b1139 100644 --- a/question/classes/bank/delete_action_column.php +++ b/question/classes/bank/delete_action_column.php @@ -14,16 +14,25 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +/** + * action to delete (or hide) a question, or restore a previously hidden question. + * + * @package core_question + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + namespace core_question\bank; +defined('MOODLE_INTERNAL') || die(); + /** * action to delete (or hide) a question, or restore a previously hidden question. * - * @copyright 2009 Tim Hunt - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ - -class delete_action_column extends action_column_base { +class delete_action_column extends menu_action_column_base { protected $strdelete; protected $strrestore; @@ -37,16 +46,30 @@ class delete_action_column extends action_column_base { return 'deleteaction'; } - protected function display_content($question, $rowclasses) { - if (question_has_capability_on($question, 'edit')) { - if ($question->hidden) { - $url = new \moodle_url($this->qbank->base_url(), array('unhide' => $question->id, 'sesskey' => sesskey())); - $this->print_icon('t/restore', $this->strrestore, $url); - } else { - $url = new \moodle_url($this->qbank->base_url(), array('deleteselected' => $question->id, 'q' . $question->id => 1, - 'sesskey' => sesskey())); - $this->print_icon('t/delete', $this->strdelete, $url); - } + /** + * Work out the info required to display this action, if appropriate. + * + * If the action is not appropriate to this question, return [null, null, null]. + * + * Otherwise return an array with three elements: + * moodel_url $url the URL to perform the action. + * string $icon the icon name. E.g. 't/delete'. + * string $label the label to display. + * + * @param object $question the row from the $question table, augmented with extra information. + * @return array [$url, $label, $icon] as above. + */ + protected function get_url_icon_and_label(\stdClass $question): array { + if (!question_has_capability_on($question, 'edit')) { + return [null, null, null]; + } + if ($question->hidden) { + $url = new \moodle_url($this->qbank->base_url(), array('unhide' => $question->id, 'sesskey' => sesskey())); + return [$url, 't/restore', $this->strrestore]; + } else { + $url = new \moodle_url($this->qbank->base_url(), array('deleteselected' => $question->id, 'q' . $question->id => 1, + 'sesskey' => sesskey())); + return [$url, 't/delete', $this->strdelete]; } } diff --git a/question/classes/bank/edit_action_column.php b/question/classes/bank/edit_action_column.php index 404596919c0..a68b2f297cf 100644 --- a/question/classes/bank/edit_action_column.php +++ b/question/classes/bank/edit_action_column.php @@ -14,21 +14,31 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +/** + * Base class for question bank columns that just contain an action icon. + * + * @package core_question + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + namespace core_question\bank; +defined('MOODLE_INTERNAL') || die(); + /** * Base class for question bank columns that just contain an action icon. * - * @copyright 2009 Tim Hunt - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class edit_action_column extends action_column_base { +class edit_action_column extends menu_action_column_base { protected $stredit; protected $strview; public function init() { parent::init(); - $this->stredit = get_string('edit'); + $this->stredit = get_string('editquestion', 'question'); $this->strview = get_string('view'); } @@ -36,11 +46,13 @@ class edit_action_column extends action_column_base { return 'editaction'; } - protected function display_content($question, $rowclasses) { + protected function get_url_icon_and_label(\stdClass $question): array { if (question_has_capability_on($question, 'edit')) { - $this->print_icon('t/edit', $this->stredit, $this->qbank->edit_question_url($question->id)); + return [$this->qbank->edit_question_moodle_url($question->id), 't/edit', $this->stredit]; } else if (question_has_capability_on($question, 'view')) { - $this->print_icon('i/info', $this->strview, $this->qbank->edit_question_url($question->id)); + return [$this->qbank->edit_question_moodle_url($question->id), 'i/info', $this->strview]; + } else { + return [null, null, null]; } } } diff --git a/question/classes/bank/edit_menu_column.php b/question/classes/bank/edit_menu_column.php new file mode 100644 index 00000000000..8ee79bbccd8 --- /dev/null +++ b/question/classes/bank/edit_menu_column.php @@ -0,0 +1,91 @@ +. + +/** + * A question bank column which gathers together all the actions into a menu. + * + * @package core_question + * @copyright 2019 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_question\bank; +defined('MOODLE_INTERNAL') || die(); + + +/** + * A question bank column which gathers together all the actions into a menu. + * + * This question bank column, if added to the question bank, will + * replace all of the other columns which implement the + * {@link menuable_action} interface and replace them with a single + * column containing an Edit menu. + * + * @copyright 2019 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class edit_menu_column extends column_base { + /** + * @var menuable_action[] + */ + protected $actions; + + /** + * Set up the list of actions that should be shown in the menu. + * + * This takes a list of column object (the list from a question + * bank view). It extracts all the ones that should go in the menu + * and stores them for later use. Then it returns the remaining columns. + * + * @param column_base[] $allcolumns a set of columns. + * @return column_base[] the non-action columns from the set. + */ + public function claim_menuable_columns($allcolumns) { + $remainingcolumns = []; + foreach ($allcolumns as $key => $column) { + if ($column instanceof menuable_action) { + $this->actions[$key] = $column; + } else { + $remainingcolumns[$key] = $column; + } + } + return $remainingcolumns; + } + + protected function get_title() { + return get_string('actions'); + } + + public function get_name() { + return 'editmenu'; + } + + protected function display_content($question, $rowclasses) { + global $OUTPUT; + + $menu = new \action_menu(); + $menu->set_menu_trigger(get_string('edit')); + $menu->set_alignment(\action_menu::TL, \action_menu::BL); + foreach ($this->actions as $actioncolumn) { + $action = $actioncolumn->get_action_menu_link($question); + if ($action) { + $menu->add($action); + } + } + + echo $OUTPUT->render($menu); + } +} diff --git a/question/classes/bank/export_xml_action_column.php b/question/classes/bank/export_xml_action_column.php new file mode 100644 index 00000000000..97118276260 --- /dev/null +++ b/question/classes/bank/export_xml_action_column.php @@ -0,0 +1,56 @@ +. + +/** + * Question bank column export the question in Moodle XML format. + * + * @package core_question + * @copyright 2019 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_question\bank; +defined('MOODLE_INTERNAL') || die(); + + +/** + * Question bank column export the question in Moodle XML format. + * + * @copyright 2019 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class export_xml_action_column extends menu_action_column_base { + /** @var string avoids repeated calls to get_string('duplicate'). */ + protected $strexportasxml; + + public function init() { + parent::init(); + $this->strexportasxml = get_string('exportasxml', 'question'); + } + + public function get_name() { + return 'exportasxmlaction'; + } + + protected function get_url_icon_and_label(\stdClass $question): array { + if (!question_has_capability_on($question, 'view')) { + return [null, null, null]; + } + + return [question_get_export_single_question_url($question), + 't/download', $this->strexportasxml]; + } +} diff --git a/question/classes/bank/menu_action_column_base.php b/question/classes/bank/menu_action_column_base.php new file mode 100644 index 00000000000..5a0b52da58b --- /dev/null +++ b/question/classes/bank/menu_action_column_base.php @@ -0,0 +1,70 @@ +. + +/** + * Base class to make it easier to implement actions that are menuable_actions. + * + * @package core_question + * @copyright 2019 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_question\bank; +defined('MOODLE_INTERNAL') || die(); + + +/** + * Base class to make it easier to implement actions that are menuable_actions. + * + * Use this class if your action is simple (defined by just a URL, label and icon). + * If your action is not simple enough to fit into the pattern that this + * class implements, then you will have to implement the menuable_action + * interface yourself. + * + * @copyright 2019 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +abstract class menu_action_column_base extends action_column_base implements menuable_action { + + /** + * Get the information required to display this action either as a menu item or a separate action column. + * + * If this action cannot apply to this question (e.g. because the user does not have + * permission, then return [null, null, null]. + * + * @param \stdClass $question the row from the $question table, augmented with extra information. + * @return array with three elements. + * $url - the URL to perform the action. + * $icon - the icon for this action. E.g. 't/delete'. + * $label - text label to display in the UI (either in the menu, or as a tool-tip on the icon) + */ + abstract protected function get_url_icon_and_label(\stdClass $question): array; + + protected function display_content($question, $rowclasses) { + [$url, $icon, $label] = $this->get_url_icon_and_label($question); + if ($url) { + $this->print_icon($icon, $label, $url); + } + } + + public function get_action_menu_link(\stdClass $question): ?\action_menu_link { + [$url, $icon, $label] = $this->get_url_icon_and_label($question); + if (!$url) { + return null; + } + return new \action_menu_link_secondary($url, new \pix_icon($icon, ''), $label); + } +} diff --git a/question/classes/bank/menuable_action.php b/question/classes/bank/menuable_action.php new file mode 100644 index 00000000000..0a0e76f2fff --- /dev/null +++ b/question/classes/bank/menuable_action.php @@ -0,0 +1,56 @@ +. + +/** + * Interface to indicate that a question bank column can go in the action menu. + * + * @package core_question + * @copyright 2019 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_question\bank; +defined('MOODLE_INTERNAL') || die(); + + +/** + * Interface to indicate that a question bank column can go in the action menu. + * + * If a question bank column implements this interface, and if the {@link edit_menu_column} + * is present in the question bank view, then the 'column' will be shown as an entry in the + * edit menu instead of as a separate column. + * + * Probably most columns that want to implement this will be subclasses of + * {@link action_column_base}, and most such columns should probably implement + * this interface. + * + * If your column is a simple action, you can probably save duplicated code by + * using the base class action_column_menuable as an easy way to implement both + * action_column_base and this interface. + * + * @copyright 2019 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +interface menuable_action { + + /** + * Return the appropriate action menu link, or null if it does not apply to this question. + * + * @param \stdClass $question data about the question being displayed in this row. + * @return \action_menu_link|null the action, if applicable to this question. + */ + public function get_action_menu_link(\stdClass $question): ?\action_menu_link; +} diff --git a/question/classes/bank/modifier_name_column.php b/question/classes/bank/modifier_name_column.php index f3b326df53e..29d7645c885 100644 --- a/question/classes/bank/modifier_name_column.php +++ b/question/classes/bank/modifier_name_column.php @@ -14,14 +14,23 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +/** + * A column type for the name of the question last modifier. + * + * @package core_question + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ namespace core_question\bank; +defined('MOODLE_INTERNAL') || die(); + /** * A column type for the name of the question last modifier. * - * @copyright 2009 Tim Hunt - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class modifier_name_column extends column_base { public function get_name() { diff --git a/question/classes/bank/preview_action_column.php b/question/classes/bank/preview_action_column.php index a81a83ca5bd..384399e735f 100644 --- a/question/classes/bank/preview_action_column.php +++ b/question/classes/bank/preview_action_column.php @@ -14,15 +14,35 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +/** + * Question bank columns for the preview action icon. + * + * @package core_question + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + namespace core_question\bank; +defined('MOODLE_INTERNAL') || die(); + /** * Question bank columns for the preview action icon. * - * @copyright 2009 Tim Hunt - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class preview_action_column extends action_column_base { +class preview_action_column extends action_column_base implements menuable_action { + /** + * @var string store this lang string for performance. + */ + protected $strpreview; + + public function init() { + parent::init(); + $this->strpreview = get_string('preview'); + } + public function get_name() { return 'previewaction'; } @@ -34,4 +54,15 @@ class preview_action_column extends action_column_base { $question->id, $this->qbank->get_most_specific_context(), false); } } + + public function get_action_menu_link(\stdClass $question): ?\action_menu_link { + if (!question_has_capability_on($question, 'use')) { + return null; + } + + $context = $this->qbank->get_most_specific_context(); + $url = question_preview_url($question->id, null, null, null, null, $context); + return new \action_menu_link_secondary($url, new \pix_icon('t/preview', ''), + $this->strpreview, ['target' => 'questionpreview']); + } } diff --git a/question/classes/bank/question_name_column.php b/question/classes/bank/question_name_column.php index 05d18c6df76..528a5ca236d 100644 --- a/question/classes/bank/question_name_column.php +++ b/question/classes/bank/question_name_column.php @@ -14,13 +14,23 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +/** + * A column type for the name of the question name. + * + * @package core_question + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + namespace core_question\bank; +defined('MOODLE_INTERNAL') || die(); + /** * A column type for the name of the question name. * - * @copyright 2009 Tim Hunt - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class question_name_column extends column_base { protected $checkboxespresent = null; diff --git a/question/classes/bank/question_text_row.php b/question/classes/bank/question_text_row.php index cf9df126bff..b05b0a7c099 100644 --- a/question/classes/bank/question_text_row.php +++ b/question/classes/bank/question_text_row.php @@ -14,14 +14,23 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +/** + * A column type for the name of the question name. + * + * @package core_question + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ namespace core_question\bank; +defined('MOODLE_INTERNAL') || die(); + /** * A column type for the name of the question name. * - * @copyright 2009 Tim Hunt - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class question_text_row extends row_base { protected $formatoptions; diff --git a/question/classes/bank/question_type_column.php b/question/classes/bank/question_type_column.php index da7c83928ba..064ce95ecbc 100644 --- a/question/classes/bank/question_type_column.php +++ b/question/classes/bank/question_type_column.php @@ -14,13 +14,23 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +/** + * A column type for the name of the question type. + * + * @package core_question + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + namespace core_question\bank; +defined('MOODLE_INTERNAL') || die(); + /** * A column type for the name of the question type. * - * @copyright 2009 Tim Hunt - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class question_type_column extends column_base { public function get_name() { diff --git a/question/classes/bank/row_base.php b/question/classes/bank/row_base.php index a091c95baa4..457899ed51c 100644 --- a/question/classes/bank/row_base.php +++ b/question/classes/bank/row_base.php @@ -14,13 +14,23 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +/** + * Base class for 'columns' that are actually displayed as a row following the main question row. + * + * @package core_question + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + namespace core_question\bank; +defined('MOODLE_INTERNAL') || die(); + /** * Base class for 'columns' that are actually displayed as a row following the main question row. * - * @copyright 2009 Tim Hunt - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ abstract class row_base extends column_base { public function is_extra_row() { diff --git a/question/classes/bank/tags_action_column.php b/question/classes/bank/tags_action_column.php index 47446fa66fd..6899d2d2ac6 100644 --- a/question/classes/bank/tags_action_column.php +++ b/question/classes/bank/tags_action_column.php @@ -22,17 +22,25 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace core_question\bank; - defined('MOODLE_INTERNAL') || die(); + /** * Action to add and remove tags to questions. * - * @package core_question - * @copyright 2018 Simey Lameze - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @copyright 2018 Simey Lameze + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class tags_action_column extends action_column_base { +class tags_action_column extends action_column_base implements menuable_action { + /** + * @var string store this lang string for performance. + */ + protected $managetags; + + public function init() { + parent::init(); + $this->managetags = get_string('managetags', 'tag'); + } /** * Return the name for this column. @@ -50,36 +58,45 @@ class tags_action_column extends action_column_base { * @param string $rowclasses */ protected function display_content($question, $rowclasses) { + global $OUTPUT; + if (\core_tag_tag::is_enabled('core_question', 'question') && question_has_capability_on($question, 'view')) { - $cantag = question_has_capability_on($question, 'tag'); - $qbank = $this->qbank; - $url = new \moodle_url($qbank->edit_question_url($question->id)); - $editingcontext = $qbank->get_most_specific_context(); - - $this->print_tag_icon($question->id, $url, $cantag, $editingcontext->id); + [$url, $attributes] = $this->get_link_url_and_attributes($question); + echo \html_writer::link($url, $OUTPUT->pix_icon('t/tags', + $this->managetags), $attributes); } } /** - * Build and print the tags icon. + * Helper used by display_content and get_action_menu_link. * - * @param int $id The question ID. - * @param \moodle_url $url Editing question url. - * @param bool $cantag Whether the user can tag questions or not. - * @param int $contextid Question category context ID. + * @param object $question the row from the $question table, augmented with extra information. + * @return array with two elements, \moodle_url and + * an array or data $attributes needed to make the JavaScript work. */ - protected function print_tag_icon($id, \moodle_url $url, $cantag, $contextid) { - global $OUTPUT; + protected function get_link_url_and_attributes($question) { + $url = new \moodle_url($this->qbank->edit_question_url($question->id)); - $params = [ - 'data-action' => 'edittags', - 'data-cantag' => $cantag, - 'data-contextid' => $contextid, - 'data-questionid' => $id + $attributes = [ + 'data-action' => 'edittags', + 'data-cantag' => question_has_capability_on($question, 'tag'), + 'data-contextid' => $this->qbank->get_most_specific_context()->id, + 'data-questionid' => $question->id ]; - echo \html_writer::link($url, $OUTPUT->pix_icon('t/tags', get_string('managetags', 'tag')), $params); + return [$url, $attributes]; + } + + public function get_action_menu_link(\stdClass $question): ?\action_menu_link { + if (!\core_tag_tag::is_enabled('core_question', 'question') || + !question_has_capability_on($question, 'view')) { + return null; + } + + [$url, $attributes] = $this->get_link_url_and_attributes($question); + return new \action_menu_link_secondary($url, new \pix_icon('t/tags', ''), + $this->managetags, $attributes); } } diff --git a/question/classes/bank/view.php b/question/classes/bank/view.php index 5bfe5d3ef22..82c0f477b5c 100644 --- a/question/classes/bank/view.php +++ b/question/classes/bank/view.php @@ -15,19 +15,19 @@ // along with Moodle. If not, see . +/** + * Class to print a view of the question bank. + * + * @package core_question + * @copyright 1999 onwards Martin Dougiamas and others {@link http://moodle.com} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + namespace core_question\bank; +defined('MOODLE_INTERNAL') || die(); use core_question\bank\search\condition; -/** - * Functions used to show question editing interface - * - * @package moodlecore - * @subpackage questionbank - * @copyright 1999 onwards Martin Dougiamas and others {@link http://moodle.com} - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - /** * This class prints a view of the question bank, including @@ -46,8 +46,8 @@ use core_question\bank\search\condition; * and sorted in the right order. * + outputting table headers. * - * @copyright 2009 Tim Hunt - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class view { const MAX_SORTS = 3; @@ -177,9 +177,10 @@ class view { if (empty($CFG->questionbankcolumns)) { $questionbankcolumns = array('checkbox_column', 'question_type_column', - 'question_name_idnumber_tags_column', 'tags_action_column', 'edit_action_column', - 'copy_action_column', 'preview_action_column', 'delete_action_column', - 'creator_name_column', 'modifier_name_column'); + 'question_name_idnumber_tags_column', + 'edit_action_column', 'copy_action_column', 'tags_action_column', + 'preview_action_column', 'delete_action_column', 'export_xml_action_column', + 'creator_name_column', 'modifier_name_column', 'edit_menu_column'); } else { $questionbankcolumns = explode(',', $CFG->questionbankcolumns); } @@ -237,6 +238,15 @@ class view { * @param string $heading The name of column that is set as heading */ protected function init_columns($wanted, $heading = '') { + // If we are using the edit menu column, allow it to absorb all the actions. + foreach ($wanted as $column) { + if ($column instanceof edit_menu_column) { + $wanted = $column->claim_menuable_columns($wanted); + break; + } + } + + // Now split columns into real columns and rows. $this->visiblecolumns = array(); $this->extrarows = array(); foreach ($wanted as $column) { @@ -482,8 +492,34 @@ class view { return $this->baseurl; } + /** + * Get the URL for editing a question as a {@link \moodle_url}. + * + * @param int $questionid the question id. + * @return \moodle_url the URL, HTML-escaped. + */ + public function edit_question_moodle_url($questionid) { + return new \moodle_url($this->editquestionurl, ['id' => $questionid]); + } + + /** + * Get the URL for editing a question as a HTML-escaped string. + * + * @param int $questionid the question id. + * @return string the URL, HTML-escaped. + */ public function edit_question_url($questionid) { - return $this->editquestionurl->out(true, array('id' => $questionid)); + return $this->edit_question_moodle_url($questionid)->out(); + } + + /** + * Get the URL for duplicating a question as a {@link \moodle_url}. + * + * @param int $questionid the question id. + * @return \moodle_url the URL. + */ + public function copy_question_moodle_url($questionid) { + return new \moodle_url($this->editquestionurl, ['id' => $questionid, 'makecopy' => 1]); } /** @@ -492,7 +528,7 @@ class view { * @return string the URL, HTML-escaped. */ public function copy_question_url($questionid) { - return $this->editquestionurl->out(true, array('id' => $questionid, 'makecopy' => 1)); + return $this->copy_question_moodle_url($questionid)->out(); } /** diff --git a/question/tests/behat/behat_question.php b/question/tests/behat/behat_question.php index 3a0d7758728..6a7c097fe54 100644 --- a/question/tests/behat/behat_question.php +++ b/question/tests/behat/behat_question.php @@ -95,9 +95,9 @@ class behat_question extends behat_question_base { * @param string $questionname the question name. */ public function i_action_the_question($action, $questionname) { - if ($action == 'Edit question') { - $action = 'Edit'; - } + // Open the menu. + $this->execute("behat_general::i_click_on_in_the", + [get_string('edit'), 'link', $questionname, 'table_row']); // Click the action from the menu. $this->execute("behat_general::i_click_on_in_the", diff --git a/question/upgrade.txt b/question/upgrade.txt index cb8daf9eccd..6f0475d91b3 100644 --- a/question/upgrade.txt +++ b/question/upgrade.txt @@ -8,6 +8,18 @@ equivalent changes in your customised version. The old column question_name_colu has been replaced by question_name_idnumber_tags_column. The old question_name_column still exists, so it is safe to continue using it. +There is a new question bank column edit_menu_column which displays all actions +in a drop-down menu, instead of as separate icons. This is now used by default. +Specifically, it gathers all other columns which implement the new interface +menuable_action. If you have made a custom subclasses of action_column_base, +you probably want to implement the new interface. If your column is a simple action, +the easiest way to do this will be to subclass menu_action_column_base. If your action +is more complex, and does not follow the simple pattern that menu_action_column_base +uses, then you will need to implement menuable_action yourself. The commit for +MDL-66816 updates all the core action columns. Looking at that change should make +it clearly the changes you need to make to your columns. + + === 3.7 === The code for the is_valid_number function that was duplicated in the diff --git a/version.php b/version.php index e52672b9693..297ec2862dc 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2019101800.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2019101800.01; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes.