From 312bffbf439802cabf597c04f7abce7aa72ffadf Mon Sep 17 00:00:00 2001
From: Carsten  Rose <carsten.rose@math.uzh.ch>
Date: Thu, 7 Jun 2018 10:09:07 +0200
Subject: [PATCH] #6178 / Input: Step: New option 'step' for FE.parameter.
 Still some trouble with checkpattern.

---
 extension/Documentation/Manual.rst       |  2 +
 extension/qfq/qfq/AbstractBuildForm.php  | 74 +++++++++++++++++++++++-
 extension/qfq/qfq/BuildFormBootstrap.php |  4 ++
 extension/qfq/qfq/Constants.php          | 10 +++-
 extension/qfq/qfq/form/FormAction.php    |  5 +-
 extension/qfq/qfq/helper/Support.php     |  4 +-
 6 files changed, 91 insertions(+), 8 deletions(-)

diff --git a/extension/Documentation/Manual.rst b/extension/Documentation/Manual.rst
index 9a94df888..99199182c 100644
--- a/extension/Documentation/Manual.rst
+++ b/extension/Documentation/Manual.rst
@@ -2937,6 +2937,8 @@ Type: text
     the value is an empty string
   * *inputType* = number (optional). Typically the HTML tag 'type' will be 'text', 'textarea' or 'number' (detected automatically).
     If necessary, the HTML tag 'type' might be forced to a specific given value.
+  * *step* = Step size of the up/down buttons which increase/decrease the number of in the input field. Optional.
+    Default 1. Only useful with `inputType=number` (defined explicit via `inputType` or detected automatically).
 
 .. _`input-typeahead`:
 
