From 9510c9faf203be33b60e9775c047a687e58e6a88 Mon Sep 17 00:00:00 2001
From: crose <carsten.rose@math.uzh.ch>
Date: Tue, 14 Mar 2017 12:30:10 +0100
Subject: [PATCH] typeAhead: very first implementation of typeAhead

---
 extension/RELEASE.txt                   |  3 +
 extension/qfq/api/typeahead.php         | 35 ++++++++++
 extension/qfq/qfq/AbstractBuildForm.php |  9 ++-
 extension/qfq/qfq/Constants.php         | 17 ++++-
 extension/qfq/qfq/Database.php          | 49 ++++++++-----
 extension/qfq/qfq/form/TypeAhead.php    | 91 +++++++++++++++++++++++++
 extension/qfq/qfq/store/Config.php      |  6 +-
 7 files changed, 186 insertions(+), 24 deletions(-)
 create mode 100644 extension/qfq/api/typeahead.php
 create mode 100644 extension/qfq/qfq/form/TypeAhead.php

diff --git a/extension/RELEASE.txt b/extension/RELEASE.txt
index ea01a5905..4127f5d0a 100644
--- a/extension/RELEASE.txt
+++ b/extension/RELEASE.txt
@@ -89,6 +89,9 @@ Changes
 
    * UPDATE EXISTING TypoScript TEMPLATES of QFQ Installation.
 
+ * Variable field parameter has changed. old '_filename', new 'filename'
+
+   * UPDATE `FormElement` SET parameter = REPLACE(parameter, '_filename', 'filename')
 
 Features
 ^^^^^^^^
