From 10b7badd61e567c77f068532b51b99c3aef37288 Mon Sep 17 00:00:00 2001
From: Peter Dias <peter@moodle.com>
Date: Tue, 15 Jun 2021 10:13:07 +0800
Subject: [PATCH] MDL-71914 mod_glossary: Add tertiary nav in glossary

---
 .../classes/local/views/secondary.php         |  47 +++
 mod/glossary/classes/output/renderer.php      |  41 +++
 .../classes/output/standard_action_bar.php    | 285 ++++++++++++++++++
 mod/glossary/edit.php                         |   1 +
 mod/glossary/export.php                       |   1 +
 mod/glossary/import.php                       |   1 +
 mod/glossary/lang/en/deprecated.txt           |   1 +
 mod/glossary/lang/en/glossary.php             |   8 +-
 mod/glossary/lib.php                          |  33 +-
 mod/glossary/tabs.php                         | 101 ++-----
 .../templates/standard_action_menu.mustache   | 168 +++++++++++
 mod/glossary/version.php                      |   2 +-
 mod/glossary/view.php                         | 102 ++-----
 13 files changed, 621 insertions(+), 170 deletions(-)
 create mode 100644 mod/glossary/classes/local/views/secondary.php
 create mode 100644 mod/glossary/classes/output/renderer.php
 create mode 100644 mod/glossary/classes/output/standard_action_bar.php
 create mode 100644 mod/glossary/lang/en/deprecated.txt
 create mode 100644 mod/glossary/templates/standard_action_menu.mustache