diff --git a/extension/qfq/qfq/AbstractBuildForm.php b/extension/qfq/qfq/AbstractBuildForm.php
index bbea73faf..54d53da30 100644
--- a/extension/qfq/qfq/AbstractBuildForm.php
+++ b/extension/qfq/qfq/AbstractBuildForm.php
@@ -329,6 +329,9 @@ abstract class AbstractBuildForm {
      * Returns '<form ...>'-tag with various attributes.
      *
      * @return string
+     * @throws CodeException
+     * @throws DbException
+     * @throws UserFormException
      */
     public function getFormTag() {
         $md5 = '';
@@ -347,6 +350,7 @@ abstract class AbstractBuildForm {
      *
      * @return string
      * @throws CodeException
+     * @throws DbException
      * @throws UserFormException
      */
     public function buildInputRecordHashMd5() {
@@ -1196,7 +1200,7 @@ abstract class AbstractBuildForm {
         if (empty($formElement[F_FE_DATA_PATTERN_ERROR])) {
             $formElement[F_FE_DATA_PATTERN_ERROR] = $sanitizeMessage;
         };
-        $attribute .= $this->getAttributeList($formElement, [F_FE_DATA_PATTERN_ERROR, F_FE_DATA_REQUIRED_ERROR, F_FE_DATA_MATCH_ERROR, F_FE_DATA_ERROR, FE_MIN, FE_MAX]);
+        $attribute .= $this->getAttributeList($formElement, [F_FE_DATA_PATTERN_ERROR, F_FE_DATA_REQUIRED_ERROR, F_FE_DATA_MATCH_ERROR, F_FE_DATA_ERROR, FE_MIN, FE_MAX, FE_STEP]);
         $attribute .= Support::doAttribute('data-load', ($formElement[FE_DYNAMIC_UPDATE] === 'yes') ? 'data-load' : '');
         $attribute .= Support::doAttribute('title', $formElement[FE_TOOLTIP]);
 
@@ -1545,6 +1549,7 @@ abstract class AbstractBuildForm {
      * @param string $fieldType
      *
      * @return array
+     * @throws CodeException
      * @throws UserFormException
      */
     private function getItemsForEnumOrSet($column, &$fieldType) {
@@ -1590,6 +1595,7 @@ abstract class AbstractBuildForm {
      * @param array $itemKey
      * @param array $formElement
      *
+     * @throws CodeException
      * @throws UserFormException
      */
     private function prepareCheckboxCheckedUncheckedValue(array $itemKey, array &$formElement) {
@@ -1630,6 +1636,8 @@ abstract class AbstractBuildForm {
      * @param string $mode FORM_LOAD | FORM_UPDATE | FORM_SAVE
      *
      * @return string
+     * @throws CodeException
+     * @throws UserFormException
      */
     public function buildCheckboxSingle(array $formElement, $htmlFormElementName, $attribute, $value, array &$json, $mode = FORM_LOAD) {
 
@@ -1663,6 +1671,8 @@ abstract class AbstractBuildForm {
      * @param array $json
      *
      * @return string
+     * @throws CodeException
+     * @throws UserFormException
      */
     public function constructCheckboxSingleButton(array $formElement, $htmlFormElementName, $attribute, $value, array &$json) {
         $html = '';
@@ -1721,6 +1731,8 @@ abstract class AbstractBuildForm {
      * @param array $json
      *
      * @return string
+     * @throws CodeException
+     * @throws UserFormException
      */
     public function constructCheckboxSinglePlain(array $formElement, $htmlFormElementName, $attribute, $value, array &$json) {
         $html = '';
@@ -1769,6 +1781,7 @@ abstract class AbstractBuildForm {
      * @param array $itemValue
      * @param array $json
      * @return string
+     * @throws CodeException
      */
     public function buildCheckboxMulti(array $formElement, $htmlFormElementName, $attributeBase, $value, array $itemKey, array $itemValue, array &$json) {
 
@@ -1788,7 +1801,8 @@ abstract class AbstractBuildForm {
      * @param array $formElement
      * @param string $htmlFormElementName
      * @param string $htmlHidden
-     *
+     * @throws CodeException
+     * @throws UserFormException
      */
     private function fillStoreAdditionalFormElementsCheckboxHidden(array $formElement, $htmlFormElementName, $htmlHidden) {
 
@@ -1818,6 +1832,7 @@ abstract class AbstractBuildForm {
      * @param array $json
      *
      * @return string
+     * @throws CodeException
      */
     public function constructCheckboxMultiButton(array $formElement, $htmlFormElementName, $attributeBase, $value, array $itemKey, array $itemValue, array &$json) {
         $json = array();
@@ -1889,6 +1904,7 @@ abstract class AbstractBuildForm {
      * @param array $json
      *
      * @return string
+     * @throws CodeException
      */
     public function constructCheckboxMultiPlain(array $formElement, $htmlFormElementName, $attributeBase, $value, array $itemKey, array $itemValue, array &$json) {
         $json = array();
@@ -1977,6 +1993,8 @@ abstract class AbstractBuildForm {
      * @param string $mode FORM_LOAD | FORM_UPDATE | FORM_SAVE
      *
      * @return string
+     * @throws CodeException
+     * @throws UserFormException
      */
     public function buildExtra(array $formElement, $htmlFormElementName, $value, array &$json, $mode = FORM_LOAD) {
 
@@ -2001,6 +2019,8 @@ abstract class AbstractBuildForm {
      * @param string $mode FORM_LOAD | FORM_UPDATE | FORM_SAVE
      *
      * @return string
+     * @throws CodeException
+     * @throws UserFormException
      */
     public function buildRadio(array $formElement, $htmlFormElementName, $value, array &$json, $mode = FORM_LOAD) {
 
@@ -2047,6 +2067,8 @@ abstract class AbstractBuildForm {
      * @param string $mode FORM_LOAD | FORM_UPDATE | FORM_SAVE
      *
      * @return string
+     * @throws CodeException
+     * @throws UserFormException
      */
     private function constructRadioButton(array $formElement, $htmlFormElementName, $value, array &$json, $mode = FORM_LOAD) {
         $itemKey = array();
@@ -2128,6 +2150,8 @@ abstract class AbstractBuildForm {
      * @param string $mode FORM_LOAD | FORM_UPDATE | FORM_SAVE
      *
      * @return string
+     * @throws CodeException
+     * @throws UserFormException
      */
     private function constructRadioPlain(array $formElement, $htmlFormElementName, $value, array &$json, $mode = FORM_LOAD) {
         $attributeBase = '';
@@ -2216,6 +2240,8 @@ abstract class AbstractBuildForm {
      * @param string $mode FORM_LOAD | FORM_UPDATE | FORM_SAVE
      *
      * @return mixed
+     * @throws CodeException
+     * @throws UserFormException
      */
     public function buildSelect(array $formElement, $htmlFormElementName, $value, array &$json, $mode = FORM_LOAD) {
         $itemKey = array();
@@ -2289,6 +2315,7 @@ abstract class AbstractBuildForm {
      * @param array $control Array with <th> column names / format.
      *
      * @return string
+     * @throws UserFormException
      */
     private function subrecordHead($linkNew, $flagDelete, array $firstRow, array &$control) {
 
@@ -2321,6 +2348,9 @@ abstract class AbstractBuildForm {
      * @param string $mode FORM_LOAD | FORM_UPDATE | FORM_SAVE
      *
      * @return string
+     * @throws CodeException
+     * @throws UserFormException
+     * @throws UserReportException
      */
     public function buildSubrecord(array $formElement, $htmlFormElementName, $value, array &$json, $mode = FORM_LOAD) {
         $rcText = false;
@@ -2478,6 +2508,8 @@ abstract class AbstractBuildForm {
      * @param $toolTip
      * @param array $currentRow
      * @return string
+     * @throws CodeException
+     * @throws UserFormException
      */
     private function createFormLink(array $formElement, $targetRecordId, array $record, $symbol, $toolTip, $currentRow = array()) {
 
@@ -2545,6 +2577,9 @@ abstract class AbstractBuildForm {
      * @param string $formName
      *
      * @return string   tableName for $formName
+     * @throws CodeException
+     * @throws DbException
+     * @throws UserFormException
      */
     private function getFormTable($formName) {
         $row = $this->dbArray[$this->dbIndexQfq]->sql("SELECT " . F_TABLE_NAME . " FROM Form AS f WHERE f.name = ?", ROW_EXPECT_0_1, [$formName]);
@@ -2575,6 +2610,7 @@ abstract class AbstractBuildForm {
      * @param array $titleRaw
      *
      * @return array
+     * @throws UserFormException
      */
     private function getSubrecordColumnControl(array $titleRaw) {
         $control = array();
@@ -2652,6 +2688,9 @@ abstract class AbstractBuildForm {
      * @param string $columnValue
      *
      * @return string
+     * @throws CodeException
+     * @throws UserFormException
+     * @throws UserReportException
      */
     private function renderCell(array $control, $columnName, $columnValue) {
         $link = null;
@@ -2714,6 +2753,8 @@ abstract class AbstractBuildForm {
      *                          parameters.
      *
      * @return string String: "API_DIR/delete.php?sip=...."
+     * @throws CodeException
+     * @throws UserFormException
      */
     public function createDeleteUrl($formName, $recordId, $mode = RETURN_URL) {
 
@@ -2741,7 +2782,10 @@ abstract class AbstractBuildForm {
      * @param string $mode FORM_LOAD | FORM_UPDATE | FORM_SAVE
      *
      * @return string
+     * @throws CodeException
+     * @throws DbException
      * @throws UserFormException
+     * @throws UserReportException
      */
     public function buildFile(array $formElement, $htmlFormElementName, $value, array &$json, $mode = FORM_LOAD) {
         $attribute = '';
@@ -2858,6 +2902,7 @@ abstract class AbstractBuildForm {
      * @param string $mode FORM_LOAD | FORM_UPDATE | FORM_SAVE
      *
      * @return string
+     * @throws CodeException
      * @throws UserFormException
      */
     public function buildAnnotate(array $formElement, $htmlFormElementName, $value, array &$json, $mode = FORM_LOAD) {
@@ -2910,6 +2955,7 @@ abstract class AbstractBuildForm {
      * @param string $mode FORM_LOAD | FORM_UPDATE | FORM_SAVE
      *
      * @return string
+     * @throws CodeException
      * @throws UserFormException
      */
     public function buildImageCut(array $formElement, $htmlFormElementName, $value, array &$json, $mode = FORM_LOAD) {
@@ -2977,6 +3023,8 @@ abstract class AbstractBuildForm {
     /**
      * @param string $pathFileName
      * @return string SIP encoded URL
+     * @throws CodeException
+     * @throws UserFormException
      */
     private function fileToSipUrl($pathFileName) {
         $param[DOWNLOAD_MODE] = DOWNLOAD_MODE_FILE;
@@ -3000,6 +3048,7 @@ abstract class AbstractBuildForm {
      * @param string $mode FORM_LOAD | FORM_UPDATE | FORM_SAVE
      *
      * @return string
+     * @throws CodeException
      * @throws UserFormException
      */
     public function buildDateTime(array $formElement, $htmlFormElementName, $value, array &$json, $mode = FORM_LOAD) {
@@ -3112,6 +3161,8 @@ abstract class AbstractBuildForm {
      * @param string $mode FORM_LOAD | FORM_UPDATE | FORM_SAVE
      *
      * @return string
+     * @throws CodeException
+     * @throws UserFormException
      */
     public function buildDateJQW(array $formElement, $htmlFormElementName, $value, array &$json, $mode = FORM_LOAD) {
         $arrMinMax = null;
@@ -3194,6 +3245,8 @@ abstract class AbstractBuildForm {
      * @param string $mode
      *
      * @return string
+     * @throws CodeException
+     * @throws UserFormException
      */
     public function buildEditor(array $formElement, $htmlFormElementName, $value, array &$json, $mode = FORM_LOAD) {
         $attribute = '';
@@ -3344,6 +3397,7 @@ abstract class AbstractBuildForm {
      *
      * @param string $mode FORM_LOAD | FORM_UPDATE | FORM_SAVE
      * @return mixed
+     * @throws CodeException
      */
     public function buildNote(array $formElement, $htmlFormElementName, $value, array &$json, $mode = FORM_LOAD) {
 
@@ -3378,6 +3432,10 @@ abstract class AbstractBuildForm {
      * @param string $mode FORM_LOAD | FORM_UPDATE | FORM_SAVE
      *
      * @return mixed
+     * @throws CodeException
+     * @throws DbException
+     * @throws UserFormException
+     * @throws UserReportException
      */
     public function buildFieldset(array $formElement, $htmlFormElementName, $value, array &$json, $mode = FORM_LOAD) {
         $attribute = '';
@@ -3444,6 +3502,10 @@ abstract class AbstractBuildForm {
      * @param string $mode FORM_LOAD | FORM_UPDATE | FORM_SAVE
      *
      * @return mixed
+     * @throws CodeException
+     * @throws DbException
+     * @throws UserFormException
+     * @throws UserReportException
      */
     public function buildTemplateGroup(array $formElement, $htmlFormElementName, $value, array &$json, $mode = FORM_LOAD) {
         $attribute = '';
@@ -3556,6 +3618,10 @@ EOT;
      * @param array $json
      *
      * @return string
+     * @throws CodeException
+     * @throws DbException
+     * @throws UserFormException
+     * @throws UserReportException
      */
     private function templateGroupCollectFilledElements($max, $htmlDelete, array &$json) {
 
@@ -3638,6 +3704,10 @@ EOT;
      * Additional the maximum count of all select rows will be determined and returned.
      *
      * @return int   max number of records in FormElement[FE_VALUE] over all FormElements.
+     * @throws CodeException
+     * @throws DbException
+     * @throws UserFormException
+     * @throws UserReportException
      */
     private function templateGroupDoValue() {
 
diff --git a/extension/qfq/qfq/BuildFormBootstrap.php b/extension/qfq/qfq/BuildFormBootstrap.php
index 15e25e47e..280f8a781 100644
--- a/extension/qfq/qfq/BuildFormBootstrap.php
+++ b/extension/qfq/qfq/BuildFormBootstrap.php
@@ -113,6 +113,7 @@ class BuildFormBootstrap extends AbstractBuildForm {
      * @throws CodeException
      * @throws DbException
      * @throws UserFormException
+     * @throws UserReportException
      */
     public function head($mode = FORM_LOAD) {
         $html = '';
@@ -515,6 +516,7 @@ class BuildFormBootstrap extends AbstractBuildForm {
      * @return string
      * @throws CodeException
      * @throws DbException
+     * @throws UserFormException
      */
     public function getFormTag() {
 
@@ -617,6 +619,7 @@ EOF;
      * @throws CodeException
      * @throws DbException
      * @throws UserFormException
+     * @throws UserReportException
      */
     public function buildPill(array $formElement, $htmlFormElementName, $value, array &$json) {
         $html = '';
@@ -812,6 +815,7 @@ EOF;
      * @throws CodeException
      * @throws DbException
      * @throws UserFormException
+     * @throws UserReportException
      */
     public function process($mode, $htmlElementNameIdZero = false, $latestFeSpecNative = array()) {
 
diff --git a/extension/qfq/qfq/Constants.php b/extension/qfq/qfq/Constants.php
index 969a84179..da2e77b05 100644
--- a/extension/qfq/qfq/Constants.php
+++ b/extension/qfq/qfq/Constants.php
@@ -146,8 +146,9 @@ const ERROR_NOT_IMPLEMENTED = 1033;
 const ERROR_RESERVED_KEY_NAME = 1034;
 const ERROR_MISSING_FORM = 1035;
 const ERROR_UNKNOWN_FORWARD_MODE = 1036;
-
-const ERROR_MISSING_HIDDEN_FIELD_IN_SIP = 1038;
+const ERROR_MISSING_MESSAGE_FAIL = 1037;
+const ERROR_MISSING_EXPECT_RECORDS = 1038;
+const ERROR_MISSING_HIDDEN_FIELD_IN_SIP = 1039;
 
 const ERROR_UNKNOWN_CHECKTYPE = 1042;
 const ERROR_PATTERN_VIOLATION = 1043;
@@ -170,7 +171,7 @@ const ERROR_MISSING_OPEN_DELIMITER = 1060;
 const ERROR_MISSING_CLOSE_DELIMITER = 1061;
 const ERROR_EXPECTED_ARRAY = 1062;
 const ERROR_REPORT_FAILED_ACTION = 1063;
-const ERROR_MISSING_MESSAGE_FAIL = 1064;
+
 const ERROR_MISSING_TABLE_NAME = 1065;
 const ERROR_MISSING_TABLE = 1066;
 const ERROR_RECORD_NOT_FOUND = 1067;
@@ -1003,6 +1004,7 @@ const FE_SUBRECORD_TABLE_CLASS = 'subrecordTableClass';
 const FE_FILE_BUTTON_TEXT = 'fileButtonText';
 const FE_FILE_BUTTON_TEXT_DEFAULT = 'Choose File';
 const FE_INPUT_TYPE = 'inputType';
+const FE_STEP = 'step';
 
 const FE_IMAGE_CUT_RESIZE_WIDTH = 'resizeWidth';
 const FE_IMAGE_CUT_KEEP_ORIGINAL = 'keepOriginal';
@@ -1079,6 +1081,8 @@ const HTML_ATTR_ID = 'id';
 const HTML_ATTR_NAME = 'name';
 const HTML_ATTR_CLASS = 'class';
 
+const HTML_INPUT_TYPE_NUMBER = 'number';
+
 const SHEBANG_REPORT = '#!report';
 
 // SUPPORT
diff --git a/extension/qfq/qfq/form/FormAction.php b/extension/qfq/qfq/form/FormAction.php
index dd57b7ffe..c816f8dc3 100644
--- a/extension/qfq/qfq/form/FormAction.php
+++ b/extension/qfq/qfq/form/FormAction.php
@@ -299,10 +299,13 @@ class FormAction {
             return;
         }
 
+        if($fe[FE_EXPECT_RECORDS]===''){
+            throw new UserFormException("Missing parameter '" . FE_EXPECT_RECORDS . "'", ERROR_MISSING_EXPECT_RECORDS);
+        }
         $expect = $this->evaluate->parse($fe[FE_EXPECT_RECORDS]);
 
         if ($fe[FE_MESSAGE_FAIL] === '') {
-            throw new UserFormException("Missing error message. Column: " . FE_MESSAGE_FAIL, ERROR_MISSING_MESSAGE_FAIL);
+            throw new UserFormException("Missing parameter '" . FE_MESSAGE_FAIL . "'", ERROR_MISSING_MESSAGE_FAIL);
         }
 
         // Do the check
diff --git a/extension/qfq/qfq/helper/Support.php b/extension/qfq/qfq/helper/Support.php
index ca924704f..bfe6c7665 100644
--- a/extension/qfq/qfq/helper/Support.php
+++ b/extension/qfq/qfq/helper/Support.php
@@ -867,7 +867,7 @@ class Support {
             case 'mediumint':
             case 'int':
             case 'bigint':
-                $inputType = 'number';
+                $inputType = HTML_INPUT_TYPE_NUMBER;
                 $arr = $control[$token];
                 if ($sign == 'signed') {
                     $min = $arr[0];
@@ -887,7 +887,7 @@ class Support {
                 break;
 
             case 'bit':
-                $inputType = 'number';
+                $inputType = HTML_INPUT_TYPE_NUMBER;
                 $checkType = SANITIZE_ALLOW_DIGIT;
                 break;
 
-- 
GitLab