diff --git a/extension/qfq/api/typeahead.php b/extension/qfq/api/typeahead.php
new file mode 100644
index 000000000..e635c8ec7
--- /dev/null
+++ b/extension/qfq/api/typeahead.php
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: ep
+ * Date: 12/23/15
+ * Time: 6:17 PM
+ */
+
+
+namespace qfq;
+
+use qfq;
+
+require_once(__DIR__ . '/../qfq/form/TypeAhead.php');
+require_once(__DIR__ . '/../qfq/Constants.php');
+
+
+/**
+ * Return JSON encoded answer
+ *
+ */
+
+
+try {
+    $qfq = new \qfq\TypeAhead();
+
+    $answer = $qfq->process();
+
+} catch (\Exception $e) {
+    $answer[API_MESSAGE] = "Generic Exception: " . $e->getMessage();
+}
+
+header("Content-Type: application/json");
+echo json_encode($answer);
+
diff --git a/extension/qfq/qfq/AbstractBuildForm.php b/extension/qfq/qfq/AbstractBuildForm.php
index 686236080..98499b4b9 100644
--- a/extension/qfq/qfq/AbstractBuildForm.php
+++ b/extension/qfq/qfq/AbstractBuildForm.php
@@ -725,10 +725,17 @@ abstract class AbstractBuildForm {
     public function buildInput(array $formElement, $htmlFormElementName, $value, array &$json, $mode = FORM_LOAD) {
         $textarea = '';
         $attribute = '';
+        $class = 'form-control';
+
+        if (isset($formElement[FE_TYPE_AHEAD_SQL])) {
+            $class .= ' qfq-type-ahead';
+            $dataSip = $this->sip->queryStringToSip(TYPE_AHEAD_SELECT . '=' . $formElement[FE_TYPE_AHEAD_SQL], RETURN_SIP);
+            $attribute .= Support::doAttribute(DATA_SIP, $dataSip);
+        }
 
         $attribute .= Support::doAttribute('id', $formElement[FE_HTML_ID]);
         $attribute .= Support::doAttribute('name', $htmlFormElementName);
-        $attribute .= Support::doAttribute('class', 'form-control');
+        $attribute .= Support::doAttribute('class', $class);
 
         if (isset($formElement[FE_RETYPE_SOURCE_NAME])) {
             $htmlFormElementNamePrimary = str_replace(RETYPE_FE_NAME_EXTENSION, '', $htmlFormElementName);
diff --git a/extension/qfq/qfq/Constants.php b/extension/qfq/qfq/Constants.php
index 727f712ea..8b0de6dfa 100644
--- a/extension/qfq/qfq/Constants.php
+++ b/extension/qfq/qfq/Constants.php
@@ -373,7 +373,16 @@ const VAR_FILE_DESTINATION = 'fileDestination';
 const VAR_SLAVE_ID = ACTION_KEYWORD_SLAVE_ID;
 const VAR_FILENAME = 'filename'; // Original filename of an uploaded file.
 
-//const RECORD_ID_NEW = -1;
+
+// Class DB can operate in these modes
+const MODE_DB_REGULAR = 'regular';
+const MODE_DB_NO_LOG = 'noLog';
+
+// CLASS TypeAhead
+const TYPE_AHEAD_API_QUERY = 'query';  // Name of parameter in API call of typeahead.php?query=...&s=...
+const TYPE_AHEAD_API_SIP = 'sip';  // Name of parameter in API call of typeahead.php?query=...&s=...
+const TYPE_AHEAD_SELECT = 'select'; // Value of FE_TYPE_AHEAD_SQL, stored in a SIP under the name TYPE_AHEAD_SELECT.
+const TYPE_AHEAD_LDAP = 'ldap'; // Value of FE_TYPE_AHEAD_LDAP, stored in a SIP under the name TYPE_AHEAD_LDAP.
 
 // TOKEN evaluate
 const TOKEN_ESCAPE_SINGLE_TICK = 's';
@@ -435,6 +444,8 @@ const DATA_HIDDEN = 'data-hidden';
 const DATA_DISABLED = 'data-disabled';
 const DATA_REQUIRED = 'data-required';
 
+const DATA_SIP = 'data-sip'; // Used for typeAhead
+
 const API_ANSWER_STATUS_SUCCESS = 'success';
 const API_ANSWER_STATUS_ERROR = 'error';
 const API_ANSWER_REDIRECT_CLIENT = 'client';
@@ -580,6 +591,7 @@ const FE_TEMPLATE_GROUP_CLASS = 'tgClass';
 const FE_TEMPLATE_GROUP_DEFAULT_MAX_LENGTH = 5;
 const FE_TEMPLATE_GROUP_NAME_PATTERN = '%d';
 const FE_BUTTON_CLASS = 'buttonClass';
+const FE_TYPE_AHEAD_SQL = 'typeAheadSql';
 const RETYPE_FE_NAME_EXTENSION = 'RETYPE';
 
 const FE_HTML_ID = 'htmlId'; // Will be dynamically computed during runtime.
@@ -699,4 +711,5 @@ const COLUMN_PAGEE = "pagee";
 const COLUMN_PAGEH = "pageh";
 const COLUMN_PAGEI = "pagei";
 const COLUMN_PAGEN = "pagen";
-const COLUMN_PAGES = "pages";
\ No newline at end of file
+const COLUMN_PAGES = "pages";
+
diff --git a/extension/qfq/qfq/Database.php b/extension/qfq/qfq/Database.php
index 18f23d48d..bda86fc36 100644
--- a/extension/qfq/qfq/Database.php
+++ b/extension/qfq/qfq/Database.php
@@ -20,6 +20,7 @@ require_once(__DIR__ . '/exceptions/CodeException.php');
 require_once(__DIR__ . '/exceptions/DbException.php');
 
 require_once(__DIR__ . '/store/Store.php');
+require_once(__DIR__ . '/store/Config.php');
 require_once(__DIR__ . '/helper/Support.php');
 require_once(__DIR__ . '/helper/Logger.php');
 require_once(__DIR__ . '/helper/BindParam.php');
@@ -62,16 +63,29 @@ class Database {
      * @throws CodeException
      * @throws UserFormException
      */
-    public function __construct() {
-        $this->store = Store::getInstance();
+    public function __construct($mode = MODE_DB_REGULAR) {
+        $dbInit = '';
+
+        switch ($mode) {
+            case MODE_DB_REGULAR:
+                $this->store = Store::getInstance();
+                $config = $this->store->getStore(STORE_SYSTEM);
+                $this->sqlLog = $this->store->getVar(SYSTEM_SQL_LOG, STORE_SYSTEM);
+                $dbInit = $this->store->getVar(SYSTEM_DB_INIT, STORE_SYSTEM);
+                break;
+            case MODE_DB_NO_LOG:
+                $configClass = new Config();
+                $config = $configClass->readConfig();
+                break;
+            default:
+                throw new \qfq\CodeException('Unknown mode: ' . $mode, ERROR_UNKNOWN_MODE);
+        }
 
         if ($this->mysqli === null) {
-            $this->mysqli = $this->dbConnect();
+            $this->mysqli = $this->dbConnect($config);
         }
-        $this->sqlLog = $this->store->getVar(SYSTEM_SQL_LOG, STORE_SYSTEM);
 
         // DB Init
-        $dbInit = $this->store->getVar(SYSTEM_DB_INIT, STORE_SYSTEM);
         if ($dbInit !== false && $dbInit != '') {
             $this->sql($dbInit);
         }
@@ -84,18 +98,13 @@ class Database {
      * @return \mysqli
      * @throws UserFormException
      */
-    private function dbConnect() {
+    private function dbConnect($config) {
         $mysqli = null;
 
-        $dbuser = $this->store->getVar(SYSTEM_DB_USER, STORE_SYSTEM);
-        $dbserver = $this->store->getVar(SYSTEM_DB_SERVER, STORE_SYSTEM);
-        $dbpw = $this->store->getVar(SYSTEM_DB_PASSWORD, STORE_SYSTEM);
-        $db = $this->store->getVar(SYSTEM_DB_NAME, STORE_SYSTEM);
-
-        $mysqli = new \mysqli($dbserver, $dbuser, $dbpw, $db);
+        $mysqli = new \mysqli($config[SYSTEM_DB_SERVER], $config[SYSTEM_DB_USER], $config[SYSTEM_DB_PASSWORD], $config[SYSTEM_DB_NAME]);
 
         if ($mysqli->connect_error) {
-            throw new UserFormException ("Error open Database 'mysql:host=" . $dbserver . ";dbname=" . $db . ";dbuser=" . $dbuser . "'': " . $mysqli->connect_errno . PHP_EOL . $mysqli->connect_error, ERROR_DB_OPEN);
+            throw new UserFormException ("Error open Database 'mysql:host=" . $config[SYSTEM_DB_SERVER] . ";dbname=" . $config[SYSTEM_DB_NAME] . ";dbuser=" . $config[SYSTEM_DB_USER] . "'': " . $mysqli->connect_errno . PHP_EOL . $mysqli->connect_error, ERROR_DB_OPEN);
         }
 
         return $mysqli;
@@ -239,8 +248,10 @@ class Database {
         $result = 0;
         $stat = array();
 
-        $this->store->setVar(SYSTEM_SQL_FINAL, $sql, STORE_SYSTEM);
-        $this->store->setVar(SYSTEM_SQL_PARAM_ARRAY, $parameterArray, STORE_SYSTEM);
+        if ($this->store !== null) {
+            $this->store->setVar(SYSTEM_SQL_FINAL, $sql, STORE_SYSTEM);
+            $this->store->setVar(SYSTEM_SQL_PARAM_ARRAY, $parameterArray, STORE_SYSTEM);
+        }
 
         // Logfile
         $this->dbLog($sqlLogMode, $sql, $parameterArray);
@@ -308,7 +319,9 @@ class Database {
                 break;
         }
 
-        $this->store->setVar(SYSTEM_SQL_COUNT, $count, STORE_SYSTEM);
+        if ($this->store !== null) {
+            $this->store->setVar(SYSTEM_SQL_COUNT, $count, STORE_SYSTEM);
+        }
 
         // Logfile
         $this->dbLog($sqlLogMode, $msg);
@@ -347,7 +360,7 @@ class Database {
 
         $status = '';
 
-        $sqlLogMode = $this->store->getVar(SYSTEM_SQL_LOG_MODE, STORE_SYSTEM);
+        $sqlLogMode = ($this->store === null) ? $this->store->getVar(SYSTEM_SQL_LOG_MODE, STORE_SYSTEM) : SQL_LOG_MODE_ERROR;
 
         switch ($mode) {
             case SQL_LOG_MODE_ALL:
@@ -367,7 +380,7 @@ class Database {
         }
 
         // Client IP Address
-        $remoteAddress = $this->store->getVar(CLIENT_REMOTE_ADDRESS, STORE_CLIENT);
+        $remoteAddress = ($this->store === null) ? $this->store->getVar(CLIENT_REMOTE_ADDRESS, STORE_CLIENT) : '0.0.0.0';
 
         $msg = '[' . date('Y.m.d H:i:s O') . '][' . $remoteAddress . ']';
 
diff --git a/extension/qfq/qfq/form/TypeAhead.php b/extension/qfq/qfq/form/TypeAhead.php
new file mode 100644
index 000000000..d5152d00d
--- /dev/null
+++ b/extension/qfq/qfq/form/TypeAhead.php
@@ -0,0 +1,91 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: crose
+ * Date: 3/13/17
+ * Time: 9:29 PM
+ */
+
+namespace qfq;
+
+require_once(__DIR__ . '/../store/Sip.php');
+//require_once(__DIR__ . '/store/FillStoreForm.php');
+require_once(__DIR__ . '/../store/Session.php');
+require_once(__DIR__ . '/../Constants.php');
+//require_once(__DIR__ . '/Save.php');
+//require_once(__DIR__ . '/helper/KeyValueStringParser.php');
+//require_once(__DIR__ . '/helper/HelperFormElement.php');
+//require_once(__DIR__ . '/exceptions/UserFormException.php');
+//require_once(__DIR__ . '/exceptions/CodeException.php');
+//require_once(__DIR__ . '/exceptions/DbException.php');
+//require_once(__DIR__ . '/exceptions/ErrorHandler.php');
+require_once(__DIR__ . '/../Database.php');
+//require_once(__DIR__ . '/Evaluate.php');
+//require_once(__DIR__ . '/BuildFormPlain.php');
+//require_once(__DIR__ . '/BuildFormTable.php');
+//require_once(__DIR__ . '/BuildFormBootstrap.php');
+//require_once(__DIR__ . '/report/Report.php');
+//require_once(__DIR__ . '/BodytextParser.php');
+//require_once(__DIR__ . '/Delete.php');
+//require_once(__DIR__ . '/form/FormAction.php');
+
+
+class TypeAhead {
+
+    /**
+     * @var Database instantiated class
+     */
+    protected $db = null;
+
+    /**
+     * @var array
+     */
+    protected $vars = array();
+
+    /**
+     *
+     */
+    public function __construct($phpUnit = false) {
+
+        if (!isset($_GET[TYPE_AHEAD_API_QUERY]) || !isset($_GET[TYPE_AHEAD_API_SIP])) {
+            throw new CodeException('Missing GET parameter "' . TYPE_AHEAD_API_SIP . '" or "' . TYPE_AHEAD_API_QUERY . '"');
+        }
+
+        $this->vars[TYPE_AHEAD_API_QUERY] = $_GET[TYPE_AHEAD_API_QUERY];
+        $this->vars[TYPE_AHEAD_API_SIP] = $_GET[TYPE_AHEAD_API_SIP];
+
+        $session = Session::getInstance($phpUnit);
+
+        $this->db = new Database();
+
+    }
+
+    /**
+     * @return array|int
+     * @throws CodeException
+     * @throws DbException
+     * @throws UserFormException
+     */
+    public function process() {
+        //test
+        $arr = array();
+        $values = array();
+
+        $sipClass = new Sip();
+
+        $sipVars = $sipClass->getVarsFromSip($this->vars[TYPE_AHEAD_API_SIP]);
+
+        $query = '%' . $this->vars[TYPE_AHEAD_API_QUERY] . '%';
+        $cnt = substr_count($sipVars[TYPE_AHEAD_SELECT], '?');
+        for ($ii = 0; $ii < $cnt; $ii++) {
+            $values[] = $query;
+        }
+
+        if (isset($sipVars[TYPE_AHEAD_SELECT])) {
+            $arr = $this->db->sql($sipVars[TYPE_AHEAD_SELECT], ROW_REGULAR, $values);
+        }
+
+        return $arr;
+    }
+
+}
\ No newline at end of file
diff --git a/extension/qfq/qfq/store/Config.php b/extension/qfq/qfq/store/Config.php
index fe646b5eb..92316a589 100644
--- a/extension/qfq/qfq/store/Config.php
+++ b/extension/qfq/qfq/store/Config.php
@@ -17,10 +17,10 @@ class Config {
     /**
      * Read config.qfq.ini.
      *
-     * @throws CodeException
-     * @throws qfq\UserFormException
+     * @param string $fileConfigIni
+     * @return array
+     * @throws UserFormException
      */
-
     public function readConfig($fileConfigIni = '') {
 
         if ($fileConfigIni == '') {
-- 
GitLab