Commit f116b3b5 authored by Carsten  Rose's avatar Carsten Rose

Merge branch 'master' into F10013CodeMirror

parents 59844782 ced8891f
Pipeline #3230 passed with stages
in 5 minutes and 1 second
......@@ -22,7 +22,7 @@
Release
=======
Version 19.x.x
Version 20.x.x
--------------
Date: <date>
......@@ -36,6 +36,63 @@ Features
Bug Fixes
^^^^^^^^^
Version 20.2.0
--------------
Date: 02.02.2020
Notes
^^^^^
* Add new keyword 'render' in tt-content Report and QFQ config. 'render' will control if a) only Form OR Report will be
rendered (render=single) or b) as previous Form AND Report together (render=both).
Advantage: with 'render=single' no more SELECT ... FROM (SELECT '') AS fake WHERE '{{form:SE}}'=''.
Attention: NEW default behaviour in new QFQ installations - render=single. Behaviour in old installations is unchanged.
* tt-content records with 'render = api' can stay on the same page as the link to get the content (e.g. Excel Export).
* Change default doc page to qfq.io/doc.
* Add new specialColumnName: '_noWrap' - skips wrapping of fbeg,fsep,fend.
* First version of 'FullCalendar.io' - new SpecialColumnName will follow in the future.
Features
^^^^^^^^
* #9929 / New keyword '_noWrap' for column names (alias) - skips wrapping of fbeg/fskip/fend.
* #9905 / Keyword 'render' in Report. Final implementation. Doc updated.
* #9959 / Update QFQ Config on the fly.
* #9990 / Describe order of FormElement processing - https://qfq.io/doc/#form-process-order.
* #8658 / FullCalendar.io V3 implemented.
* #9535 / VerticalText new implementation.
* Manual.rst: Add list of icons. Enhance sendmail doc.
* Change color of qfq-info-* from blue to light-blue. Add qfq-primary, qfq-danger.
Bug Fixes
^^^^^^^^^
* #5869 / Table names not properly escaped.
* #10010 / FE.type=sendmail will now be fired together with fe.type=after* (not after).
* Fixed problem with border showing when qfq-color-white is set.
* Fix selenium tests, remove chromedriver from npm.
* Log problem that crashes qfq when calendar dependencies are missing.
* Fixed gruntfile problem.
Version 20.1.1
--------------
Date: 13.01.2020
Bug Fixes
^^^^^^^^^
* #7705 / Fix problem with wrong value after save and form update.
* #8587 / A form triggers a save only, if there are real table columns.
Version 20.1.0
--------------
Date: 09.01.2020
Notes
......
......@@ -11,7 +11,11 @@ Neue Versionsnummer
0) Fuer jede neue Version ein Ticket erstellen. Template: #6994
1) Alle offenen Branches auf **Develop** und dann auf **Master mergen**.
1) Alle offenen Branches auf **Develop** mergen.
1a) Develop auf **Master mergen**.
1b) Wechseln auf **Master**.
2) Die aktuellen Commits anschauen und wichtige Topics uebernehmen (git log > ~/qfq.log, alles bis zum letzten TAG anschauen):
......@@ -43,7 +47,7 @@ Neue Versionsnummer
**Achtung**: die Release Minor darf KEINE fuehrenden Nullen enthalten!!! Ansonsten funktioniert die Verteilung vie TER nicht.
**Auto**: ./setVersion.sh <MAJOR>.<MINOR>.<MICRO>
**Auto**: ./setVersion.sh 20.2.0
Manuell:
* extension/Documentation/_make/conf.py: release, version-
......@@ -59,12 +63,12 @@ Neue Versionsnummer
* Update the version number in this document (topic 6)
* Commit & Push new version changes to master branch:
New version 20.1.1
New version 20.2.0
6) **New Tag**:
git tag v20.1.1
git push -u origin v20.1.1
git tag v20.2.0
git push -u origin v20.2.0
7) Tickets:
* Schliessen und der QFQ Version zuweisen.
......
......@@ -22,7 +22,7 @@ Quick Form Query Extension
en
:Copyright:
2017-2019
2017-2020
:Authors:
Carsten Rose, Benjamin Baer, Marc Egger
......@@ -63,7 +63,7 @@ Quick Form Query Extension
**Extension Manual**
This documentation is for the TYPO3 extension qfq.
This documentation is for the TYPO3 extension **qfq**.
**Sitemap:**
......@@ -80,3 +80,4 @@ Quick Form Query Extension
README
License
Sitemap
This diff is collapsed.
......@@ -36,6 +36,50 @@ Features
Bug Fixes
^^^^^^^^^
Version 20.2.0
--------------
Date: 02.02.2020
Notes
^^^^^
* Add new keyword 'render' in tt-content Report and QFQ config. 'render' will control if a) only Form OR Report will be
rendered (render=single) or b) as previous Form AND Report together (render=both).
Advantage: with 'render=single' no more SELECT ... FROM (SELECT '') AS fake WHERE '{{form:SE}}'=''.
Attention: NEW default behaviour in new QFQ installations - render=single. Behaviour in old installations is unchanged.
* tt-content records with 'render = api' can stay on the same page as the link to get the content (e.g. Excel Export).
* Change default doc page to qfq.io/doc.
* Add new specialColumnName: '_noWrap' - skips wrapping of fbeg,fsep,fend.
* First version of 'FullCalendar.io' - new SpecialColumnName will follow in the future.
Features
^^^^^^^^
* #9929 / New keyword '_noWrap' for column names (alias) - skips wrapping of fbeg/fskip/fend.
* #9905 / Keyword 'render' in Report. Final implementation. Doc updated.
* #9959 / Update QFQ Config on the fly.
* #9990 / Describe order of FormElement processing - https://qfq.io/doc/#form-process-order.
* #8658 / FullCalendar.io V3 implemented.
* #9535 / VerticalText new implementation.
* Manual.rst: Add list of icons. Enhance sendmail doc.
* Change color of qfq-info-* from blue to light-blue. Add qfq-primary, qfq-danger.
Bug Fixes
^^^^^^^^^
* #10010 / FE.type=sendmail will now be fired together with fe.type=after* (not after).
* #5869 / Table names not properly escaped.
* #9638 / TextArea: Autosize - broken when using clipboard,
* Fixed problem with border showing when qfq-color-white is set.
* Fix selenium tests, remove chromedriver from npm.
* Log problem that crashes qfq when calendar dependencies are missing.
* Fixed gruntfile problem.
Version 20.1.1
--------------
......
......@@ -21,8 +21,8 @@
; you can use in 'conf.py'
project = QFQ - Quick Form Query
version = 20.1
release = 20.1.1
version = 20.2
release = 20.2.0
t3author = Carsten Rose
copyright = since 2017 by the author
......
......@@ -253,7 +253,7 @@ abstract class AbstractBuildForm {
$this->store->setStore($row, STORE_PARENT_RECORD, true);
$this->store->setVar(F_MULTI_COL_ID, $row[$idName], STORE_PARENT_RECORD); // In case '_id' is used, both '_id' and 'id' should be accessible.
$record = $this->dbArray[$this->dbIndexData]->sql('SELECT * FROM `' . $this->formSpec[F_TABLE_NAME] . '` WHERE id=' . $row[F_MULTI_COL_ID], ROW_EXPECT_1);
$record = $this->dbArray[$this->dbIndexData]->sql('SELECT * FROM `' . $this->formSpec[F_TABLE_NAME] . '` WHERE `id`=' . $row[F_MULTI_COL_ID], ROW_EXPECT_1);
$this->store->setStore($record, STORE_RECORD, true);
$jsonTmp = array();
......@@ -555,7 +555,7 @@ abstract class AbstractBuildForm {
$record = array();
if ($recordId != 0) {
$record = $this->dbArray[$this->dbIndexData]->sql("SELECT * FROM $tableName WHERE $primaryKey=?", ROW_EXPECT_1, [$recordId], "Record to load not found.");
$record = $this->dbArray[$this->dbIndexData]->sql("SELECT * FROM `$tableName` WHERE `$primaryKey`=?", ROW_EXPECT_1, [$recordId], "Record to load not found.");
}
return OnArray::getMd5($record);
......@@ -742,7 +742,7 @@ abstract class AbstractBuildForm {
$primaryKey = $this->formSpec[F_PRIMARY_KEY];
if ($recordId > 0 && $this->store->getVar($primaryKey, STORE_RECORD) === false) {
$tableName = $this->formSpec[F_TABLE_NAME];
$row = $this->dbArray[$this->dbIndexData]->sql("SELECT * FROM $tableName WHERE $primaryKey = ?", ROW_EXPECT_1,
$row = $this->dbArray[$this->dbIndexData]->sql("SELECT * FROM `$tableName` WHERE `$primaryKey` = ?", ROW_EXPECT_1,
array($recordId), "Form '" . $this->formSpec[F_NAME] . "' failed to load record '$primaryKey'='$recordId' from table '" .
$this->formSpec[F_TABLE_NAME] . "'.");
$this->store->setStore($row, STORE_RECORD);
......@@ -2108,7 +2108,7 @@ abstract class AbstractBuildForm {
} elseif (!empty($formElement[SUBRECORD_PARAMETER_FORM])) {
// Read table from form specified in subrecord
$formName = $formElement[SUBRECORD_PARAMETER_FORM];
$form = $this->dbArray[$this->dbIndexQfq]->sql("SELECT * FROM Form AS f WHERE f." . F_NAME . " LIKE ? AND f.deleted='no'",
$form = $this->dbArray[$this->dbIndexQfq]->sql("SELECT * FROM `Form` AS f WHERE `f`.`" . F_NAME . "` LIKE ? AND `f`.`deleted`='no'",
ROW_REGULAR, [$formName]);
if (count($form) > 0) {
$dndTable = $form[0][F_TABLE_NAME];
......@@ -2371,7 +2371,7 @@ abstract class AbstractBuildForm {
* @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]);
$row = $this->dbArray[$this->dbIndexQfq]->sql("SELECT `" . F_TABLE_NAME . "` FROM `Form` AS f WHERE `f`.`name` = ?", ROW_EXPECT_0_1, [$formName]);
if (isset($row[F_TABLE_NAME])) {
return $row[F_TABLE_NAME];
}
......
......@@ -252,7 +252,7 @@ class BuildFormBootstrap extends AbstractBuildForm {
break;
case 'formElement':
if (false !== ($formId = $this->store->getVar(FE_FORM_ID, STORE_SIP . STORE_RECORD))) {
$row = $this->dbArray[$this->dbIndexQfq]->sql("SELECT f.name FROM Form AS f WHERE id=" . $formId, ROW_EXPECT_1);
$row = $this->dbArray[$this->dbIndexQfq]->sql("SELECT `f`.`name` FROM `Form` AS f WHERE `id`=" . $formId, ROW_EXPECT_1);
$form = current($row);
}
break;
......
......@@ -63,15 +63,15 @@ const RETURN_URL = 'return_url';
const RETURN_SIP = 'return_sip';
const RETURN_ARRAY = 'return_array';
const SQL_FORM_ELEMENT_BY_ID = "SELECT * FROM FormElement AS fe WHERE fe.id = ?";
const SQL_FORM_ELEMENT_RAW = "SELECT * FROM FormElement AS fe WHERE fe.formId = ? AND fe.deleted = 'no' AND fe.enabled='yes' ORDER BY fe.ord, fe.id";
const SQL_FORM_ELEMENT_SPECIFIC_CONTAINER = "SELECT *, ? AS 'nestedInFieldSet' FROM FormElement AS fe WHERE fe.formId = ? AND fe.deleted = 'no' AND FIND_IN_SET(fe.class, ? ) AND fe.feIdContainer = ? AND fe.enabled='yes' ORDER BY fe.ord, fe.id";
const SQL_FORM_ELEMENT_ALL_CONTAINER = "SELECT *, ? AS 'nestedInFieldSet' FROM FormElement AS fe WHERE fe.formId = ? AND fe.deleted = 'no' AND FIND_IN_SET(fe.class, ? ) AND fe.enabled='yes' ORDER BY fe.ord, fe.id";
const SQL_FORM_ELEMENT_SIMPLE_ALL_CONTAINER = "SELECT fe.id, fe.feIdContainer, fe.name, fe.value, fe.label, fe.type, fe.encode, fe.checkType, fe.checkPattern, fe.mode, fe.modeSql, fe.parameter, fe.dynamicUpdate FROM FormElement AS fe, Form AS f WHERE f.name = ? AND f.id = fe.formId AND fe.deleted = 'no' AND fe.class = 'native' AND fe.enabled='yes' ORDER BY fe.ord, fe.id";
const SQL_FORM_ELEMENT_CONTAINER_TEMPLATE_GROUP = "SELECT fe.id, fe.name, fe.label, fe.maxLength, fe.parameter FROM FormElement AS fe, Form AS f WHERE f.name = ? AND f.id = fe.formId AND fe.deleted = 'no' AND fe.class = 'container' AND fe.type='templateGroup' AND fe.enabled='yes' ORDER BY fe.ord, fe.id";
const SQL_FORM_ELEMENT_TEMPLATE_GROUP_FE_ID = "SELECT * FROM FormElement AS fe WHERE fe.id = ? AND fe.deleted = 'no' AND fe.class = 'container' AND fe.type='templateGroup' AND fe.enabled='yes' ";
const SQL_FORM_ELEMENT_BY_ID = "SELECT * FROM `FormElement` AS fe WHERE `fe`.`id` = ?";
const SQL_FORM_ELEMENT_RAW = "SELECT * FROM `FormElement` AS `fe` WHERE `fe`.`formId` = ? AND `fe`.`deleted` = 'no' AND `fe`.`enabled`='yes' ORDER BY `fe`.`ord`, `fe`.`id`";
const SQL_FORM_ELEMENT_SPECIFIC_CONTAINER = "SELECT *, ? AS 'nestedInFieldSet' FROM `FormElement` AS fe WHERE `fe`.`formId` = ? AND `fe`.`deleted` = 'no' AND FIND_IN_SET(`fe`.`class`, ? ) AND `fe`.`feIdContainer` = ? AND `fe`.`enabled`='yes' ORDER BY `fe`.`ord`, `fe`.`id`";
const SQL_FORM_ELEMENT_ALL_CONTAINER = "SELECT *, ? AS 'nestedInFieldSet' FROM `FormElement` AS `fe` WHERE `fe`.`formId` = ? AND `fe`.`deleted` = 'no' AND FIND_IN_SET(`fe`.`class`, ? ) AND `fe`.`enabled`='yes' ORDER BY `fe`.`ord`, `fe`.`id`";
const SQL_FORM_ELEMENT_SIMPLE_ALL_CONTAINER = "SELECT `fe`.`id`, `fe`.`feIdContainer`, `fe`.`name`, `fe`.`value`, `fe`.`label`, `fe`.`type`, `fe`.`encode`, `fe`.`checkType`, `fe`.`checkPattern`, `fe`.`mode`, `fe`.`modeSql`, `fe`.`parameter`, `fe`.`dynamicUpdate` FROM `FormElement` AS fe, `Form` AS f WHERE `f`.`name` = ? AND `f`.`id` = `fe`.`formId` AND `fe`.`deleted` = 'no' AND `fe`.`class` = 'native' AND `fe`.`enabled`='yes' ORDER BY `fe`.`ord`, `fe`.`id`";
const SQL_FORM_ELEMENT_CONTAINER_TEMPLATE_GROUP = "SELECT `fe`.`id`, `fe`.`name`, `fe`.`label`, `fe`.`maxLength`, `fe`.`parameter` FROM `FormElement` AS fe, `Form` AS f WHERE `f`.`name` = ? AND `f`.`id` = `fe`.`formId` AND `fe`.`deleted` = 'no' AND `fe`.`class` = 'container' AND `fe`.`type`='templateGroup' AND `fe`.`enabled`='yes' ORDER BY `fe`.`ord`, `fe`.`id`";
const SQL_FORM_ELEMENT_TEMPLATE_GROUP_FE_ID = "SELECT * FROM `FormElement` AS fe WHERE `fe`.`id` = ? AND `fe`.`deleted` = 'no' AND `fe`.`class` = 'container' AND `fe`.`type`='templateGroup' AND `fe`.`enabled`='yes' ";
//const SQL_FORM_ELEMENT_NATIVE_TG_COUNT = "SELECT fe.*, IFNULL(feTg.maxLength,0) AS _tgCopies FROM FormElement AS fe LEFT JOIN FormElement AS feTg ON fe.feIdContainer=feTg.id AND feTg.deleted = 'no' AND feTg.class = 'container' AND feTg.type='templateGroup' AND feTg.enabled='yes' WHERE fe.formId = ? AND fe.deleted = 'no' AND fe.class = 'native' AND fe.enabled='yes'";
const SQL_FORM_ELEMENT_NATIVE_TG_COUNT = "SELECT fe.*, IFNULL(feTg.maxLength,0) AS _tgCopies FROM FormElement AS fe LEFT JOIN FormElement AS feTg ON fe.feIdContainer=feTg.id AND feTg.deleted = 'no' AND feTg.class = 'container' AND feTg.type='templateGroup' AND feTg.enabled='yes' WHERE fe.formId = ? AND fe.deleted = 'no' AND (fe.class = 'native' OR (fe.class = 'container' AND fe.type='pill')) AND fe.enabled='yes'";
const SQL_FORM_ELEMENT_NATIVE_TG_COUNT = "SELECT `fe`.*, IFNULL(`feTg`.`maxLength`,0) AS _tgCopies FROM `FormElement` AS fe LEFT JOIN `FormElement` AS feTg ON `fe`.`feIdContainer`=`feTg`.`id` AND `feTg`.`deleted` = 'no' AND `feTg`.`class` = 'container' AND `feTg`.`type`='templateGroup' AND `feTg`.`enabled`='yes' WHERE `fe`.`formId` = ? AND `fe`.`deleted` = 'no' AND (`fe`.`class` = 'native' OR (`fe`.`class` = 'container' AND `fe`.`type`='pill')) AND `fe`.`enabled`='yes'";
const NAME_TG_COPIES = '_tgCopies'; // Number of templatesGroup copies to create on the fly. Also used in SQL_FORM_ELEMENT_NATIVE_TG_COUNT.
const FE_TG_INDEX = '_tgIndex'; // Index of the current copy of a templateGroup FE.
......@@ -516,6 +516,10 @@ const SYSTEM_REDIRECT_ALL_MAIL_TO = 'redirectAllMailTo';
const SYSTEM_THROW_GENERAL_ERROR = 'throwExceptionGeneralError';
const SYSTEM_FLAG_PRODUCTION = 'flagProduction';
const SYSTEM_RENDER = 'render';
const SYSTEM_RENDER_SINGLE = 'single';
const SYSTEM_RENDER_BOTH = 'both';
const SYSTEM_RENDER_API = 'api';
const SYSTEM_SHOW_DEBUG_INFO = 'showDebugInfo';
const SYSTEM_SHOW_DEBUG_INFO_YES = 'yes';
......@@ -650,10 +654,10 @@ const SYSTEM_THUMBNAIL_DIR_PUBLIC = 'thumbnailDirPublic';
const SYSTEM_THUMBNAIL_DIR_PUBLIC_DEFAULT = 'typo3temp/qfqThumbnail';
const SYSTEM_DOCUMENTATION_QFQ = 'documentation';
const SYSTEM_DOCUMENTATION_QFQ_URL = 'https://docs.typo3.org/p/IMATHUZH/qfq/master/en-us/Manual.html';
const SYSTEM_DOCUMENTATION_QFQ_URL = 'https://qfq.io/doc';
// Not stored in config.qfq.ini, but used in STORE_SYSTEM
// Not stored in config.qfq.php, but used in STORE_SYSTEM
// Information for: Log / Debug / Exception
const SYSTEM_SQL_RAW = 'sqlRaw'; // Type: SANITIZE_ALL / String. SQL Query (before substitute). Useful for error reporting.
const SYSTEM_SQL_FINAL = 'sqlFinal'; // Type: SANITIZE_ALL / String. SQL Query (after substitute). Useful for error reporting.
......@@ -1516,7 +1520,7 @@ const TOKEN_DEBUG_BODYTEXT = TYPO3_DEBUG_SHOW_BODY_TEXT;
const TOKEN_DB_INDEX = F_DB_INDEX;
const TOKEN_CONTENT = 'content';
const TOKEN_VALID_LIST = 'sql|twig|head|althead|altsql|tail|shead|stail|rbeg|rend|renr|rsep|fbeg|fend|fsep|fskipwrap|rbgd|debug|form|r|debugShowBodyText|dbIndex|sqlLog|sqlLogMode|content';
const TOKEN_VALID_LIST = 'sql|twig|head|althead|altsql|tail|shead|stail|rbeg|rend|renr|rsep|fbeg|fend|fsep|fskipwrap|rbgd|debug|form|r|debugShowBodyText|dbIndex|sqlLog|sqlLogMode|content|render';
const TOKEN_COLUMN_CTRL = '_';
......@@ -1584,9 +1588,18 @@ const COLUMN_MAILTO = "mailto";
const COLUMN_SENDMAIL = "sendmail";
const COLUMN_VERTICAL = "vertical";
const COLUMN_NO_WRAP = "noWrap";
const COLUMN_HIDE = "hide";
const C_FULL = 'full';
const C_TITLE = 'title';
const C_NO_WRAP = 'noWrap';
const C_SPECIAL = 'special';
const C_HIDE = 'hide';
const COLUMN_WRAP_TOKEN = '+';
const COLUMN_STORE_USER = '=';
const FORM_NAME_FORM = 'form';
const FORM_NAME_FORM_ELEMENT = 'formElement';
const FORM_LOG_MODE = '_formLogMode'; // Variable to call the form in debug mode.
......
......@@ -1055,7 +1055,7 @@ class Database {
*/
public function deleteSplitFileAndRecord($xId, $tableName) {
$sql = 'SELECT pathFileName FROM ' . TABLE_NAME_SPLIT . ' WHERE tableName=? AND xId=?';
$sql = 'SELECT `pathFileName` FROM `' . TABLE_NAME_SPLIT . '` WHERE `tableName`=? AND `xId`=?';
$data = $this->sql($sql, ROW_REGULAR, [$tableName, $xId]);
foreach ($data AS $row) {
......@@ -1064,7 +1064,7 @@ class Database {
}
}
$this->sql('DELETE FROM ' . TABLE_NAME_SPLIT . ' WHERE tableName=? AND xId=?', ROW_REGULAR, [$tableName, $xId]);
$this->sql('DELETE FROM `' . TABLE_NAME_SPLIT . '` WHERE `tableName`=? AND `xId`=?', ROW_REGULAR, [$tableName, $xId]);
}
}
\ No newline at end of file
......@@ -10,6 +10,7 @@ namespace IMATHUZH\Qfq\Core\Database;
use IMATHUZH\Qfq\Core\Helper\Logger;
use IMATHUZH\Qfq\Core\Store\Store;
use IMATHUZH\Qfq\Core\Typo3\T3Handler;
/*
......@@ -129,14 +130,17 @@ class DatabaseUpdate {
*/
public function checkNupdate($dbUpdate) {
if ($dbUpdate === SYSTEM_DB_UPDATE_NEVER) {
return;
}
$new = $this->getExtensionVersion();
$versionInfo = $this->getDatabaseVersion();
$old = $versionInfo[QFQ_VERSION_KEY] ?? false;
$this->checkT3QfqConfig($old, $new);
if ($dbUpdate === SYSTEM_DB_UPDATE_NEVER) {
return;
}
if ($dbUpdate === SYSTEM_DB_UPDATE_ALWAYS || ($dbUpdate === SYSTEM_DB_UPDATE_AUTO && $new != $old)) {
$newFunctionHash = $this->updateSqlFunctions($versionInfo[QFQ_VERSION_KEY_FUNCTION_HASH] ?? '');
......@@ -167,6 +171,23 @@ class DatabaseUpdate {
}
/**
* Check Typo3 config if values needs to be updated.
* This is typically necessary if default config values change, to guarantee existing installations behave in legacy mode.
*
* @param $old
* @param $new
*/
private function checkT3QfqConfig($old, $new) {
if ($new == $old) {
return;
}
if (version_compare($old, '20.2.0') == -1) {
T3Handler::updateT3QfqConfig(SYSTEM_RENDER, SYSTEM_RENDER_BOTH); //Legacy behaviour.
}
}
/**
* Check if there are special columns without prepended underscore in the QFQ application. If yes, then throw an error.
* A link is provided to automatically prepend all found special columns. And another link to skip the auto-replacement.
......@@ -201,14 +222,14 @@ class DatabaseUpdate {
if (defined('PHPUNIT_QFQ')) {
$res = array();
} else {
$res = $this->db->sql("SELECT uid, header, bodytext FROM " . $dbT3 . ".tt_content WHERE CType='qfq_qfq' AND deleted=0;");
$res = $this->db->sql("SELECT `uid`, `header`, `bodytext` FROM `" . $dbT3 . "`.`tt_content` WHERE `CType`='qfq_qfq' AND `deleted`=0;");
}
foreach ($res as $i => $tt_content) {
$replaced_placeholder = preg_replace($patterns, '${1}' . $placeholder . '${2}', $tt_content['bodytext']);
if (strpos($replaced_placeholder, $placeholder) !== false) {
if ($actionSpecialColumn === ACTION_SPECIAL_COLUMN_DO_REPLACE) {
$replace = str_replace($placeholder, '_', $replaced_placeholder);
$query = "UPDATE " . $dbT3 . ".tt_content SET bodytext='" . addslashes($replace) . "' WHERE uid='" . $tt_content['uid'] . "'";
$query = "UPDATE `" . $dbT3 . "`.`tt_content` SET `bodytext`='" . addslashes($replace) . "' WHERE `uid`='" . $tt_content['uid'] . "'";
$this->db->sql($query);
}
$message_fe .= '<hr><b>' . $tt_content['header'] . ' [uid:' . $tt_content['uid'] . ']</b><br><br>';
......@@ -226,7 +247,7 @@ class DatabaseUpdate {
if (defined('PHPUNIT_QFQ')) {
$res = array();
} else {
$res = $this->db->sql("SELECT fe.id, fe.name, fe.value, fe.note FROM FormElement as fe WHERE fe.type='note' AND fe.value LIKE '#!report%' OR fe.note LIKE '%#!report%';");
$res = $this->db->sql("SELECT `fe`.`id`, `fe`.`name`, `fe`.`value`, `fe`.`note` FROM `FormElement` AS fe WHERE `fe`.`type`='note' AND `fe`.`value` LIKE '#!report%' OR `fe`.`note` LIKE '%#!report%';");
}
foreach ($res as $i => $tt_content) {
......@@ -235,7 +256,7 @@ class DatabaseUpdate {
if (strpos($replaced_placeholder, $placeholder) !== false) {
if ($actionSpecialColumn === ACTION_SPECIAL_COLUMN_DO_REPLACE) {
$replace = str_replace($placeholder, '_', $replaced_placeholder);
$query = "UPDATE FormElement SET " . $columnName . "='" . addslashes($replace) . "' WHERE id='" . $tt_content['id'] . "'";
$query = "UPDATE `FormElement` SET `" . $columnName . "`='" . addslashes($replace) . "' WHERE `id`='" . $tt_content['id'] . "'";
$this->db->sql($query);
}
$message_ttc .= '<hr><b>' . $tt_content['name'] . ' [id:' . $tt_content['id'] . '] (FormElement.' . $columnName . ')</b><br><br>';
......
......@@ -23,7 +23,7 @@ $UPDATE_ARRAY = array(
],
'0.15.0' => [
"UPDATE FormElement SET parameter = REPLACE(parameter, 'typeAheadLdapKeyPrintf', 'typeAheadLdapIdPrintf')",
"UPDATE `FormElement` SET `parameter` = REPLACE(parameter, 'typeAheadLdapKeyPrintf', 'typeAheadLdapIdPrintf')",
"ALTER TABLE `FormElement` CHANGE `placeholder` `placeholder` VARCHAR( 2048 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' ",
],
......@@ -44,7 +44,7 @@ $UPDATE_ARRAY = array(
'0.18.0' => [
"ALTER TABLE `Form` CHANGE `forwardMode` `forwardMode` ENUM( 'client', 'no', 'page', 'url', 'url-skip-history' ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'client'",
"UPDATE Form SET forwardMode='url' WHERE forwardMode='page'",
"UPDATE `Form` SET `forwardMode`='url' WHERE `forwardMode`='page'",
"ALTER TABLE `Form` CHANGE `forwardMode` `forwardMode` ENUM( 'client', 'no', 'url', 'url-skip-history' ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'client'",
],
......@@ -57,7 +57,7 @@ $UPDATE_ARRAY = array(
"ALTER TABLE `Form` ADD `dirtyMode` ENUM( 'exclusive', 'advisory', 'none' ) NOT NULL DEFAULT 'exclusive' AFTER `requiredParameter`",
"ALTER TABLE `Form` ADD `recordLockTimeoutSeconds` INT NOT NULL DEFAULT '900' AFTER `parameter`",
"CREATE TABLE IF NOT EXISTS `Period` (`id` INT(11) NOT NULL AUTO_INCREMENT, `start` DATETIME NOT NULL, `name` VARCHAR(255) NOT NULL, `modified` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `created` DATETIME NOT NULL, PRIMARY KEY (`id`), KEY `start` (`start`)) ENGINE = InnoDB DEFAULT CHARSET = utf8 AUTO_INCREMENT = 0;",
"INSERT INTO Period (start, name, created) VALUES (NOW(), 'dummy', NOW());"
"INSERT INTO `Period` (`start`, `name`, `created`) VALUES (NOW(), 'dummy', NOW());"
],
'0.19.2' => [
......@@ -79,7 +79,7 @@ $UPDATE_ARRAY = array(
'0.21.0' => [
"ALTER TABLE `Form` CHANGE `requiredParameter` `requiredParameterNew` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT ''",
"ALTER TABLE `Form` ADD `requiredParameterEdit` VARCHAR( 255 ) NOT NULL AFTER `requiredParameterNew`",
"UPDATE Form SET requiredParameterEdit=requiredParameterNew",
"UPDATE `Form` SET `requiredParameterEdit`=requiredParameterNew",
],
'0.24.0' => [
......@@ -103,7 +103,7 @@ $UPDATE_ARRAY = array(
],
'0.25.11' => [
"UPDATE FormElement SET checkType = 'alnumx', checkPattern = '', parameter = CONCAT(parameter, '\nmin = ', SUBSTRING_INDEX(checkPattern, '|', 1), '\nmax = ', SUBSTRING_INDEX(checkPattern, '|', -1)) WHERE checkType LIKE 'min|max%' AND checkPattern <> ''",
"UPDATE `FormElement` SET `checkType` = 'alnumx', checkPattern = '', parameter = CONCAT(parameter, '\nmin = ', SUBSTRING_INDEX(checkPattern, '|', 1), '\nmax = ', SUBSTRING_INDEX(checkPattern, '|', -1)) WHERE checkType LIKE 'min|max%' AND checkPattern <> ''",
"ALTER TABLE `FormElement` CHANGE `checkType` `checkType` ENUM('alnumx','digit','numerical','email','pattern','allbut','all') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'alnumx';",
],
......@@ -120,7 +120,7 @@ $UPDATE_ARRAY = array(
'18.6.0' => [
"ALTER TABLE `Form` CHANGE `forwardMode` `forwardMode` ENUM('auto', 'client','no','url','url-skip-history','url-sip') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'client'",
"UPDATE `Form` SET forwardMode='auto' WHERE forwardMode='client'",
"UPDATE `Form` SET `forwardMode`='auto' WHERE `forwardMode`='client'",
"ALTER TABLE `Form` CHANGE `forwardMode` `forwardMode` ENUM('auto', 'close', 'no','url','url-skip-history','url-sip') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'auto';",
],
......@@ -147,7 +147,7 @@ $UPDATE_ARRAY = array(
'19.3.2' => [
"ALTER TABLE `Form` CHANGE `forwardMode` `forwardMode` ENUM('auto','close','no','url','url-skip-history','url-sip','url-sip-skip-history' ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'auto';",
"UPDATE `Form` SET forwardMode='url-sip-skip-history' WHERE forwardMode='url-sip'",
"UPDATE `Form` SET `forwardMode`='url-sip-skip-history' WHERE `forwardMode`='url-sip'",
],
'19.7.2' => [
......
......@@ -79,12 +79,12 @@ class Delete {
}
// Read record first.
$row = $this->db->sql("SELECT * FROM $tableName WHERE $primaryKey=?", ROW_EXPECT_0_1, [$recordId]);
$row = $this->db->sql("SELECT * FROM `$tableName` WHERE `$primaryKey`=?", ROW_EXPECT_0_1, [$recordId]);
if (count($row) > 0) {
$this->deleteReferencedFiles($row, $tableName, $primaryKey);
$this->db->sql("DELETE FROM $tableName WHERE $primaryKey =? LIMIT 1", ROW_REGULAR, [$recordId]);
$this->db->sql("DELETE FROM `$tableName` WHERE `$primaryKey` =? LIMIT 1", ROW_REGULAR, [$recordId]);
} else {
throw new \UserFormException(
json_encode([ERROR_MESSAGE_TO_USER => 'Record not found in table', ERROR_MESSAGE_TO_DEVELOPER => "Record $recordId not found in table '$tableName'."]),
......@@ -124,7 +124,7 @@ class Delete {
// check if there are other records referencing the same file: do not delete the file now.
// This check won't find duplicates, if they are spread over different columns or tables.
$samePathFileName = $this->db->sql("SELECT COUNT($primaryKey) AS cnt FROM $tableName WHERE $key LIKE ?", ROW_EXPECT_1, [$file]);
$samePathFileName = $this->db->sql("SELECT COUNT($primaryKey) AS cnt FROM `$tableName` WHERE `$key` LIKE ?", ROW_EXPECT_1, [$file]);
if ($samePathFileName['cnt'] === 1) {
HelperFile::unlink($file);
$this->db->deleteSplitFileAndRecord($row[$primaryKey], $tableName);
......
......@@ -16,7 +16,7 @@ use IMATHUZH\Qfq\Core\Report\Link;
use IMATHUZH\Qfq\Core\Report\Tablesorter;
use IMATHUZH\Qfq\Core\Store\Sip;
use IMATHUZH\Qfq\Core\Store\Store;
use IMATHUZH\Qfq\Core\Typo3\Password;
use IMATHUZH\Qfq\Core\Typo3\T3Handler;
const EVALUATE_DB_INDEX_DEFAULT = 0;
......@@ -423,7 +423,7 @@ class Evaluate {
case TOKEN_ESCAPE_NONE: // do nothing
break;
case TOKEN_ESCAPE_PASSWORD_T3FE:
$value = Password::getHash($value);
$value = T3Handler::getHash($value);
break;
case TOKEN_ESCAPE_STOP_REPLACE:
$value = Support::encryptDoubleCurlyBraces($value);
......
......@@ -279,7 +279,7 @@ class AbstractException extends \Exception {
$linkFormElement = '';
try {
$db = new Database();
$sql = "SELECT id FROM Form WHERE name='" . $storeSystem[SYSTEM_FORM] . "'";
$sql = "SELECT `id` FROM `Form` WHERE `name`='" . $storeSystem[SYSTEM_FORM] . "'";
$r = $db->sql($sql, ROW_EXPECT_0_1);
if (!is_numeric($r[F_ID])) {
......
......@@ -125,7 +125,7 @@ class Dirty {
$this->dbIndexData = empty($sipVars[PARAM_DB_INDEX_DATA]) ? $this->store->getVar(SYSTEM_DB_INDEX_DATA, STORE_SYSTEM) : $sipVars[PARAM_DB_INDEX_DATA];
$this->doDbArray($this->dbIndexData, $this->dbIndexQfq);
$tableVars = $this->dbArray[$this->dbIndexQfq]->sql("SELECT tableName, primaryKey, dirtyMode, recordLockTimeoutSeconds FROM Form WHERE name=?", ROW_EXPECT_1, [$sipVars[SIP_FORM]], "Form not found: '" . $sipVars[SIP_FORM] . "'");
$tableVars = $this->dbArray[$this->dbIndexQfq]->sql("SELECT `tableName`, `primaryKey`, `dirtyMode`, `recordLockTimeoutSeconds` FROM `Form` WHERE `name`=?", ROW_EXPECT_1, [$sipVars[SIP_FORM]], "Form not found: '" . $sipVars[SIP_FORM] . "'");
if (empty($tableVars[F_PRIMARY_KEY])) {
$tableVars[F_PRIMARY_KEY] = F_PRIMARY_KEY_DEFAULT;
}
......@@ -208,7 +208,7 @@ class Dirty {
*/
private function getRecordDirty($tableName, $recordId) {
$recordDirty = $this->dbArray[$this->dbIndexQfq]->sql("SELECT * FROM Dirty AS d WHERE d.tableName LIKE ? AND recordId=? ",
$recordDirty = $this->dbArray[$this->dbIndexQfq]->sql("SELECT * FROM `Dirty` AS d WHERE `d`.`tableName` LIKE ? AND `recordId`=? ",
ROW_EXPECT_0_1, [$tableName, $recordId]);
// Check if the record is timed out - owner doesn't matter.
......@@ -286,12 +286,12 @@ class Dirty {
$primaryKey = $tableVars[F_PRIMARY_KEY];
$formDirtyMode = $tableVars[F_DIRTY_MODE];
$record = $this->dbArray[$this->dbIndexData]->sql("SELECT * FROM $tableName WHERE $primaryKey=?", ROW_EXPECT_1, [$recordId], "Record to lock not found.");
$record = $this->dbArray[$this->dbIndexData]->sql("SELECT * FROM `$tableName` WHERE `$primaryKey`=?", ROW_EXPECT_1, [$recordId], "Record to lock not found.");
# Dirty workaround: setting the 'expired timestamp' minus 1 second guarantees that the client ask for relock always if the timeout is expired.
$expire = date('Y-m-d H:i:s', strtotime("+" . $tableVars[F_RECORD_LOCK_TIMEOUT_SECONDS] - 1 . " seconds"));
// Write 'dirty' record
$this->dbArray[$this->dbIndexQfq]->sql("INSERT INTO Dirty (`sip`, `tableName`, `recordId`, `expire`, `recordHashMd5`, `tabUniqId`, `feUser`, `qfqUserSessionCookie`, `dirtyMode`, `remoteAddress`, `created`) " .
$this->dbArray[$this->dbIndexQfq]->sql("INSERT INTO `Dirty` (`sip`, `tableName`, `recordId`, `expire`, `recordHashMd5`, `tabUniqId`, `feUser`, `qfqUserSessionCookie`, `dirtyMode`, `remoteAddress`, `created`) " .
"VALUES ( ?,?,?,?,?,?,?,?,?,?,? )", ROW_REGULAR,
[$s, $tableName, $recordId, $expire, $recordHashMd5, $tabUniqId, $feUser, $this->client[CLIENT_COOKIE_QFQ], $formDirtyMode,
$this->client[CLIENT_REMOTE_ADDRESS], date('YmdHis')]);
......@@ -320,7 +320,7 @@ class Dirty {
return false; // If there is no recordHashMd5, the check is not possible. Always return 'not modified' (=ok)
}
$record = $this->dbArray[$this->dbIndexData]->sql("SELECT * FROM $tableName WHERE $primaryKey=?", ROW_EXPECT_1, [$recordId], "Record to lock not found.");
$record = $this->dbArray[$this->dbIndexData]->sql("SELECT * FROM `$tableName` WHERE `$primaryKey`=?", ROW_EXPECT_1, [$recordId], "Record to lock not found.");
$rcMd5 = OnArray::getMd5($record);
......@@ -477,7 +477,7 @@ class Dirty {
*/
private function deleteDirtyRecord($recordDirtyId) {
$cnt = $this->dbArray[$this->dbIndexQfq]->sql('DELETE FROM Dirty WHERE id=? LIMIT 1', ROW_REGULAR, [$recordDirtyId]);
$cnt = $this->dbArray[$this->dbIndexQfq]->sql('DELETE FROM `Dirty` WHERE `id`=? LIMIT 1', ROW_REGULAR, [$recordDirtyId]);
if ($cnt != 1) {
throw new \CodeException("Failed to delete dirty record id=" . $recordDirtyId, ERROR_DIRTY_DELETE_RECORD);
}
......
......@@ -204,7 +204,7 @@ class DragAndDrop {
return $data;
}
$this->db->sql("UPDATE $tableName SET $orderColumn=? WHERE id=?", ROW_REGULAR, [$ordNew, $id]);
$this->db->sql("UPDATE `$tableName` SET `$orderColumn`=? WHERE `id`=?", ROW_REGULAR, [$ordNew, $id]);
// Converting to string is necessary: JSON detects int else.
$data[API_ELEMENT_UPDATE][DND_ORD_HTML_ID_PREFIX . $id][API_ELEMENT_CONTENT] = (string)$ordNew;
......
......@@ -430,7 +430,7 @@ class FormAction {
// Check if there is a column with the same name as the 'action'-FormElement.
if ($flagFeAction && false !== $this->store->getVar($fe[FE_NAME], STORE_RECORD)) {
// After an insert or update, propagate the (new) slave id to the master record.
$this->db->sql("UPDATE " . $this->primaryTableName . " SET " . $fe[FE_NAME] . " = $slaveId WHERE id = ? LIMIT 1", ROW_REGULAR, [$recordId]);
$this->db->sql("UPDATE `" . $this->primaryTableName . "` SET `" . $fe[FE_NAME] . "` = $slaveId WHERE `id` = ? LIMIT 1", ROW_REGULAR, [$recordId]);
}
}
......@@ -552,7 +552,7 @@ class FormAction {
// will be used in sub paste's
// $clipboard["_src_id"] = $newColumns[COLUMN_ID];
$rowSrc = $this->db->sql("SELECT * FROM $recordSourceTable WHERE id=?", ROW_EXPECT_1, [$newColumns[COLUMN_ID]]);
$rowSrc = $this->db->sql("SELECT * FROM `$recordSourceTable` WHERE `id`=?", ROW_EXPECT_1, [$newColumns[COLUMN_ID]]);
$this->checkNCopyFiles($rowSrc, $newColumns);
......@@ -603,11 +603,11 @@ class FormAction {
foreach ($translateMap as $oldId => $newId) {
$row = $this->db->sql("SELECT $translateIdColumn FROM $tableName WHERE id=$newId", ROW_EXPECT_1);
$row = $this->db->sql("SELECT `$translateIdColumn` FROM `$tableName` WHERE `id`=$newId", ROW_EXPECT_1);
if (!empty($row[$translateIdColumn])) {
$newNewId = $translateMap[$row[$translateIdColumn]];
$this->db->sql("UPDATE $tableName SET $translateIdColumn=$newNewId WHERE id=$newId LIMIT 1");
$this->db->sql("UPDATE `$tableName` SET `$translateIdColumn`=$newNewId WHERE `id`=$newId LIMIT 1");
}
}
......@@ -691,10 +691,10 @@ class FormAction {
return (0);
}
$keyString = implode(',', $keys);
$keyString = '`' . implode('`,`', $keys) . '`';
$valueString = implode(',', $placeholder);
$sql = "INSERT INTO $destTable ($keyString) VALUES ($valueString)";
$sql = "INSERT INTO `$destTable` ($keyString) VALUES ($valueString)";
return $this->db->sql($sql, ROW_REGULAR, $values);
......
......@@ -1572,4 +1572,12 @@ class Support {
return $formModeGlobal;
}
/**
* Set QFQ Error Handler.
* Should not be active if T3 code runs.
*/
public static function setQfqErrorHandler() {
set_error_handler("\\IMATHUZH\\Qfq\\Core\\Exception\\ErrorHandler::exception_error_handler");
}
}
\ No newline at end of file
......@@ -129,7 +129,8 @@ class QuickFormQuery {
// Refresh the session even if no new data saved.
Session::set(SESSION_LAST_ACTIVITY, time());
set_error_handler("\\IMATHUZH\\Qfq\\Core\\Exception\\ErrorHandler::exception_error_handler");
Support::setQfqErrorHandler();
// PHPExcel
set_include_path(get_include_path() . PATH_SEPARATOR . '../../Resources/Private/Classes/');
......@@ -242,13 +243,20 @@ class QuickFormQuery {
public function process() {
$html = '';
$render = $this->store->getVar(SYSTEM_RENDER, STORE_TYPO3 . STORE_SYSTEM);
if ($render == SYSTEM_RENDER_API && isset($GLOBALS['TYPO3_CONF_VARS'])) {
return '';
}
if ($this->store->getVar(TYPO3_DEBUG_SHOW_BODY_TEXT, STORE_TYPO3) === 'yes') {
$htmlId = HelperFormElement::buildFormElementId($this->formSpec[F_ID], 0, 0, 0);
$html .= Support::doTooltip($htmlId . HTML_ID_EXTENSION_TOOLTIP, $this->t3data['bodytext']);
}