diff --git a/extension/Documentation/Manual.rst b/extension/Documentation/Manual.rst index 7b0f91b13000f9bdb7ef061a8c29aabe8ac6034a..b07878c273b28760601475a957c48cc8df75b3ab 100644 --- a/extension/Documentation/Manual.rst +++ b/extension/Documentation/Manual.rst @@ -326,6 +326,10 @@ Extension Manager: QFQ Configuration | sqlLog | fileadmin/protected/log/sql.log | Filename to log SQL commands: relative to <site path> or absolute. If the | | | | directory does not exist, create it. | +-------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+ +| formSubmitLogMode | all | *all*: every form submission will be logged. | +| | | *none*: no logging. | +| | | See `Form Submit Log page`_ for example qfq code to display the log. | ++-------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+ | mailLog | fileadmin/protected/log/mail.log | Filename to log `sendEmail` commands: relative to <site path> or absolute. | | | | If the directory does not exist, create it. | +-------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+ @@ -733,25 +737,29 @@ version, the system tables will be automatically installed or updated. System tables ^^^^^^^^^^^^^ -+-------------+------------+------------+ -| Name | Use | Database | -+=============+============+============+ -| Clipboard | Temporary | QFQ | -+-------------+------------+------------+ -| Cron | Persistent | QFQ | -+-------------+------------+------------+ -| Dirty | Temporary | QFQ | Data | -+-------------+------------+------------+ -| Form | Persistent | QFQ | -+-------------+------------+------------+ -| FormElement | Persistent | QFQ | -+-------------+------------+------------+ -| MailLog | Persistent | QFQ | Data | -+-------------+------------+------------+ -| Period | Persistent | Data | -+-------------+------------+------------+ -| Split | Persistent | Data | -+-------------+------------+------------+ ++---------------+------------+------------+ +| Name | Use | Database | ++===============+============+============+ +| Clipboard | Temporary | QFQ | ++---------------+------------+------------+ +| Cron | Persistent | QFQ | ++---------------+------------+------------+ +| Dirty | Temporary | QFQ | Data | ++---------------+------------+------------+ +| Form | Persistent | QFQ | ++---------------+------------+------------+ +| FormElement | Persistent | QFQ | ++---------------+------------+------------+ +| FormSubmitLog | Persistent | QFQ | Data | ++---------------+------------+------------+ +| MailLog | Persistent | QFQ | Data | ++---------------+------------+------------+ +| Period | Persistent | Data | ++---------------+------------+------------+ +| Split | Persistent | Data | ++---------------+------------+------------+ + +See `Mail Log page`_ and `Form Submit Log page`_ for some Frontend views for these tables. * Check Bug #5459 - support of system tables in different DBs not supported. @@ -918,6 +926,109 @@ configuration_ * Clear 'CC' and 'Bcc' * Write a note and the original configured receiver at the top of the email body. +_`Mail Log page` + +Mail Log page +------------- + +For debugging purposes you may like to add a Mail Log page in the frontend. +The following QFQ code could be used for that purpose (put it in a QFQ PageContent element): :: + + # Page parameters + 1.sql = SELECT @grId := '{{grId:C0:digit}}' AS _grId + 2.sql = SELECT @summary := IF('{{summary:CE:alnumx}}' = 'true', 'true', 'false') AS _s + + # Filters + 10 { + sql = SELECT gr.id, IF(gr.id = @grId, "' selected>", "'>"), gr.value, ' (Id: ', gr.id, ')' + FROM gGroup AS gr + INNER JOIN MailLog AS ml ON ml.grId = gr.id + GROUP BY gr.id + head = <form onchange='this.submit();' class='form-inline'><input type='hidden' name='id' value='{{pageId:T0}}'>Filter By Group: <select name='grId' class='form-control'><option value=''></option> + rbeg = <option value=' + rend = </option> + tail = </select> + } + 20 { + sql = SELECT IF(@summary = 'true', ' checked', '') + head = <div class='checkbox'><label><input type='checkbox' name='summary' value='true' + tail = >Summary</label></div></form> + } + + # Mail Log + 50 { + sql = SELECT id, '</td><td>', grId, '</td><td>', xId, '</td><td>', + REPLACE(receiver, ',', '<br>'), '</td><td>', REPLACE(sender, ',', '<br>'), '</td><td>', + DATE_FORMAT(modified, '%d.%m.%Y<br>%H:%i:%s'), '</td><td style="word-break:break-word;">', + CONCAT('<b>', subject, '</b><br>', IF(@summary = 'true', CONCAT(SUBSTR(body, 1, LEAST(IF(INSTR(body, '\n') = 0, 50, INSTR(body, '\n')), IF(INSTR(body, '<br>') = 0, 50, INSTR(body, '<br>')))-1), ' ...'), CONCAT('<br>', REPLACE(body, '\n', '<br>'))) ) + FROM MailLog WHERE (grId = @grId OR @grId = 0) + ORDER BY modified DESC + LIMIT 100 + head = <table class="table table-condensed table-hover"><tr><th>Id</th><th>grId</th><th>xId</th><th>To</th><th>From</th><th>Date</th><th>E-Mail</th></tr> + tail = </table> + rbeg = <tr><td> + rend = </td></tr> + } + +_`Form Submit Log page` + +Form Submit Log page +-------------------- + +For debugging purposes you may like to add a Form Submit Log page in the frontend. +The following QFQ code could be used for that purpose (put it in a QFQ PageContent element): :: + + # Filters + 20.shead = <form onchange='this.submit()' class='form-inline'><input type='hidden' name='id' value='{{pageId:T0}}'> + 20 { + sql = SELECT id, IF(id = '{{formId:SC0}}', "' selected>", "'>"), name + FROM Form ORDER BY name + head = <label for='formId'>Form:</label> <select name='formId' id='formId' class='form-control'><option value=0></option> + tail = </select> + rbeg = <option value=' + rend = </option> + } + 30 { + sql = SELECT feUser, IF(feUser = '{{feUser:SCE:alnumx}}', "' selected>", "'>"), feUser + FROM FormSubmitLog GROUP BY feUser ORDER BY feUser + head = <label for='feUser'>FE User:</label> <select name='feUser' id='feUser' class='form-control'><option value=''></option> + tail = </select> + rbeg = <option value=' + rend = </option> + } + 30.stail = </form> + + # Show Log + 50 { + sql = SELECT l.id, + CONCAT('<b>Form</b>: ', f.name, + '<br><b>Record Id</b>: ', l.recordId, + '<br><b>Fe User</b>: ', l.feUser, + '<br><b>Date</b>: ', l.created, + '<br><b>Page Id</b>: ', l.pageId, + '<br><b>Session Id</b>: ', l.sessionId, + '<br><b>IP Address</b>: ', l.clientIp, + '<br><b>User Agent</b>: ', l.userAgent, + '<br><b>SIP Data</b>: <div style="margin-left:20px;">', "<script>var data = JSON.parse('", l.sipData, + "'); for (var key in data) { + document.write('<b>' + key + '</b>: ' + data[key] + '<br>'); }</script>", '</div>'), + CONCAT("<script>var data = JSON.parse('", l.formData, + "'); for (var key in data) { + document.write('<b>' + key + '</b>: ' + data[key] + '<br>'); }</script>") + FROM FormSubmitLog AS l + LEFT JOIN Form AS f ON f.id = l.formId + WHERE (l.formId = '{{formId:SC0}}' OR '{{formId:SC0}}' = 0) + AND (l.feUser = '{{feUser:SCE:alnumx}}' OR '{{feUser:SCE:alnumx}}' = '') + ORDER BY l.created DESC LIMIT 100 + head = <table class="table table-hover"> + <tr><th>Id</th><th style="min-width:250px;">Environment</th><th>Submitted Data</th> + tail = </table> + rbeg = <tr> + renr = </tr> + fbeg = <td> + fend = </td> + } + .. _variables: Variable @@ -2200,6 +2311,8 @@ Parameter +-----------------------------+--------+----------------------------------------------------------------------------------------------------------+ | showIdInFormTitle | string | Overwrite default from configuration_ | +-----------------------------+--------+----------------------------------------------------------------------------------------------------------+ +| formSubmitLogMode | string | Overwrite default from configuration_ | ++-----------------------------+--------+----------------------------------------------------------------------------------------------------------+ * Example: diff --git a/extension/ext_conf_template.txt b/extension/ext_conf_template.txt index 333c0c0350255db00f492536bdd8fbc0a7196c9e..19988dc4b7984e6a82b7bc3a9929a3ae0d3a9fa2 100644 --- a/extension/ext_conf_template.txt +++ b/extension/ext_conf_template.txt @@ -60,6 +60,9 @@ sqlLogMode = modify # cat=debug/sql; type=string; label=SQL log file:Default is 'fileadmin/protected/log/sql.log'. A logfile of fired SQL statements. PathFile is absolute or relative to '<site path>'. sqlLog = fileadmin/protected/log/sql.log +# cat=debug/form; type=string; label=Form submit log mode:Default is 'all'. Form submit requests will be logged in the table FormSubmitLog. Possible modes are 'all' - every form save will be logged. 'none' - no logging. This setting can also be changed per form. +formSubmitLogMode = all + # cat=debug/mail; type=string; label=Mail log file:Default is 'fileadmin/protected/log/mail.log'. A logfile of sent mail. PathFile is absolute or relative to '<site path>'. mailLog = fileadmin/protected/log/mail.log diff --git a/extension/qfq/qfq/Constants.php b/extension/qfq/qfq/Constants.php index 53313c886e388a011244497e1f0c64a0eabdf848..648d31f9b8f329a1c601c0193dd2f3fffe92b21d 100644 --- a/extension/qfq/qfq/Constants.php +++ b/extension/qfq/qfq/Constants.php @@ -422,6 +422,10 @@ const SYSTEM_SQL_LOG = 'sqlLog'; // Logging to file const SYSTEM_SQL_LOG_FILE = 'fileadmin/protected/log/sql.log'; const SYSTEM_SQL_LOG_MODE = 'sqlLogMode'; // Mode, which statements to log. +const SYSTEM_FORM_SUBMIT_LOG_MODE = 'formSubmitLogMode'; +const FORM_SUBMIT_LOG_MODE_ALL = 'all'; +const FORM_SUBMIT_LOG_MODE_NONE = 'none'; + const SYSTEM_DATE_FORMAT = 'dateFormat'; const SYSTEM_REDIRECT_ALL_MAIL_TO = 'redirectAllMailTo'; const SYSTEM_MAIL_LOG = 'mailLog'; @@ -842,6 +846,7 @@ const F_PARAMETER = 'parameter'; // valid for F_ and FE_ const F_DB_INDEX = 'dbIndex'; const DB_INDEX_DEFAULT = "1"; const PARAM_DB_INDEX_DATA = '__dbIndexData'; // Submitted via SIP to make record locking DB aware. +const F_FORM_SUBMIT_LOG_MODE = 'formSubmitLogMode'; const F_LDAP_SERVER = 'ldapServer'; const F_LDAP_BASE_DN = 'ldapBaseDn'; diff --git a/extension/qfq/qfq/QuickFormQuery.php b/extension/qfq/qfq/QuickFormQuery.php index a4eb59955ef2ae7298f96364b618a33edd70e578..de324ca73e36c404a00d642fc6f929f568ef6aab 100644 --- a/extension/qfq/qfq/QuickFormQuery.php +++ b/extension/qfq/qfq/QuickFormQuery.php @@ -457,6 +457,8 @@ class QuickFormQuery { break; case FORM_SAVE: + $this->logFormSubmitRequest(); + $recordId = $this->store->getVar(SIP_RECORD_ID, STORE_SIP); // Action: Before @@ -544,6 +546,36 @@ class QuickFormQuery { return $data; } + /** + * @throws CodeException + * @throws DbException + * @throws UserFormException + */ + private function logFormSubmitRequest() { + $formSubmitLogMode = $this->formSpec[F_FORM_SUBMIT_LOG_MODE] ?? + $this->store->getVar(SYSTEM_FORM_SUBMIT_LOG_MODE, STORE_SYSTEM, SANITIZE_ALLOW_ALNUMX); + if ($formSubmitLogMode === FORM_SUBMIT_LOG_MODE_NONE) { + return; + } + + $formData = $_POST; + unset($formData[CLIENT_SIP]); + $formData = json_encode($formData, JSON_UNESCAPED_UNICODE); + $clientIp = $_SERVER[CLIENT_REMOTE_ADDRESS]; + $userAgent = $_SERVER[CLIENT_HTTP_USER_AGENT]; + $sipData = json_encode($this->store->getStore(STORE_SIP), JSON_UNESCAPED_UNICODE); + $formId = $this->formSpec[F_ID]; + $recordId = $this->store->getVar(SIP_RECORD_ID, STORE_SIP); + $feUser = $this->store->getVar(TYPO3_FE_USER, STORE_TYPO3, SANITIZE_ALLOW_ALNUMX); + $pageId = $this->store->getVar(TYPO3_PAGE_ID, STORE_TYPO3, SANITIZE_ALLOW_ALNUMX); + $sessionId = session_id(); + + $sql = "INSERT INTO FormSubmitLog (formData, sipData, clientIp, feUser, userAgent, formId, recordId, pageId, sessionId, created)" . + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())"; + $params = [$formData, $sipData, $clientIp, $feUser, $userAgent, $formId, $recordId, $pageId, $sessionId]; + $this->dbArray[$this->dbIndexQfq]->sql($sql, ROW_REGULAR, $params); + } + /** * Check if forwardMode='url...'. diff --git a/extension/qfq/qfq/database/DatabaseUpdateData.php b/extension/qfq/qfq/database/DatabaseUpdateData.php index e86958364ac9d0555d1008af0b5cf3dd2c343459..3fafacf8910fd99891f8760ed97ad37cf97c5c6f 100644 --- a/extension/qfq/qfq/database/DatabaseUpdateData.php +++ b/extension/qfq/qfq/database/DatabaseUpdateData.php @@ -120,7 +120,6 @@ $UPDATE_ARRAY = array( "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';", ], - ); diff --git a/extension/qfq/sql/formEditor.sql b/extension/qfq/sql/formEditor.sql index d59fa78c1eb82394ceda33e42017c5959f636f41..fbc42cc9c18a4c4db652585cdb7f3bc36589186e 100644 --- a/extension/qfq/sql/formEditor.sql +++ b/extension/qfq/sql/formEditor.sql @@ -353,6 +353,27 @@ CREATE TABLE IF NOT EXISTS `MailLog` ( DEFAULT CHARSET = utf8 AUTO_INCREMENT = 0; +CREATE TABLE IF NOT EXISTS `FormSubmitLog` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `formData` TEXT NOT NULL, + `sipData` TEXT NOT NULL, + `clientIp` VARCHAR(64) NOT NULL, + `feUser` VARCHAR(64) NOT NULL, + `userAgent` TEXT NOT NULL, + `formId` INT(11) NOT NULL, + `recordId` INT(11) NOT NULL, + `pageId` INT NOT NULL, + `sessionId` VARCHAR(32) NOT NULL, + `created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + + PRIMARY KEY (`id`), + INDEX (`feUser`), + INDEX (`formId`) +) + ENGINE = InnoDB + DEFAULT CHARSET = utf8 + AUTO_INCREMENT = 0; + CREATE TABLE IF NOT EXISTS `Clipboard` ( `id` INT(11) NOT NULL AUTO_INCREMENT,