From 16408b47a4ea8f85cf7cf08762b2d325abe76cdb Mon Sep 17 00:00:00 2001
From: Carsten  Rose <carsten.rose@math.uzh.ch>
Date: Thu, 14 Apr 2016 08:10:47 +0200
Subject: [PATCH] After creating a new record and still open form: a new sip is
 created and transfered to client. Attention: 'new + save + modify + save
 again' still create two records (error). AbstractBuildForm.php: buildNewSip()
 renamed to buildHiddenSip(). Call of buildHiddenSip() moved from tail() to
 process(). buildHiddenSip() returns JSON code too. BuildFormBootstrap.php,
 BuildFromPlain.php, BuildFormTable.php : removed call buildNewSip()
 QuickFormQuery.php: After save, reload current record and create new sip
 Save.php: process() returns id of affected record.

---
 extension/qfq/qfq/AbstractBuildForm.php  | 115 +++++++++++++----------
 extension/qfq/qfq/BuildFormBootstrap.php |   4 +-
 extension/qfq/qfq/BuildFormPlain.php     |   2 +-
 extension/qfq/qfq/BuildFormTable.php     |   2 +-
 extension/qfq/qfq/QuickFormQuery.php     |  56 ++++++++++-
 extension/qfq/qfq/Save.php               |  22 +++--
 6 files changed, 135 insertions(+), 66 deletions(-)