diff --git a/mod/glossary/classes/local/views/secondary.php b/mod/glossary/classes/local/views/secondary.php
new file mode 100644
index 00000000000..83e2edd46e8
--- /dev/null
+++ b/mod/glossary/classes/local/views/secondary.php
@@ -0,0 +1,47 @@
+<?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/>.
+
+namespace mod_glossary\local\views;
+
+use core\navigation\views\secondary as core_secondary;
+
+/**
+ * Class secondary_navigation_view.
+ *
+ * Custom implementation for a plugin.
+ *
+ * @package     mod_glossary
+ * @category    navigation
+ * @copyright   2021 onwards Peter Dias
+ * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class secondary extends core_secondary {
+    /**
+     * Define a custom secondary nav order/view
+     *
+     * @return array
+     */
+    protected function get_default_module_mapping(): array {
+        return [
+            self::TYPE_SETTING => [
+                'modedit' => 1,
+            ],
+            self::TYPE_CUSTOM => [
+                'pendingapproval' => 2,
+            ],
+        ];
+    }
+}
diff --git a/mod/glossary/classes/output/renderer.php b/mod/glossary/classes/output/renderer.php
new file mode 100644
index 00000000000..701ecdbd3ba
--- /dev/null
+++ b/mod/glossary/classes/output/renderer.php
@@ -0,0 +1,41 @@
+<?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/>.
+
+namespace mod_glossary\output;
+
+use plugin_renderer_base;
+
+/**
+ * Class actionbar - Display the action bar
+ *
+ * @package   mod_glossary
+ * @copyright 2021 Peter Dias
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class renderer extends plugin_renderer_base {
+    /**
+     * Render the glossary tertiary nav
+     *
+     * @param standard_action_bar $actionmenu
+     * @return bool|string
+     * @throws \moodle_exception
+     */
+    public function main_action_bar(standard_action_bar $actionmenu) {
+        $context = $actionmenu->export_for_template($this);
+
+        return $this->render_from_template('mod_glossary/standard_action_menu', $context);
+    }
+}
diff --git a/mod/glossary/classes/output/standard_action_bar.php b/mod/glossary/classes/output/standard_action_bar.php
new file mode 100644
index 00000000000..699b6ac782d
--- /dev/null
+++ b/mod/glossary/classes/output/standard_action_bar.php
@@ -0,0 +1,285 @@
+<?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/>.
+
+namespace mod_glossary\output;
+
+use moodle_url;
+use context_module;
+use renderable;
+use renderer_base;
+use single_button;
+use templatable;
+use url_select;
+
+/**
+ * Class standard_action_bar - Display the action bar
+ *
+ * @package   mod_glossary
+ * @copyright 2021 Peter Dias
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class standard_action_bar implements renderable, templatable {
+    /** @var object $cm The course module. */
+    private $cm;
+    /** @var string $mode The type of view. */
+    private $mode;
+    /** @var string $hook The term, entry, cat, etc... to look for based on mode. */
+    private $hook;
+    /** @var string $sortkey Sorted view: CREATION | UPDATE | FIRSTNAME | LASTNAME. */
+    private $sortkey;
+    /** @var string $sortorder The sort order (ASC or DESC). */
+    private $sortorder;
+    /** @var int $offset Entries to bypass (for paging purposes). */
+    private $offset;
+    /** @var int $pagelimit The page to resume with. */
+    private $pagelimit;
+    /** @var int $context The context of the glossary. */
+    private $context;
+    /** @var object $module The glossary record . */
+    private $module;
+    /** @var int $fullsearch Full search (concept and definition) when searching. */
+    private $fullsearch;
+    /** @var object $displayformat Override of the glossary display format. */
+    private $displayformat;
+    /** @var string $tab Browsing entries by categories. */
+    private $tab;
+
+    /**
+     * standard_action_bar constructor.
+     *
+     * @param object $cm
+     * @param object $module
+     * @param object $displayformat
+     * @param string $mode
+     * @param string $hook
+     * @param string $sortkey
+     * @param string $sortorder
+     * @param int $offset
+     * @param int $pagelimit
+     * @param int $fullsearch
+     * @param string $tab
+     * @param string $defaulttab
+     * @throws \coding_exception
+     */
+    public function __construct(object $cm, object $module, object $displayformat, string $mode, string $hook,
+            string $sortkey, string $sortorder, int $offset, int $pagelimit, int $fullsearch,
+            string $tab, string $defaulttab) {
+        $this->cm = $cm;
+        $this->module = $module;
+        $this->displayformat = $displayformat;
+        $this->mode = $mode;
+        $this->tab = $tab;
+        $this->hook = $hook;
+        $this->sortkey = $sortkey;
+        $this->sortorder = $sortorder;
+        $this->offset = $offset;
+        $this->pagelimit = $pagelimit;
+        $this->fullsearch = $fullsearch;
+        $this->context = context_module::instance($this->cm->id);
+
+        if (!has_capability('mod/glossary:approve', $this->context) && $this->tab == GLOSSARY_APPROVAL_VIEW) {
+            // Non-teachers going to approval view go to defaulttab.
+            $this->tab = $defaulttab;
+        }
+    }
+
+    /**
+     * Export the action bar
+     *
+     * @param renderer_base $output
+     * @return array
+     */
+    public function export_for_template(renderer_base $output) {
+        return [
+            'addnewbutton' => $this->create_add_button($output),
+            'searchbox' => $this->create_search_box(),
+            'tools' => $this->get_additional_tools($output),
+            'tabjumps' => $this->generate_tab_jumps($output)
+        ];
+    }
+
+    /**
+     * Render the search box with the checkbox
+     *
+     * @return array
+     */
+    private function create_search_box(): array {
+        global $OUTPUT;
+        $fullsearchchecked = false;
+        if ($this->fullsearch || $this->mode != 'search') {
+            $fullsearchchecked = true;
+        }
+
+        $check = [
+            'name' => 'fullsearch',
+            'id' => 'fullsearch',
+            'value' => '1',
+            'checked' => $fullsearchchecked,
+            'label' => get_string("searchindefinition", "glossary"),
+        ];
+
+        $checkbox = $OUTPUT->render_from_template('core/checkbox', $check);
+
+        $hiddenfields = [
+            (object) ['name' => 'id', 'value' => $this->cm->id],
+            (object) ['name' => 'mode', 'value' => 'search'],
+        ];
+        $data = [
+            'action' => new moodle_url('/mod/glossary/view.php'),
+            'hiddenfields' => $hiddenfields,
+            'otherfields' => $checkbox,
+            'inputname' => 'hook',
+            'query' => ($this->mode == 'search') ? s($this->hook) : '',
+            'searchstring' => get_string('search'),
+        ];
+
+        return $data;
+    }
+
+    /**
+     * Render the add entry button
+     *
+     * @param renderer_base $output
+     * @return \stdClass
+     */
+    private function create_add_button(renderer_base $output): \stdClass {
+        if (!has_capability('mod/glossary:write', $this->context)) {
+            return '';
+        }
+        $btn = new single_button(new moodle_url('/mod/glossary/edit.php', ['cmid' => $this->cm->id]),
+            get_string('addsingleentry', 'glossary'), 'post', true);
+
+        return $btn->export_for_template($output);
+    }
+
+    /**
+     * Render the additional tools required by the glossary
+     *
+     * @param renderer_base $output
+     * @return array
+     */
+    private function get_additional_tools(renderer_base $output): array {
+        global $USER, $CFG;
+        $items = [];
+        $buttons = [];
+
+        if (has_capability('mod/glossary:import', $this->context)) {
+            $items['button'] = new single_button(
+                new moodle_url('/mod/glossary/import.php', ['id' => $this->cm->id]),
+                get_string('importentries', 'glossary')
+            );
+        }
+
+        if (has_capability('mod/glossary:export', $this->context)) {
+            $url = new moodle_url('/mod/glossary/export.php', [
+                'id' => $this->cm->id,
+                'mode' => $this->mode,
+                'hook' => $this->hook
+            ]);
+            $buttons[get_string('export', 'glossary')] = $url->out(false);
+        }
+
+        if (has_capability('mod/glossary:manageentries', $this->context) or $this->module->allowprintview) {
+            $params = array(
+                'id'        => $this->cm->id,
+                'mode'      => $this->mode,
+                'hook'      => $this->hook,
+                'sortkey'   => $this->sortkey,
+                'sortorder' => $this->sortorder,
+                'offset'    => $this->offset,
+                'pagelimit' => $this->pagelimit
+            );
+            $printurl = new moodle_url('/mod/glossary/print.php', $params);
+            $buttons[get_string('printerfriendly', 'glossary')] = $printurl->out(false);
+        }
+
+        if (!empty($CFG->enablerssfeeds) && !empty($CFG->glossary_enablerssfeeds)
+                && $this->module->rsstype && $this->module->rssarticles
+                && has_capability('mod/glossary:view', $this->context)) {
+            require_once("$CFG->libdir/rsslib.php");
+            $string = get_string('rssfeed', 'glossary');
+            $url = new moodle_url(rss_get_url($this->context->id, $USER->id, 'mod_glossary', $this->cm->instance));
+            $buttons[$string] = $url->out(false);
+        }
+
+        foreach ($items as $key => $value) {
+            $items[$key] = $value->export_for_template($output);
+        }
+
+        if ($buttons) {
+            foreach ($buttons as $index => $value) {
+                $items['select']['options'][] = [
+                    'url' => $value,
+                    'string' => $index
+                ];
+            }
+        }
+
+        return $items;
+    }
+
+    /**
+     * Generate a url select to match any types of glossary views
+     *
+     * @param renderer_base $output
+     * @return \stdClass|null
+     */
+    private function generate_tab_jumps(renderer_base $output) {
+        $tabs = glossary_get_visible_tabs($this->displayformat);
+        $validtabs = [
+            GLOSSARY_STANDARD => [
+                'mode' => 'letter',
+                'descriptor' => 'standardview'
+            ],
+            GLOSSARY_CATEGORY => [
+                'mode' => 'cat',
+                'descriptor' => 'categoryview'
+            ],
+            GLOSSARY_DATE => [
+                'mode' => 'date',
+                'descriptor' => 'dateview'
+            ],
+            GLOSSARY_AUTHOR => [
+                'mode' => 'author',
+                'descriptor' => 'authorview'
+            ],
+        ];
+
+        $baseurl = new moodle_url('/mod/glossary/view.php', ['id' => $this->cm->id]);
+        $active = null;
+        $options = [];
+        foreach ($validtabs as $key => $tabinfo) {
+            if (in_array($key, $tabs)) {
+                $baseurl->params(['mode' => $tabinfo['mode']]);
+                $active = $active ?? $baseurl->out(false);
+                $active = ($tabinfo['mode'] == $this->mode ? $baseurl->out(false) : $active);
+                $options[get_string($tabinfo['descriptor'], 'glossary')] = $baseurl->out(false);
+            }
+        }
+
+        if ($this->tab < GLOSSARY_STANDARD_VIEW || $this->tab > GLOSSARY_AUTHOR_VIEW) {
+            $options[get_string('edit')] = '#';
+        }
+
+        if (count($options) > 1) {
+            $select = new url_select(array_flip($options), $active, null);
+            $select->set_label(get_string('explainalphabet', 'glossary'), ['class' => 'sr-only']);
+            return $select->export_for_template($output);
+        }
+
+        return null;
+    }
+}
diff --git a/mod/glossary/edit.php b/mod/glossary/edit.php
index 15d06e5ad50..830288b7a97 100644
--- a/mod/glossary/edit.php
+++ b/mod/glossary/edit.php
@@ -82,6 +82,7 @@ if (!empty($id)) {
 
 $PAGE->set_title($glossary->name);
 $PAGE->set_heading($course->fullname);
+$PAGE->set_secondary_active_tab('modulepage');
 echo $OUTPUT->header();
 echo $OUTPUT->heading(format_string($glossary->name), 2);
 if ($glossary->intro) {
diff --git a/mod/glossary/export.php b/mod/glossary/export.php
index 56bdb8f3cd2..c8e8078930b 100644
--- a/mod/glossary/export.php
+++ b/mod/glossary/export.php
@@ -50,6 +50,7 @@ $PAGE->set_url('/mod/glossary/export.php', array('id'=>$cm->id));
 $PAGE->navbar->add($strexportentries);
 $PAGE->set_title($glossary->name);
 $PAGE->set_heading($course->fullname);
+$PAGE->set_secondary_active_tab('modulepage');
 
 echo $OUTPUT->header();
 echo $OUTPUT->heading($strexportentries);
diff --git a/mod/glossary/import.php b/mod/glossary/import.php
index 783864ef6d5..d73158b8008 100644
--- a/mod/glossary/import.php
+++ b/mod/glossary/import.php
@@ -49,6 +49,7 @@ $strimportentries = get_string('importentriesfromxml', 'glossary');
 $PAGE->navbar->add($strimportentries);
 $PAGE->set_title($glossary->name);
 $PAGE->set_heading($course->fullname);
+$PAGE->set_secondary_active_tab('modulepage');
 
 echo $OUTPUT->header();
 echo $OUTPUT->heading($strimportentries);
diff --git a/mod/glossary/lang/en/deprecated.txt b/mod/glossary/lang/en/deprecated.txt
new file mode 100644
index 00000000000..7b49b56cbca
--- /dev/null
+++ b/mod/glossary/lang/en/deprecated.txt
@@ -0,0 +1 @@
+waitingapproval,mod_glossary
diff --git a/mod/glossary/lang/en/glossary.php b/mod/glossary/lang/en/glossary.php
index 0e64b561885..35500edabfa 100644
--- a/mod/glossary/lang/en/glossary.php
+++ b/mod/glossary/lang/en/glossary.php
@@ -26,6 +26,7 @@
 $string['addcomment'] = 'Add comment';
 $string['addcategory'] = 'Add category';
 $string['addentry'] = 'Add a new entry';
+$string['addsingleentry'] = 'Add entry';
 $string['addingcomment'] = 'Add a comment';
 $string['alias'] = 'Keyword';
 $string['aliases'] = 'Keyword(s)';
@@ -180,6 +181,7 @@ $string['explainalphabet'] = 'Browse the glossary using this index';
 $string['explainexport'] = 'Click on the button below to export glossary entries.<br />You can import it anytime you wish in this or other course.<p>Please note that attachments (e.g. images) and authors are not exported.</p>';
 $string['explainimport'] = 'You must specify the file to import and define the criteria of the process.<p>Submit your request and review the results.</p>';
 $string['explainspecial'] = 'Shows entries that do not begin with a letter';
+$string['export'] = 'Export';
 $string['exportedentry'] = 'Exported entry';
 $string['exportentries'] = 'Export entries';
 $string['exportentriestoxml'] = 'Export entries to XML file';
@@ -276,6 +278,7 @@ $string['onebyline'] = '(one per line)';
 $string['page-mod-glossary-x'] = 'Any glossary module page';
 $string['page-mod-glossary-edit'] = 'Glossary add/edit entry page';
 $string['page-mod-glossary-view'] = 'View glossary edit page';
+$string['pendingapproval'] = 'Pending approval';
 $string['pluginadministration'] = 'Glossary administration';
 $string['pluginname'] = 'Glossary';
 $string['popupformat'] = 'Popup format';
@@ -303,6 +306,7 @@ $string['resetglossariesall'] = 'Delete entries from all glossaries';
 $string['rssarticles'] = 'Number of RSS recent articles';
 $string['rssarticles_help'] = 'This setting specifies the number of glossary entry concepts to include in the RSS feed. Between 5 and 20 generally acceptable.';
 $string['rsssubscriberss'] = 'Display the RSS feed for \'{$a}\' concepts';
+$string['rssfeed'] = 'RSS feed';
 $string['rsstype'] = 'RSS feed for this activity';
 $string['rsstype_help'] = 'To enable the RSS feed for this activity, select either concepts with author or concepts without author to be included in the feed.';
 $string['search:activity'] = 'Glossary - activity information';
@@ -328,9 +332,11 @@ $string['totalentries'] = 'Total entries';
 $string['usedynalink'] = 'Automatically link glossary entries';
 $string['usedynalink_help'] = 'If site-wide glossary auto-linking has been enabled by an administrator and this setting is enabled, the "Add a new entry" form includes the option to automatically link the entry wherever the concept words and phrases appear throughout the rest of the course.';
 $string['visibletabs'] = 'Visible tabs';
-$string['waitingapproval'] = 'Waiting approval';
 $string['warningstudentcapost'] = '(Applies only if the glossary is not the main one)';
 $string['withauthor'] = 'Concepts with author';
 $string['withoutauthor'] = 'Concepts without author';
 $string['writtenby'] = 'by';
 $string['youarenottheauthor'] = 'You are not the author of this comment, so you are not allowed to edit it.';
+
+// Deprecated since 4.0.
+$string['waitingapproval'] = 'Waiting approval';
diff --git a/mod/glossary/lib.php b/mod/glossary/lib.php
index 47e7640c670..e5c7bdcdf43 100644
--- a/mod/glossary/lib.php
+++ b/mod/glossary/lib.php
@@ -3142,22 +3142,32 @@ function glossary_extend_settings_navigation(settings_navigation $settings, navi
     $hook = optional_param('hook', 'ALL', PARAM_CLEAN);
 
     if (has_capability('mod/glossary:import', $PAGE->cm->context)) {
-        $glossarynode->add(get_string('importentries', 'glossary'), new moodle_url('/mod/glossary/import.php', array('id'=>$PAGE->cm->id)));
+        $node = $glossarynode->add(get_string('importentries', 'glossary'),
+            new moodle_url('/mod/glossary/import.php', ['id' => $PAGE->cm->id]));
+        $node->set_show_in_secondary_navigation(false);
     }
 
     if (has_capability('mod/glossary:export', $PAGE->cm->context)) {
-        $glossarynode->add(get_string('exportentries', 'glossary'), new moodle_url('/mod/glossary/export.php', array('id'=>$PAGE->cm->id, 'mode'=>$mode, 'hook'=>$hook)));
-    }
-
-    if (has_capability('mod/glossary:approve', $PAGE->cm->context) && ($hiddenentries = $DB->count_records('glossary_entries', array('glossaryid'=>$PAGE->cm->instance, 'approved'=>0)))) {
-        $glossarynode->add(get_string('waitingapproval', 'glossary'), new moodle_url('/mod/glossary/view.php', array('id'=>$PAGE->cm->id, 'mode'=>'approval')));
-    }
-
-    if (has_capability('mod/glossary:write', $PAGE->cm->context)) {
-        $glossarynode->add(get_string('addentry', 'glossary'), new moodle_url('/mod/glossary/edit.php', array('cmid'=>$PAGE->cm->id)));
+        $node = $glossarynode->add(get_string('exportentries', 'glossary'),
+            new moodle_url('/mod/glossary/export.php', ['id' => $PAGE->cm->id, 'mode' => $mode, 'hook' => $hook]));
+        $node->set_show_in_secondary_navigation(false);
     }
 
     $glossary = $DB->get_record('glossary', array("id" => $PAGE->cm->instance));
+    $hiddenentries = $DB->count_records('glossary_entries', ['glossaryid' => $PAGE->cm->instance, 'approved' => 0]);
+
+    // Safe guard check - Ideally, there shouldn't be any hidden entries if the glossary has 'defaultapproval'.
+    if (has_capability('mod/glossary:approve', $PAGE->cm->context) && (!$glossary->defaultapproval || $hiddenentries)) {
+        $glossarynode->add(get_string('pendingapproval', 'glossary'),
+            new moodle_url('/mod/glossary/view.php', ['id' => $PAGE->cm->id, 'mode' => 'approval']),
+            navigation_node::TYPE_CUSTOM, null, 'pendingapproval');
+    }
+
+    if (has_capability('mod/glossary:write', $PAGE->cm->context)) {
+        $node = $glossarynode->add(get_string('addentry', 'glossary'),
+            new moodle_url('/mod/glossary/edit.php', ['cmid' => $PAGE->cm->id]));
+        $node->set_show_in_secondary_navigation(false);
+    }
 
     if (!empty($CFG->enablerssfeeds) && !empty($CFG->glossary_enablerssfeeds) && $glossary->rsstype && $glossary->rssarticles && has_capability('mod/glossary:view', $PAGE->cm->context)) {
         require_once("$CFG->libdir/rsslib.php");
@@ -3165,7 +3175,8 @@ function glossary_extend_settings_navigation(settings_navigation $settings, navi
         $string = get_string('rsstype', 'glossary');
 
         $url = new moodle_url(rss_get_url($PAGE->cm->context->id, $USER->id, 'mod_glossary', $glossary->id));
-        $glossarynode->add($string, $url, settings_navigation::TYPE_SETTING, null, null, new pix_icon('i/rss', ''));
+        $node = $glossarynode->add($string, $url, settings_navigation::TYPE_SETTING, null, null, new pix_icon('i/rss', ''));
+        $node->set_show_in_secondary_navigation(false);
     }
 }
 
diff --git a/mod/glossary/tabs.php b/mod/glossary/tabs.php
index 015312baef3..1d8a7a572cf 100644
--- a/mod/glossary/tabs.php
+++ b/mod/glossary/tabs.php
@@ -1,81 +1,29 @@
 <?php
-    if (!isset($sortorder)) {
-        $sortorder = '';
-    }
-    if (!isset($sortkey)) {
-        $sortkey = '';
-    }
-
-    //make sure variables are properly cleaned
-    $sortkey   = clean_param($sortkey, PARAM_ALPHA);// Sorted view: CREATION | UPDATE | FIRSTNAME | LASTNAME...
-    $sortorder = clean_param($sortorder, PARAM_ALPHA);   // it defines the order of the sorting (ASC or DESC)
-
-    $toolsrow = array();
-    $browserow = array();
-    $inactive = array();
-    $activated = array();
-
-    if (!has_capability('mod/glossary:approve', $context) && $tab == GLOSSARY_APPROVAL_VIEW) {
-    /// Non-teachers going to approval view go to defaulttab
-        $tab = $defaulttab;
-    }
-
-    // Get visible tabs for the format and check tab needs to be displayed.
-    $dt = glossary_get_visible_tabs($dp);
-
-    if (in_array(GLOSSARY_STANDARD, $dt)) {
-        $browserow[] = new tabobject(GLOSSARY_STANDARD_VIEW,
-            $CFG->wwwroot.'/mod/glossary/view.php?id='.$id.'&amp;mode=letter',
-            get_string('standardview', 'glossary'));
-    }
-
-    if (in_array(GLOSSARY_CATEGORY, $dt)) {
-        $browserow[] = new tabobject(GLOSSARY_CATEGORY_VIEW,
-            $CFG->wwwroot.'/mod/glossary/view.php?id='.$id.'&amp;mode=cat',
-            get_string('categoryview', 'glossary'));
-    }
-
-    if (in_array(GLOSSARY_DATE, $dt)) {
-        $browserow[] = new tabobject(GLOSSARY_DATE_VIEW,
-            $CFG->wwwroot.'/mod/glossary/view.php?id='.$id.'&amp;mode=date',
-            get_string('dateview', 'glossary'));
-    }
-
-    if (in_array(GLOSSARY_AUTHOR, $dt)) {
-        $browserow[] = new tabobject(GLOSSARY_AUTHOR_VIEW,
-            $CFG->wwwroot.'/mod/glossary/view.php?id='.$id.'&amp;mode=author',
-            get_string('authorview', 'glossary'));
-    }
-
-    if ($tab < GLOSSARY_STANDARD_VIEW || $tab > GLOSSARY_AUTHOR_VIEW) {   // We are on second row
-        $inactive = array('edit');
-        $activated = array('edit');
-
-        $browserow[] = new tabobject('edit', '#', get_string('edit'));
-    }
-
-/// Put all this info together
-
-    $tabrows = array();
-    $tabrows[] = $browserow;     // Always put these at the top
-    if ($toolsrow) {
-        $tabrows[] = $toolsrow;
-    }
-
-?>
-  <div class="glossarydisplay">
-
-
-<?php
-if ($showcommonelements && (count($tabrows[0]) > 1)) {
-    print_tabs($tabrows, $tab, $inactive, $activated);
-}
-?>
-
-  <div class="entrybox">
-
-<?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/>.
+/**
+ * prints the tabbed bar
+ *
+ * @author Peter Dias
+ * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
+ * @package mod_glossary
+ * @copyright 2021 Peter Dias
+ */
+defined('MOODLE_INTERNAL') || die;
 
+    echo html_writer::start_div('entrybox');
     if (!isset($category)) {
         $category = "";
     }
@@ -120,4 +68,3 @@ if ($showcommonelements && (count($tabrows[0]) > 1)) {
         break;
     }
     echo html_writer::empty_tag('hr');
-?>
\ No newline at end of file
diff --git a/mod/glossary/templates/standard_action_menu.mustache b/mod/glossary/templates/standard_action_menu.mustache
new file mode 100644
index 00000000000..a45ce837296
--- /dev/null
+++ b/mod/glossary/templates/standard_action_menu.mustache
@@ -0,0 +1,168 @@
+{{!
+    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/>.
+}}
+{{!
+    @template mod_glossary/standard_action_menu
+    Actions panel at the bottom of the assignment grading UI.
+    Classes required for JS:
+        * none
+    Data attributes required for JS:
+        * none
+    Context variables required for this template:
+        * see mod/glossary/classes/output/standard_action_bar.php
+    Example context (json):
+    {
+        "addnewbutton": {
+            "method" : "get",
+            "url" : "#",
+            "primary" : true,
+            "tooltip" : "This is a tooltip",
+            "label" : "This is a the button text",
+            "attributes": [
+                {
+                  "name": "data-attribute",
+                  "value": "yeah"
+                }
+            ]
+        },
+        "tools": {
+            "button": {
+                "method" : "get",
+                "url" : "#",
+                "primary" : true,
+                "tooltip" : "This is a tooltip",
+                "label" : "This is a the button text",
+                "attributes": [
+                    {
+                      "name": "data-attribute",
+                      "value": "yeah"
+                    }
+                ]
+            },
+            "select": {
+                "options": [
+                    {
+                        "url": "www.google.com",
+                        "string": "Google"
+                    },
+                    {
+                        "url": "www.yahoo.com",
+                        "string": "Yahoo"
+                    }
+                ]
+            }
+        },
+        "tabjumps":
+        {
+            "id": "url_select_test",
+            "action": "https://example.com/post",
+            "formid": "url_select_form",
+            "sesskey": "sesskey",
+            "label": "core/url_select",
+            "helpicon": {
+                "title": "Help with something",
+                "text": "Help with something",
+                "url": "http://example.org/help",
+                "linktext": "",
+                "icon":{
+                    "extraclasses": "iconhelp",
+                    "attributes": [
+                        {"name": "src", "value": "../../../pix/help.svg"},
+                        {"name": "alt", "value": "Help icon"}
+                    ]
+                }
+            },
+            "showbutton": "Go",
+            "options": [{
+                "name": "Group 1", "isgroup": true, "options":
+                [
+                    {"name": "Item 1", "isgroup": false, "value": "1"},
+                    {"name": "Item 2", "isgroup": false, "value": "2"}
+                ]},
+                {"name": "Group 2", "isgroup": true, "options":
+                [
+                    {"name": "Item 3", "isgroup": false, "value": "3"},
+                    {"name": "Item 4", "isgroup": false, "value": "4"}
+                ]}],
+            "disabled": false,
+            "title": "Some cool title"
+        },
+        "searchbox": {
+            "action": "http://localhost/stable/mod/glossary/view.php",
+            "hiddenfields": [
+                {
+                    "name": "id",
+                    "value": 23
+                },
+                {
+                    "name": "mode",
+                    "value": "search"
+                }
+            ],
+            "otherfields": "<input type='checkbox'>Rendered checkbox",
+            "inputname": "hook",
+            "query": "hook",
+            "searchstring": "Search"
+        }
+    }
+}}
+<div class="container-fluid px-0">
+    <div class="row mx-0">
+        <div class="d-flex">
+            <div class="col-xs">
+                {{#addnewbutton}}
+                    {{> core/single_button }}
+                {{/addnewbutton}}
+            </div>
+        </div>
+
+        <div class="d-flex ml-auto">
+            {{#tools}}
+                {{#button}}
+                    <div class="pr-1">
+                        {{> core/single_button}}
+                    </div>
+                {{/button}}
+                {{#select}}
+                <div>
+                    <div class="btn-group">
+                        <button type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                            ...
+                            <span class="sr-only">{{#str}} exportentries, mod_glossary {{/str}}</span>
+                        </button>
+                        <div class="dropdown-menu">
+                            {{#options}}
+                                <a class="dropdown-item" href="{{url}}">{{string}}</a>
+                            {{/options}}
+                        </div>
+                    </div>
+                </div>
+                {{/select}}
+            {{/tools}}
+        </div>
+    </div>
+    <div class="row mt-2 ml-0">
+        <div class="d-flex">
+            {{#tabjumps}}
+            <div class="col-xs pr-1">
+                {{> core/url_select }}
+            </div>
+            {{/tabjumps}}
+            {{#searchbox}}
+            <div class="col-xs">
+                {{> core/search_input }}
+            </div>
+            {{/searchbox}}
+        </div>
+    </div>
+</div>
diff --git a/mod/glossary/version.php b/mod/glossary/version.php
index a237d5d2bdf..dd7b89ed8da 100644
--- a/mod/glossary/version.php
+++ b/mod/glossary/version.php
@@ -24,7 +24,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2021052502;       // The current module version (Date: YYYYMMDDXX).
+$plugin->version   = 2021052503;       // The current module version (Date: YYYYMMDDXX).
 $plugin->requires  = 2021052500;    // Requires this Moodle version.
 $plugin->component = 'mod_glossary';   // Full name of the plugin (used for diagnostics)
 $plugin->cron      = 0;
diff --git a/mod/glossary/view.php b/mod/glossary/view.php
index c363643f8a4..4e7f2357f1d 100644
--- a/mod/glossary/view.php
+++ b/mod/glossary/view.php
@@ -51,6 +51,7 @@ $cm = cm_info::create($cm);
 require_course_login($course->id, true, $cm);
 $context = context_module::instance($cm->id);
 require_capability('mod/glossary:view', $context);
+$hassecondary = $PAGE->has_secondary_navigation();
 
 // Prepare format_string/text options
 $fmtoptions = array(
@@ -270,16 +271,20 @@ $straddentry = get_string("addentry", "glossary");
 $strnoentries = get_string("noentries", "glossary");
 $strsearchindefinition = get_string("searchindefinition", "glossary");
 $strsearch = get_string("search");
-$strwaitingapproval = get_string('waitingapproval', 'glossary');
+$strwaitingapproval = get_string('pendingapproval', 'glossary');
 
 /// If we are in approval mode, prit special header
 $PAGE->set_title($glossary->name);
 $PAGE->set_heading($course->fullname);
 $url = new moodle_url('/mod/glossary/view.php', array('id'=>$cm->id));
-if (isset($mode)) {
+if (isset($mode) && $mode) {
     $url->param('mode', $mode);
 }
 $PAGE->set_url($url);
+
+$renderer = $PAGE->get_renderer('mod_glossary');
+$actionbar = new \mod_glossary\output\standard_action_bar($cm, $glossary, $dp, $mode, $hook,
+    $sortkey, $sortorder, $offset, $pagelimit, $fullsearch, $tab, $defaulttab);
 $PAGE->force_settings_menu();
 
 if (!empty($CFG->enablerssfeeds) && !empty($CFG->glossary_enablerssfeeds)
@@ -288,22 +293,27 @@ if (!empty($CFG->enablerssfeeds) && !empty($CFG->glossary_enablerssfeeds)
     $rsstitle = format_string($course->shortname, true, array('context' => context_course::instance($course->id))) . ': '. format_string($glossary->name);
     rss_add_http_header($context, 'mod_glossary', $glossary, $rsstitle);
 }
-
 if ($tab == GLOSSARY_APPROVAL_VIEW) {
     require_capability('mod/glossary:approve', $context);
     $PAGE->navbar->add($strwaitingapproval);
-    echo $OUTPUT->header();
-    echo $OUTPUT->heading($strwaitingapproval);
-} else { /// Print standard header
-    echo $OUTPUT->header();
 }
-echo $OUTPUT->heading(format_string($glossary->name), 2);
+echo $OUTPUT->header();
+$hassecondary = $PAGE->has_secondary_navigation();
+
+if (!$hassecondary) {
+    if ($tab == GLOSSARY_APPROVAL_VIEW) {
+        echo $OUTPUT->heading($strwaitingapproval);
+    }
+    echo $OUTPUT->heading(format_string($glossary->name), 2);
+}
 
 // Render the activity information.
 $completiondetails = \core_completion\cm_completion_details::get_instance($cm, $USER->id);
 $activitydates = \core\activity_dates::get_dates_for_module($cm, $USER->id);
 echo $OUTPUT->activity_information($cm, $completiondetails, $activitydates);
-
+if ($showcommonelements) {
+    echo $renderer->main_action_bar($actionbar);
+}
 /// All this depends if whe have $showcommonelements
 if ($showcommonelements) {
 /// To calculate available options
@@ -331,7 +341,7 @@ if ($showcommonelements) {
     }*/
 
 /// Decide about to print the approval link
-    if (has_capability('mod/glossary:approve', $context)) {
+    if (has_capability('mod/glossary:approve', $context) && !$hassecondary) {
     /// Check we have pending entries
         if ($hiddenentries = $DB->count_records('glossary_entries', array('glossaryid'=>$glossary->id, 'approved'=>0))) {
             if ($availableoptions) {
@@ -340,8 +350,8 @@ if ($showcommonelements) {
             $availableoptions .='<span class="helplink">' .
                                 '<a href="' . $CFG->wwwroot . '/mod/glossary/view.php?id=' . $cm->id .
                                 '&amp;mode=approval' . '"' .
-                                '  title="' . s(get_string('waitingapproval', 'glossary')) . '">' .
-                                get_string('waitingapproval', 'glossary') . ' ('.$hiddenentries.')</a>' .
+                                '  title="' . s($strwaitingapproval) . '">' .
+                                $strwaitingapproval . ' ('.$hiddenentries.')</a>' .
                                 '</span>';
         }
     }
@@ -351,27 +361,6 @@ if ($showcommonelements) {
     echo '<div class="glossarycontrol" style="text-align: right">';
     echo $availableoptions;
 
-/// The print icon
-    if ( $showcommonelements and $mode != 'search') {
-        if (has_capability('mod/glossary:manageentries', $context) or $glossary->allowprintview) {
-            $params = array(
-                'id'        => $cm->id,
-                'mode'      => $mode,
-                'hook'      => $hook,
-                'sortkey'   => $sortkey,
-                'sortorder' => $sortorder,
-                'offset'    => $offset,
-                'pagelimit' => $pagelimit
-            );
-            $printurl = new moodle_url('/mod/glossary/print.php', $params);
-            $printtitle = get_string('printerfriendly', 'glossary');
-            $printattributes = array(
-                'class' => 'printicon',
-                'title' => $printtitle
-            );
-            echo html_writer::link($printurl, $printtitle, $printattributes);
-        }
-    }
 /// End glossary controls
 //        print_box_end(); /// glossarycontrol
     echo '</div><br />';
@@ -384,52 +373,6 @@ if ($glossary->intro && $showcommonelements) {
     echo $OUTPUT->box(format_module_intro('glossary', $glossary, $cm->id), 'generalbox', 'intro');
 }
 
-/// Search box
-if ($showcommonelements ) {
-    $fullsearchchecked = false;
-    if ($fullsearch || $mode != 'search') {
-        $fullsearchchecked = true;
-    }
-
-    $check = [
-        'name' => 'fullsearch',
-        'id' => 'fullsearch',
-        'value' => '1',
-        'checked' => $fullsearchchecked,
-        'label' => $strsearchindefinition
-    ];
-
-    $checkbox = $OUTPUT->render_from_template('core/checkbox', $check);
-
-    $hiddenfields = [
-        (object) ['name' => 'id', 'value' => $cm->id],
-        (object) ['name' => 'mode', 'value' => 'search'],
-    ];
-    $data = [
-        'action' => new moodle_url('/mod/glossary/view.php'),
-        'hiddenfields' => $hiddenfields,
-        'otherfields' => $checkbox,
-        'inputname' => 'hook',
-        'query' => ($mode == 'search') ? s($hook) : '',
-        'searchstring' => get_string('search'),
-        'extraclasses' => 'my-2'
-    ];
-    echo $OUTPUT->render_from_template('core/search_input', $data);
-}
-
-/// Show the add entry button if allowed
-if (has_capability('mod/glossary:write', $context) && $showcommonelements ) {
-    echo '<div class="singlebutton glossaryaddentry">';
-    echo "<form class=\"form form-inline mb-1\" id=\"newentryform\" method=\"get\" action=\"$CFG->wwwroot/mod/glossary/edit.php\">";
-    echo '<div>';
-    echo "<input type=\"hidden\" name=\"cmid\" value=\"$cm->id\" />";
-    echo '<input type="submit" value="'.get_string('addentry', 'glossary').'" class="btn btn-secondary" />';
-    echo '</div>';
-    echo '</form>';
-    echo "</div>\n";
-}
-
-
 require("tabs.php");
 
 require("sql.php");
@@ -561,7 +504,6 @@ if ( $paging ) {
     echo '</div>';
 }
 echo '<br />';
-glossary_print_tabbed_table_end();
 
 /// Finish the page
 echo $OUTPUT->footer();