MDL-66816 question bank: replace row of edit icons with an Edit menu
authorTim Hunt <T.J.Hunt@open.ac.uk>
Wed, 2 Oct 2019 12:51:48 +0000 (13:51 +0100)
committerTim Hunt <T.J.Hunt@open.ac.uk>
Fri, 18 Oct 2019 13:27:50 +0000 (14:27 +0100)
26 files changed:
config-dist.php
lang/en/question.php
lib/outputcomponents.php
mod/quiz/styles.css
question/classes/bank/action_column_base.php
question/classes/bank/checkbox_column.php
question/classes/bank/column_base.php
question/classes/bank/copy_action_column.php
question/classes/bank/creator_name_column.php
question/classes/bank/delete_action_column.php
question/classes/bank/edit_action_column.php
question/classes/bank/edit_menu_column.php [new file with mode: 0644]
question/classes/bank/export_xml_action_column.php [new file with mode: 0644]
question/classes/bank/menu_action_column_base.php [new file with mode: 0644]
question/classes/bank/menuable_action.php [new file with mode: 0644]
question/classes/bank/modifier_name_column.php
question/classes/bank/preview_action_column.php
question/classes/bank/question_name_column.php
question/classes/bank/question_text_row.php
question/classes/bank/question_type_column.php
question/classes/bank/row_base.php
question/classes/bank/tags_action_column.php
question/classes/bank/view.php
question/tests/behat/behat_question.php
question/upgrade.txt
version.php

index 4928d85..a16bebe 100644 (file)
@@ -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
 //
index 8da8aa4..94cc9df 100644 (file)
@@ -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.
 
index dd0834b..3a450b8 100644 (file)
@@ -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;
index ca30770..6fb2035 100644 (file)
@@ -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;
 }
index 865a88b..ab48c81 100644 (file)
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
+/**
+ * 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() {
index 4c0e6ca..9b2f628 100644 (file)
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
+/**
+ * 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 {
 
index 58f892f..8bacb78 100644 (file)
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
+ * 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.
index 9d0d890..4c15c1e 100644 (file)
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
+/**
+ * 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];
     }
 }
index 9110a85..f217286 100644 (file)
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
+/**
+ * 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';
index ad17bb3..3e5419b 100644 (file)
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
+/**
+ * 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];
         }
     }
 
index 4045969..a68b2f2 100644 (file)
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
+/**
+ * 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 (file)
index 0000000..8ee79bb
--- /dev/null
@@ -0,0 +1,91 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * 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 (file)
index 0000000..9711827
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * 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 (file)
index 0000000..5a0b52d
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * 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 (file)
index 0000000..0a0e76f
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * 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;
+}
index f3b326d..29d7645 100644 (file)
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
+/**
+ * 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() {
index a81a83c..384399e 100644 (file)
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
+/**
+ * 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']);
+    }
 }
index 05d18c6..528a5ca 100644 (file)
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
+/**
+ * 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;
index cf9df12..b05b0a7 100644 (file)
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
+/**
+ * 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;
index da7c839..064ce95 100644 (file)
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
+/**
+ * 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() {
index a091c95..457899e 100644 (file)
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
+/**
+ * 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() {
index 47446fa..6899d2d 100644 (file)
  * @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 <simey@moodle.com>
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @copyright 2018 Simey Lameze <simey@moodle.com>
+ * @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);
     }
 }
index 5bfe5d3..82c0f47 100644 (file)
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 
-namespace core_question\bank;
-
-use core_question\bank\search\condition;
-
 /**
- * Functions used to show question editing interface
+ * Class to print a view of the question bank.
  *
- * @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();
+
+use core_question\bank\search\condition;
+
 
 /**
  * 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();
     }
 
     /**
index 3a0d775..6a7c097 100644 (file)
@@ -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",
index cb8daf9..6f0475d 100644 (file)
@@ -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
index e52672b..297ec28 100644 (file)
@@ -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.