From 4838f8e916687ae85c8c97c0ce45dfc191abf11f Mon Sep 17 00:00:00 2001
From: Ilya Tregubov <ilya@moodle.com>
Date: Wed, 31 May 2023 17:21:12 +0800
Subject: [PATCH] MDL-77635 formslib: Allow using a sticky footer in QuickForms

---
 lib/formslib.php | 65 +++++++++++++++++++++++++++++++++++++++++++++++-
 lib/upgrade.txt  |  1 +
 2 files changed, 65 insertions(+), 1 deletion(-)

diff --git a/lib/formslib.php b/lib/formslib.php
index 7dde6309dbc..f54253a807c 100644
--- a/lib/formslib.php
+++ b/lib/formslib.php
@@ -1340,7 +1340,7 @@ abstract class moodleform {
      * @param bool $cancel whether to show cancel button, default true
      * @param string $submitlabel label for submit button, defaults to get_string('savechanges')
      */
-    function add_action_buttons($cancel = true, $submitlabel=null){
+    public function add_action_buttons($cancel = true, $submitlabel = null) {
         if (is_null($submitlabel)){
             $submitlabel = get_string('savechanges');
         }
@@ -1359,6 +1359,21 @@ abstract class moodleform {
         }
     }
 
+    /**
+     * Use this method to make a sticky submit/cancel button at the end of your form.
+     *
+     * @param bool $cancel whether to show cancel button, default true
+     * @param string|null $submitlabel label for submit button, defaults to get_string('savechanges')
+     */
+    public function add_sticky_action_buttons(bool $cancel = true, ?string $submitlabel = null): void {
+        $this->add_action_buttons($cancel, $submitlabel);
+        if ($cancel) {
+            $this->_form->set_sticky_footer('buttonar');
+        } else {
+            $this->_form->set_sticky_footer('submitbutton');
+        }
+    }
+
     /**
      * Adds an initialisation call for a standard JavaScript enhancement.
      *
@@ -1599,6 +1614,12 @@ class MoodleQuickForm extends HTML_QuickForm_DHTMLRulesTableless {
     /** @var array Array whose keys are element names. If the key exists this is a advanced element */
     var $_advancedElements = array();
 
+    /**
+     * The form element to render in the sticky footer, if any.
+     * @var string|null $_stickyfooterelement
+     */
+    protected $_stickyfooterelement = null;
+
     /**
      * Array whose keys are element names and values are the desired collapsible state.
      * True for collapsed, False for expanded. If not present, set to default in
@@ -1737,6 +1758,18 @@ class MoodleQuickForm extends HTML_QuickForm_DHTMLRulesTableless {
         }
     }
 
+    /**
+     * Use this method to indicate an element to display as a sticky footer.
+     *
+     * Only one page element can be displayed in the sticky footer. To render
+     * more than one element use addGroup to create a named group.
+     *
+     * @param string|null $elementname group or element name (not the element name of something inside a group).
+     */
+    public function set_sticky_footer(?string $elementname): void {
+        $this->_stickyfooterelement = $elementname;
+    }
+
     /**
      * Checks if a parameter was passed in the previous form submission
      *
@@ -1993,6 +2026,9 @@ class MoodleQuickForm extends HTML_QuickForm_DHTMLRulesTableless {
             // Pass the array to renderer object.
             $renderer->setCollapsibleElements($this->_collapsibleElements);
         }
+        if (method_exists($renderer, 'set_sticky_footer') && !empty($this->_stickyfooterelement)) {
+            $renderer->set_sticky_footer($this->_stickyfooterelement);
+        }
         parent::accept($renderer);
     }
 
@@ -3050,6 +3086,12 @@ class MoodleQuickForm_Renderer extends HTML_QuickForm_Renderer_Tableless{
      */
     var $_advancedElements = array();
 
+    /**
+     * The form element to render in the sticky footer, if any.
+     * @var string|null $_stickyfooterelement
+     */
+    protected $_stickyfooterelement = null;
+
     /**
      * Array whose keys are element names and the the boolean values reflect the current state. If the key exists this is a collapsible element.
      *
@@ -3103,6 +3145,15 @@ class MoodleQuickForm_Renderer extends HTML_QuickForm_Renderer_Tableless{
         $this->_advancedElements = $elements;
     }
 
+    /**
+     * Set the sticky footer element if any.
+     *
+     * @param string|null $elementname the form element name.
+     */
+    public function set_sticky_footer(?string $elementname): void {
+        $this->_stickyfooterelement = $elementname;
+    }
+
     /**
      * Setting collapsible elements
      *
@@ -3203,6 +3254,12 @@ class MoodleQuickForm_Renderer extends HTML_QuickForm_Renderer_Tableless{
             $html = str_replace('{emptylabel}', $emptylabel, $html);
         }
         $this->_templates[$group->getName()] = $html;
+        // Check if the element should be displayed in the sticky footer.
+        if ($this->_stickyfooterelement == $group->getName()) {
+            $stickyfooter = new core\output\sticky_footer($html);
+            $html = $OUTPUT->render($stickyfooter);
+        }
+
         // Fix for bug in tableless quickforms that didn't allow you to stop a
         // fieldset before a group of elements.
         // if the element name indicates the end of a fieldset, close the fieldset
@@ -3283,6 +3340,12 @@ class MoodleQuickForm_Renderer extends HTML_QuickForm_Renderer_Tableless{
             $this->_templates[$element->getName()] = $html;
         }
 
+        // Check if the element should be displayed in the sticky footer.
+        if ($this->_stickyfooterelement == $element->getName()) {
+            $stickyfooter = new core\output\sticky_footer($html);
+            $html = $OUTPUT->render($stickyfooter);
+        }
+
         if (!$fromtemplate) {
             parent::renderElement($element, $required, $error);
         } else {
diff --git a/lib/upgrade.txt b/lib/upgrade.txt
index 7fcd5bf2c96..86a2bbe0a1d 100644
--- a/lib/upgrade.txt
+++ b/lib/upgrade.txt
@@ -10,6 +10,7 @@ information provided here is intended especially for developers.
 * Support for the following phpunit coverage info properties, deprecated since 3.11, has been removed:
   - `whitelistfolders`
   - `whitelistfiles`
+* New method moodleform::add_sticky_action_buttons() is created to enable sticky footer for QuickForms.
 
 === 4.2 ===