diff --git a/extension/qfq/qfq/AbstractBuildForm.php b/extension/qfq/qfq/AbstractBuildForm.php
index 43990df36..6af125ba2 100644
--- a/extension/qfq/qfq/AbstractBuildForm.php
+++ b/extension/qfq/qfq/AbstractBuildForm.php
@@ -118,7 +118,7 @@ abstract class AbstractBuildForm {
      * @throws DbException
      * @throws \qfq\UserFormException
      */
-    public function process($mode) {
+    public function process($mode, $htmlElementNameIdZero = false) {
         $htmlHead = '';
         $htmlTail = '';
         $htmlSubrecords = '';
@@ -145,12 +145,14 @@ abstract class AbstractBuildForm {
                 $json[] = $jsonTmp;
             }
         } else {
-            $htmlElements = $this->elements($this->store->getVar(SIP_RECORD_ID, STORE_SIP), $filter, 0, $json, $modeCollectFe);
+            $htmlElements = $this->elements($this->store->getVar(SIP_RECORD_ID, STORE_SIP), $filter, 0, $json, $modeCollectFe, $htmlElementNameIdZero);
         }
 
+        $htmlSip = $this->buildHiddenSip($json);
+
         // </form>
 
-        return ($mode === FORM_LOAD) ? $htmlHead . $htmlElements . $htmlTail . $htmlSubrecords : $json;
+        return ($mode === FORM_LOAD) ? $htmlHead . $htmlElements . $htmlSip . $htmlTail . $htmlSubrecords : $json;
     }
 
     /**
@@ -300,7 +302,8 @@ abstract class AbstractBuildForm {
      * @throws DbException
      * @throws \qfq\UserFormException
      */
-    public function elements($recordId, $filter = FORM_ELEMENTS_NATIVE, $feIdContainer = 0, &$json, $modeCollectFe = FLAG_DYNAMIC_UPDATE) {
+    public function elements($recordId, $filter = FORM_ELEMENTS_NATIVE, $feIdContainer = 0, &$json,
+                             $modeCollectFe = FLAG_DYNAMIC_UPDATE, $htmlElementNameIdZero = false) {
         $html = '';
 
         // get current data record
@@ -328,9 +331,12 @@ abstract class AbstractBuildForm {
             $formElement = $evaluate->parseArray($fe, $debugStack);
 
             // Get default value
-            $value = ($formElement['value'] === '') ? $this->store->getVar($formElement['name'], STORE_USE_DEFAULT, $formElement['checkType']) : $formElement['value'];
+            $value = ($formElement['value'] === '') ? $this->store->getVar($formElement['name'], STORE_USE_DEFAULT,
+                $formElement['checkType']) : $formElement['value'];
 
-            $htmlFormElementId = HelperFormElement::buildFormElementId($formElement['name'], $recordId);
+            // Typically: $htmlElementNameIdZero = true
+            // After Saving a record, staying on the form, the FormElements on the Client are still known as '<feName>:0'.
+            $htmlFormElementId = HelperFormElement::buildFormElementId($formElement['name'], ($htmlElementNameIdZero) ? 0 : $recordId);
 
             // Construct Marshaller Name: buildElement
             $buildElementFunctionName = 'build' . $this->buildElementFunctionName[$formElement['type']];
@@ -373,6 +379,57 @@ abstract class AbstractBuildForm {
         return $html;
     }
 
+    /**
+     * Create a hidden sip, based on latest STORE_SIP Values. Return complete HTML 'hidden' element.
+     *
+     * @param $json
+     * @return string  <input type='hidden' name='s' value='<sip>'>
+     * @throws CodeException
+     * @throws \qfq\UserFormException
+     */
+    public function buildHiddenSip(&$json) {
+        $sipArray = $this->store->getStore(STORE_SIP);
+        unset($sipArray[SIP_SIP]);
+        unset($sipArray[SIP_URLPARAM]);
+
+        $queryString = Support::arrayToQueryString($sipArray);
+        $sip = $this->store->getSipInstance();
+
+        $sipValue = $sip->queryStringToSip($queryString, RETURN_SIP);
+
+        $json[] = $this->getJsonElementUpdate(CLIENT_SIP, $sipValue, '');
+
+        return $this->buildNativeHidden(CLIENT_SIP, $sipValue);
+    }
+
+    /**
+     * @param $htmlFormElementId
+     * @param string|array $value
+     * @param string $mode disabled|readonly|''
+     * @return array
+     */
+    private function getJsonElementUpdate($htmlFormElementId, $value, $mode) {
+        $json = array();
+
+        $json['form-element'] = $htmlFormElementId;
+        $json['value'] = $value;
+        $json['disabled'] = ($mode === 'disabled');
+        $json['readonly'] = ($mode === 'readonly');
+
+        return $json;
+    }
+
+    /**
+     * Builds a real HTML hidden form element. Useful for checkboxes, Multiple-Select and Radios.
+     *
+     * @param $htmlFormElementId
+     * @param $value
+     * @return string
+     */
+    public function buildNativeHidden($htmlFormElementId, $value) {
+        return '<input type="hidden" name="' . $htmlFormElementId . '" value="' . htmlentities($value) . '">';
+    }
+
     /**
      * Takes the current SIP ('form' and additional parameter), set SIP_RECORD_ID=0 and create a new 'NewRecordUrl'.
      *
@@ -621,23 +678,6 @@ abstract class AbstractBuildForm {
         return $attribute;
     }
 
-    /**
-     * @param $htmlFormElementId
-     * @param string|array $value
-     * @param $mode
-     * @return array
-     */
-    private function getJsonElementUpdate($htmlFormElementId, $value, $mode) {
-        $json = array();
-
-        $json['form-element'] = $htmlFormElementId;
-        $json['value'] = $value;
-        $json['disabled'] = ($mode === 'disabled');
-        $json['readonly'] = ($mode === 'readonly');
-
-        return $json;
-    }
-
     /**
      * Builds HTML 'checkbox' element.
      *
@@ -879,17 +919,6 @@ abstract class AbstractBuildForm {
         return $html;
     }
 
-    /**
-     * Builds a real HTML hidden form element. Useful for checkboxes, Multiple-Select and Radios.
-     *
-     * @param $htmlFormElementId
-     * @param $value
-     * @return string
-     */
-    public function buildNativeHidden($htmlFormElementId, $value) {
-        return '<input type="hidden" name="' . $htmlFormElementId . '" value="' . htmlentities($value) . '">';
-    }
-
     /**
      * Build as many Checkboxes as items.
      *
@@ -1513,24 +1542,6 @@ abstract class AbstractBuildForm {
         return $html;
     }
 
-    /**
-     * Create a new sip, based on latest STORE_SIP Values. Return complete HTML 'hidden' element.
-     *
-     * @return string
-     */
-    public function buildNewSip() {
-        $sipArray = $this->store->getStore(STORE_SIP);
-        unset($sipArray[SIP_SIP]);
-        unset($sipArray[SIP_URLPARAM]);
-
-        $queryString = Support::arrayToQueryString($sipArray);
-        $sip = $this->store->getSipInstance();
-
-        $sipValue = $sip->queryStringToSip($queryString, RETURN_SIP);
-
-        return $this->buildNativeHidden(CLIENT_SIP, $sipValue);
-    }
-
     /**
      * @param $table
      * @param $recordId
diff --git a/extension/qfq/qfq/BuildFormBootstrap.php b/extension/qfq/qfq/BuildFormBootstrap.php
index ff688059f..14cb2ebea 100644
--- a/extension/qfq/qfq/BuildFormBootstrap.php
+++ b/extension/qfq/qfq/BuildFormBootstrap.php
@@ -296,13 +296,11 @@ class BuildFormBootstrap extends AbstractBuildForm {
      */
     public function tail() {
         $html = '';
-        $html .= $this->buildNewSip();
+//        $html .= $this->buildNewSip();
         $deleteUrl = '';
 
         $formId = $this->getFormId();
 
-        // TODO: bootstrap. See BuildFormTable.tail()
-
         $html .= '</div> <!--class="tab-content" -->';  //  <div class="tab-content">
 //        $html .= '<input type="submit" value="Submit">';
 
diff --git a/extension/qfq/qfq/BuildFormPlain.php b/extension/qfq/qfq/BuildFormPlain.php
index b115932ec..72d59b0aa 100644
--- a/extension/qfq/qfq/BuildFormPlain.php
+++ b/extension/qfq/qfq/BuildFormPlain.php
@@ -87,7 +87,7 @@ class BuildFormPlain extends AbstractBuildForm {
     public function tail() {
         $html = '';
 
-        $html .= $this->buildNewSip();
+//        $html .= $this->buildNewSip();
 
         $html .= $this->wrapItem(WRAP_SETUP_INPUT, '<input type="submit" value="Submit">');
         $html = $this->wrapItem(WRAP_SETUP_ELEMENT, $html);
diff --git a/extension/qfq/qfq/BuildFormTable.php b/extension/qfq/qfq/BuildFormTable.php
index f97880371..e47e44627 100644
--- a/extension/qfq/qfq/BuildFormTable.php
+++ b/extension/qfq/qfq/BuildFormTable.php
@@ -130,7 +130,7 @@ class BuildFormTable extends AbstractBuildForm {
         $html .= $this->wrapItem(WRAP_SETUP_INPUT, '<input type="submit" value="Submit">');
         $html = $this->wrapItem(WRAP_SETUP_ELEMENT, $html);
         $html .= '</table>';
-        $html .= $this->buildNewSip();
+//        $html .= $this->buildNewSip();
         $html .= '</form>';
         $html .= '</div>'; // main <div class=...> around everything
 
diff --git a/extension/qfq/qfq/QuickFormQuery.php b/extension/qfq/qfq/QuickFormQuery.php
index 399c3892d..9e5b70ffb 100644
--- a/extension/qfq/qfq/QuickFormQuery.php
+++ b/extension/qfq/qfq/QuickFormQuery.php
@@ -226,10 +226,23 @@ class QuickFormQuery {
 
             case FORM_SAVE:
                 $save = new Save($this->formSpec, $this->feSpecAction, $this->feSpecNative);
-                $save->process();
+                $rc = $save->process();
+
+                // Reload fresh saved record and fill STORE_RECORD with it
+                $record = $this->db->sql("SELECT * FROM " . $this->formSpec['tableName'] . " WHERE id = ?", ROW_EXPECT_1, [$rc]);
+                $this->store->setVarArray($record, STORE_RECORD, true);
+
+                $htmlElementNameIdZero = false;
+                // Retrieve current STORE_SIP.
+                $sipArray = $this->store->getStore(STORE_SIP);
+                if ($sipArray[SIP_RECORD_ID] == 0) {
+                    // After insert: a new SIP for the new record id is required
+                    $this->newRecordCreateSip($sipArray, $rc);
+                    $htmlElementNameIdZero = true;
+                }
 
                 // Retrieve FE Values as JSON
-                $data = $build->process($mode);
+                $data = $build->process($mode, $htmlElementNameIdZero);
                 break;
 
             default:
@@ -340,7 +353,7 @@ class QuickFormQuery {
                 break;
             case FORM_SAVE:
             case FORM_UPDATE:
-                $store = STORE_SIP;
+            $store = STORE_SIP;
                 break;
             default:
                 throw new CodeException("Unknown mode: $mode.", ERROR_UNKNOWN_MODE);
@@ -420,6 +433,43 @@ class QuickFormQuery {
         return $sipFound;
     }
 
+    /**
+     * @param $sipArray
+     * @param $recordId
+     */
+    private function newRecordCreateSip($sipArray, $recordId) {
+
+        $tmpParam = array();
+
+        foreach ($sipArray as $key => $value) {
+            switch ($key) {
+                case SIP_SIP:
+                case SIP_URLPARAM:
+                case SIP_TABLE:
+                    continue;
+
+                case SIP_RECORD_ID:
+                    $tmpParam[SIP_RECORD_ID] = $recordId;
+                    break;
+                default:
+                    // further vars stored in old SIP (form, maybe default values)
+                    $tmpParam[$key] = $value;
+                    break;
+            }
+        }
+
+        // Construct fake urlparam
+        $tmpUrlparam = OnArray::toString($tmpParam);
+
+        // Create a SIP which has never been passed by URL - further processing might expect this to exist.
+        $sip = store::getSipInstance()->queryStringToSip($tmpUrlparam, RETURN_SIP);
+        $this->store->setVar(CLIENT_SIP, $sip, STORE_CLIENT);
+
+        // Overwrite SIP Store
+        $tmpParam[SIP_SIP] = $sip;
+        $this->store->setVarArray($tmpParam, STORE_SIP, true);
+    }
+
     /**
      * Process the SQL Queries from bodytext. Return the output.
      *
diff --git a/extension/qfq/qfq/Save.php b/extension/qfq/qfq/Save.php
index 38a261fdf..e4cc4c38b 100644
--- a/extension/qfq/qfq/Save.php
+++ b/extension/qfq/qfq/Save.php
@@ -52,22 +52,25 @@ class Save {
      * @throws UserFormException
      */
     public function process() {
+        $rc = 0;
 
         if ($this->formSpec['multiMode'] !== 'none') {
 
             $parentRecords = $this->db->sql($this->formSpec['multiSql']);
             foreach ($parentRecords as $row) {
                 $this->store->setVarArray($row, STORE_PARENT_RECORD, true);
-                $this->elements($row['_id']);
+                $rc = $this->elements($row['_id']);
             }
         } else {
-            $this->elements($this->store->getVar(SIP_RECORD_ID, STORE_SIP . STORE_ZERO));
+            $rc = $this->elements($this->store->getVar(SIP_RECORD_ID, STORE_SIP . STORE_ZERO));
         }
+
+        return $rc;
     }
 
     /**
      * @param $recordId
-     * @return string
+     * @return int   record id (in case of insert, it's different from $recordId)
      * @throws CodeException
      * @throws DbException
      * @throws UserFormException
@@ -98,7 +101,14 @@ class Save {
             }
         }
 
-        return ($recordId == 0) ? $this->insertRecord($this->formSpec['tableName'], $newValues) : $this->updateRecord($this->formSpec['tableName'], $newValues, $recordId);
+        if ($recordId == 0) {
+            $rc = $this->insertRecord($this->formSpec['tableName'], $newValues);
+        } else {
+            $this->updateRecord($this->formSpec['tableName'], $newValues, $recordId);
+            $rc = $recordId;
+        }
+
+        return $rc;
     }
 
     /**
@@ -119,7 +129,7 @@ class Save {
      * Insert new record in table $this->formSpec['tableName'].
      *
      * @param array $values
-     * @return string
+     * @return int  last insert id
      * @throws DbException
      */
     public function insertRecord($tableName, array $values) {
@@ -142,7 +152,7 @@ class Save {
      * @param string $tableName
      * @param array $values
      * @param int $recordId
-     * @return bool|int     false if $values is empty
+     * @return bool|int     false if $values is empty, else affectedrows
      * @throws CodeException
      * @throws DbException
      */
-- 
GitLab