From 34229364f136ac1e91c6077b308c05d8bed73b36 Mon Sep 17 00:00:00 2001
From: Carsten  Rose <carsten.rose@math.uzh.ch>
Date: Wed, 19 Oct 2016 15:13:56 +0200
Subject: [PATCH] Documentation/UsersManual/indext.rst: update
 Formelemnet.type=sendmail and QFQ column '_sendmail' QuickFormQuery.php,
 FormAction.php: implemented FormElement 'sendmail' Report.php: Update column
 'sendmail' Sendmail.php: Update class to send mailand to log them in table
 mailLog. AbstractBuildForm.php: refacxtured columnname 'value' to be used via
 constatn FE_VALUE. formEditor.sql: renamed FE.typ 'sendmail' to camel hook
 'sendMail'. Changed FormeElement.mode from 'select' to 'radio'. Added empty
 table 'mailLog'.

---
 extension/Documentation/UsersManual/Index.rst | 110 ++++++++---------
 extension/qfq/qfq/AbstractBuildForm.php       |   4 +-
 extension/qfq/qfq/Constants.php               |  28 ++++-
 extension/qfq/qfq/QuickFormQuery.php          |   5 +
 extension/qfq/qfq/form/FormAction.php         |  37 +++++-
 extension/qfq/qfq/report/Report.php           |  81 ++++++-------
 extension/qfq/qfq/report/Sendmail.php         | 113 ++++++++++++------
 extension/qfq/sql/formEditor.sql              |  45 ++++++-
 8 files changed, 280 insertions(+), 143 deletions(-)

diff --git a/extension/Documentation/UsersManual/Index.rst b/extension/Documentation/UsersManual/Index.rst
index 32d6b4a8d..d6f84c44e 100644
--- a/extension/Documentation/UsersManual/Index.rst
+++ b/extension/Documentation/UsersManual/Index.rst
@@ -261,6 +261,7 @@ Store: *FORM* - F
   * Formelements who will be rerendered, after a parent element has been changed by the user.
   * Formelement actions, before saving the form.
   * Values will be sanitized by the class configured in corresponding the formelement. By default, the sanitize class is `alnumx`.
+
  +-------------------------+--------------------------------------------------------------------------------------------------------------------------------------------+
  | Name                    | Explanation                                                                                                                                |
  +=========================+============================================================================================================================================+
@@ -610,9 +611,9 @@ Class: Native
 |               | 'container')                |                                                                                                   |
 +---------------+-----------------------------+---------------------------------------------------------------------------------------------------+
 |type           | enum('checkbox', 'date', 'time', 'datetime',  'dateJQW', 'datetimeJQW', 'extra', 'gridJQW', 'text', 'editor', 'note',           |
-|               | 'password', 'radio', 'select', 'subrecord', 'upload', 'fieldset', 'pill', 'beforeLoad', 'beforeSave',    |
+|               | 'password', 'radio', 'select', 'subrecord', 'upload', 'fieldset', 'pill', 'beforeLoad', 'beforeSave',                           |
 |               | 'beforeInsert', 'beforeUpdate', 'beforeDelete', 'afterLoad', 'afterSave', 'afterInsert', 'afterUpdate', 'afterDelete',          |
-|               | 'sendmail')                                                                                                                     |
+|               | 'sendMail')                                                                                                                     |
 +---------------+-----------------------------+---------------------------------------------------------------------------------------------------+
 |checkType      | enum('min|max', 'pattern',  |                                                                                                   |
 |               | 'number', 'email')          |                                                                                                   |
@@ -658,7 +659,7 @@ Class: Native
 |               |                             | disabled. Group Access: FE-Groups. User will be assigned to FE-Groups and the form defintion      |
 |               |                             | reference such FE-groups. Easy way of granting permission.                                        |
 +---------------+-----------------------------+---------------------------------------------------------------------------------------------------+
-|deleted        | string                      |'yes'|'no'.                                                                                        |
+|deleted        | string                      | 'yes'|'no'.                                                                                       |
 +---------------+-----------------------------+---------------------------------------------------------------------------------------------------+
 |modified       | timestamp                   |updated autmatically through stored procedure                                                      |
 +---------------+-----------------------------+---------------------------------------------------------------------------------------------------+
@@ -1122,8 +1123,21 @@ Types:
 Type: sendmail
 ^^^^^^^^^^^^^^
 
-* Send mail(s) on request.
-* respects 'processRow'
+* Send mail(s) after saving the record.
+
+* FormElement.'''value''': Body of the email.
+
+* FormElement.’‘’parameter’‘’:
+
+  * ‘’‘sendMailTo‘’‘ - Comma-separated list of receiver email addresses. Optional: 'realname <john@doe.com>
+  * ‘’‘sendMailCc‘’‘ - Comma-separated list of receiver email addresses. Optional: 'realname <john@doe.com>
+  * ‘’‘sendMailBcc‘’‘ - Comma-separated list of receiver email addresses. Optional: 'realname <john@doe.com>
+  * ‘’‘sendMailFrom‘’‘ - Sender of the email. Optional: 'realname <john@doe.com>'
+  * ‘’‘sendMailSubject‘’‘ - Subject of the email
+  * ‘’‘sendMailReplyTo‘’‘ - Reply this email address. Optional: 'realname <john@doe.com>'
+  * ‘’‘sendMailFlagAutoSubmit‘’‘ - **on|off** - If 'on', the mail contains the header 'Auto-Submitted: auto-send' - suppress OoO replies
+  * ‘’‘sendMailGrId‘’‘ - Will be copied to the mailLog record. Helps to setup specific logfile queries
+  * ‘’‘sendMailXId‘’‘ - Will be copied to the mailLog record. Helps to setup specific logfile queries
 
 .. _dynamic-update:
 
@@ -1140,10 +1154,11 @@ The following fields will be recalculated during 'Dynamic Update'
 
 To make a form dynamic:
 
-* Mark all FormElements with {dynamic update}=enabled, which should send **or** receive a 'do update' signal.
-* Define the receiving FormElements in a way, that they will interpret the recent user change. The form variable of the
-  specific sender FormElement `{{<sender element>:F:<sanitize>}} should be part of one of the above fields to get an
-  impact. E.g.: ::
+ * Mark all FormElements with {dynamic update}=enabled, which should send **or** receive a 'do update' signal.
+ * Define the receiving FormElements in a way, that they will interpret the recent user change. The form variable of the
+   specific sender FormElement `{{<sender element>:F:<sanitize>}}` should be part of one of the above fields to get an
+   impact. E.g.:
+   ::
 
     [receiving formElement].parameter: itemList={{ SELECT IF({{carPriceRange:FE:alnumx}}='expensive','Ferrari,Tesla,Jaguar','General Motors,Honda,Seat,Fiat') }}
 
@@ -1679,7 +1694,7 @@ The colum name is composed of the string *page* and a trailing character to spec
 ..
 
 +---------------+-----------------------------------------------+-------------------------------------+----------------------------------------------+
-|  column name  |  Purpose                                      |  default value of question parameter  |  Mandatory parameters                      |
+|  column name  |  Purpose                                      |default value of question parameter  |  Mandatory parameters                        |
 +===============+===============================================+=====================================+==============================================+
 |_page          |Internal link without a grafic                 |empty                                |p:<pageId>[&param]                            |
 +---------------+-----------------------------------------------+-------------------------------------+----------------------------------------------+
@@ -1857,29 +1872,41 @@ Easily create Email links.
 Column: _sendmail
 ^^^^^^^^^^^^^^^^^
 
-Send simple plain text emails. Every mail will be logged in the mail log. The logfile can be configured in ext_localconf.php via $TYPO3_CONF_VARS[$_EXTKEY]['log']['mail'].
+<TO:email[,email]>|<FROM:email>|<subject>|<body>|[<REPLY-TO:email>]|[<flag autosubmit: on /off>]|[<grid>]|[xId]|<CC:email[,email]>|<BCC:email[,email]>
+
+
+Send text emails. Every mail will be logged in the table `mailLog`.
 
 **Syntax**
 
 ::
 
-
-    SELECT "receiver@domain.com[:john doe],receiver2@domain.com[:jane doe]|sender@domain.com[:willi wutzmann]|subject|body" AS _sendmail
+    SELECT "john@doe.com|jane@doe.com|Reminder tomorrow|Please dont miss the meeting tomorrow" AS _sendmail
 
 ..
 
-
-
 +------------------------------------------------------------+------------------------------------------------------------------------------------------+------------+
 |**Parameter**                                               |**Description**                                                                           |**Required**|
 +============================================================+==========================================================================================+============+
-|receiver@domain.com[:johndoe],receiver2@domain.com[:janedoe]|Comma-separated list of Email-receiver(s). An optional name can be added using a colon (:)|            |
+|TO:email[,email]                                            |Comma-separated list of receiver email addresses. Optional: `realname <john@doe.com>`     |    yes     |
++------------------------------------------------------------+------------------------------------------------------------------------------------------+------------+
+|FROM:email                                                  |Sender of the email. Optional: 'realname <john@doe.com>'                                  |    yes     |
++------------------------------------------------------------+------------------------------------------------------------------------------------------+------------+
+|subject                                                     |Subject of the email                                                                      |    yes     |
++------------------------------------------------------------+------------------------------------------------------------------------------------------+------------+
+|body                                                        |Message                                                                                   |    yes     |
 +------------------------------------------------------------+------------------------------------------------------------------------------------------+------------+
-|sender@domain.com[:williwutzmann]                           |Sender of the email. An optional name can be added using a colon (:)                      |            |
+|REPLY-TO:email                                              |Email address to reply to (if different from sender)                                      |            |
 +------------------------------------------------------------+------------------------------------------------------------------------------------------+------------+
-|subject                                                     |Subject of the email                                                                      |            |
+|flagAutoSubmit  'on' / 'off'                                |If 'on', the mail contains the header 'Auto-Submitted: auto-send' - suppress OoO replies  |            |
 +------------------------------------------------------------+------------------------------------------------------------------------------------------+------------+
-|body                                                        |Message                                                                                   |            |
+|grId                                                        |Will be copied to the mailLog record. Helps to setup specific logfile queries             |            |
++------------------------------------------------------------+------------------------------------------------------------------------------------------+------------+
+|xId                                                         |Will be copied to the mailLog record. Helps to setup specific logfile queries             |            |
++------------------------------------------------------------+------------------------------------------------------------------------------------------+------------+
+|CC:email[,email]                                            |Comma-separated list of receiver email addresses. Optional: 'realname <john@doe.com>'     |            |
++------------------------------------------------------------+------------------------------------------------------------------------------------------+------------+
+|BCC:email[,email]                                           |Comma-separated list of receiver email addresses. Optional: 'realname <john@doe.com>'     |            |
 +------------------------------------------------------------+------------------------------------------------------------------------------------------+------------+
 
 
@@ -1901,50 +1928,13 @@ This will send an email with subject *Latest News* from company@example.com to j
 ::
 
 
-    10.sql = SELECT "customer1@example.com, customer2@example.com|company@example.com|Latest News|The new version is now available." AS _sendmail
+    10.sql = SELECT "customer1@example.com,Firstname Lastname <customer2@example.com>, Firstname Lastname <customer3@example.com>|company@example.com|Latest News|The new version is now available.|sales@example.com|on|101|222|ceo@example.com|backup@example.com" AS _sendmail
 
 ..
 
-
-
-This will send an email with subject *Latest news* from company@example.com to customer1@example.com and to customer2@example.com.
-
-Column: _advancedmail
-^^^^^^^^^^^^^^^^^^^^^
-
-Send plain text/html emails. This is identical to ?t#Column:_sendmail, but allows to additionaly set the cc:, bcc: and reply-to: -headers. Every mail will be logged in the mail log. The logfile can be configured in ext_localconf.php via
-$TYPO3_CONF_VARS[$_EXTKEY]['log']['mail'].
-
-**Syntax**
-
-::
-
-
-    SELECT "receiver@domain.com[:john doe],receiver2@domain.com[:jane doe]|sender@domain.com[:willi wutzmann]|subject|cc1@domain.com[:willi wutzmann]|bcc1@domain.com[:george wutzmann]|replyto@domain.com[:Support-Desk]|format|body" AS _sendmail
-
-..
-
-
-
-+------------------------------------------------------------+----------------------------------------------------------------------------------------------+------------+
-|**Parameter**                                               |**Description**                                                                               |**required**|
-+============================================================+==============================================================================================+============+
-|receiver@domain.com[:johndoe],receiver2@domain.com[:janedoe]|Comma-separated list of Email-receiver(s). An optional name can be added using a colon (:)    |            |
-+------------------------------------------------------------+----------------------------------------------------------------------------------------------+------------+
-|sender@domain.com[:williwutzmann]                           |Sender of the email. An optional name can be added using a colon (:)                          |            |
-+------------------------------------------------------------+----------------------------------------------------------------------------------------------+------------+
-|subject                                                     |Subject of the email                                                                          |            |
-+------------------------------------------------------------+----------------------------------------------------------------------------------------------+------------+
-|cc1@domain.com[:williwutzmann]                              |Comma-separated list of CC-receiver(s). An optional name can be added using a colon (:)       |            |
-+------------------------------------------------------------+----------------------------------------------------------------------------------------------+------------+
-|bcc1@domain.com[:georgewutzmann]                            |Comma-separated list of BCC-receiver(s). An optional name can be added using a colon (:)      |            |
-+------------------------------------------------------------+----------------------------------------------------------------------------------------------+------------+
-|replyto@domain.com[:Support-Desk]                           |Reply-to address. An optional name can be added using a colon (:)                             |            |
-+------------------------------------------------------------+----------------------------------------------------------------------------------------------+------------+
-|format                                                      |Flag indicating if this is a plaintext or html message. Possible values are 'plain' and 'html'|            |
-+------------------------------------------------------------+----------------------------------------------------------------------------------------------+------------+
-|body                                                        |Message (plain text or html)                                                                  |            |
-+------------------------------------------------------------+----------------------------------------------------------------------------------------------+------------+
+This will send an email with subject *Latest News* from company@example.com to customer1, customer2 and customer3 by
+using a realname for customer2 and customer3 and suppress generating of OoO answer if any receiver is on vacation.
+Additional the CEO as well as backup will receive the mail via CC and BCC.
 
 
 Column: _img
diff --git a/extension/qfq/qfq/AbstractBuildForm.php b/extension/qfq/qfq/AbstractBuildForm.php
index a909769ef..cf36a5719 100644
--- a/extension/qfq/qfq/AbstractBuildForm.php
+++ b/extension/qfq/qfq/AbstractBuildForm.php
@@ -380,8 +380,8 @@ abstract class AbstractBuildForm {
             }
 
             // Get default value
-            $value = ($formElement['value'] === '') ? $this->store->getVar($formElement['name'], $storeUse,
-                $formElement['checkType']) : $formElement['value'];
+            $value = ($formElement[FE_VALUE] === '') ? $this->store->getVar($formElement['name'], $storeUse,
+                $formElement['checkType']) : $formElement[FE_VALUE];
 
             // Typically: $htmlElementNameIdZero = true
             // After Saving a record, staying on the form, the FormElements on the Client are still known as '<feName>:0'.
diff --git a/extension/qfq/qfq/Constants.php b/extension/qfq/qfq/Constants.php
index 232b5eff8..68821394b 100644
--- a/extension/qfq/qfq/Constants.php
+++ b/extension/qfq/qfq/Constants.php
@@ -151,7 +151,8 @@ const ERROR_RECORD_NOT_FOUND = 1066;
 const ERROR_INVALID_EDITOR_PROPERTY_NAME = 1067;
 const ERROR_UNKNOWN_ESCAPE_MODE = 1068;
 const ERROR_MISSING_CONFIG_INI_VALUE = 1069;
-
+const ERROR_SENDMAIL = 1070;
+const ERROR_SENDMAIL_MISSING_VALUE = 1071;
 
 // Subrecord
 const ERROR_SUBRECORD_MISSING_COLUMN_ID = 1100;
@@ -450,6 +451,7 @@ const FE_MODE = 'mode';
 const FE_MODE_SQL = 'modeSql';
 // TODO: Konstante FE_DYNAMIC_UPDATE ueberall einsetzen
 const FE_DYNAMIC_UPDATE = 'dynamicUpdate';
+const FE_VALUE = 'value';
 
 // FormElement columns: via parameter field
 const FE_DATE_FORMAT = 'dateFormat';  // value: FORMAT_DATE_INTERNATIONAL | FORMAT_DATE_GERMAN
@@ -465,9 +467,20 @@ const FE_SQL_UPDATE = 'sqlUpdate'; // Action: Update Statement for slave record
 const FE_SQL_INSERT = 'sqlInsert'; // Action: Insert Statement to create slave record.
 const FE_SQL_DELETE = 'sqlDelete'; // Action: Delete Statement to delete unused slave record.
 const FE_EDITOR_PREFIX = 'editor-'; // TinyMCE configuration settings.
+const FE_SENDMAIL_TO = 'sendMailTo'; // Receiver email adresses. Separate multiple by comma.
+const FE_SENDMAIL_CC = 'sendMailCc'; // CC Receiver email adresses. Separate multiple by comma.
+const FE_SENDMAIL_BCC = 'sendMailBcc'; // BCC Receiver email adresses. Separate multiple by comma.
+const FE_SENDMAIL_FROM = 'sendMailFrom'; // Sender email address.
+const FE_SENDMAIL_SUBJECT = 'sendMailSubject'; // Email subject
+const FE_SENDMAIL_REPLY_TO = 'sendMailReplyTo'; // Reply to email address
+const FE_SENDMAIL_FLAG_AUTO_SUBMIT = 'sendMailFlagAutoSubmit'; // on|off - if 'on', suppresses OoO answers from receivers.
+const FE_SENDMAIL_GR_ID = 'sendMailGrId'; // gr_id: used to classify mail log entries ind table mailLog
+const FE_SENDMAIL_X_ID = 'sendMailXId'; // x_id: used to classify mail log entries ind table mailLog
+
 
 // FormElement Types
 const FE_TYPE_EXTRA = 'extra';
+const FE_TYPE_SENDMAIL = 'sendMail';
 const FE_TYPE_BEFORE_LOAD = 'beforeLoad';
 const FE_TYPE_BEFORE_SAVE = 'beforeSave';
 const FE_TYPE_BEFORE_INSERT = 'beforeInsert';
@@ -537,3 +550,16 @@ const COLUMN_PATH_FILE_NAME = 'pathFileName';
 
 // Used to in SIP Store to handle 'delete' after upload
 const EXISTING_PATH_FILE_NAME = '_existingPathFileName';
+
+//SENDMAIL
+const SENDMAIL_IDX_RECEIVER = 0;
+const SENDMAIL_IDX_SENDER = 1;
+const SENDMAIL_IDX_SUBJECT = 2;
+const SENDMAIL_IDX_BODY = 3;
+const SENDMAIL_IDX_REPLY_TO = 4;
+const SENDMAIL_IDX_FLAG_AUTO_SUBMIT = 5;
+const SENDMAIL_IDX_GR_ID = 6;
+const SENDMAIL_IDX_X_ID = 7;
+const SENDMAIL_IDX_RECEIVER_CC = 8;
+const SENDMAIL_IDX_RECEIVER_BCC = 9;
+const SENDMAIL_IDX_SRC = 10;
diff --git a/extension/qfq/qfq/QuickFormQuery.php b/extension/qfq/qfq/QuickFormQuery.php
index ff69ea8c3..7ff260e52 100644
--- a/extension/qfq/qfq/QuickFormQuery.php
+++ b/extension/qfq/qfq/QuickFormQuery.php
@@ -262,6 +262,7 @@ class QuickFormQuery {
             case FORM_SAVE:
                 $recordId = $this->store->getVar(SIP_RECORD_ID, STORE_SIP);
 
+                // Action: Before
                 $formAction->elements($recordId, $this->feSpecAction, FE_TYPE_BEFORE_INSERT . ',' . FE_TYPE_BEFORE_UPDATE . ',' . FE_TYPE_BEFORE_SAVE);
 
                 // If an old record exist: load it. Necessary to delete uploaded files which should be overwritten.
@@ -275,6 +276,7 @@ class QuickFormQuery {
 
                 $save->processAllUploads($rc);
 
+                // Action: After
                 $formAction->elements($recordId, $this->feSpecAction, FE_TYPE_AFTER_INSERT . ',' . FE_TYPE_AFTER_UPDATE . ',' . FE_TYPE_AFTER_SAVE);
 
                 $htmlElementNameIdZero = false;
@@ -286,6 +288,9 @@ class QuickFormQuery {
                     $htmlElementNameIdZero = true;
                 }
 
+                // Action: Sendmail
+                $formAction->elements($recordId, $this->feSpecAction, FE_TYPE_SENDMAIL);
+
                 // Retrieve FE Values as JSON
                 $data = $build->process($mode, $htmlElementNameIdZero);
                 break;
diff --git a/extension/qfq/qfq/form/FormAction.php b/extension/qfq/qfq/form/FormAction.php
index ef9b75ba7..8da84e288 100644
--- a/extension/qfq/qfq/form/FormAction.php
+++ b/extension/qfq/qfq/form/FormAction.php
@@ -12,6 +12,7 @@ require_once(__DIR__ . '/../Constants.php');
 require_once(__DIR__ . '/../Database.php');
 require_once(__DIR__ . '/../store/Store.php');
 require_once(__DIR__ . '/../Evaluate.php');
+require_once(__DIR__ . '/../report/Sendmail.php');
 
 /**
  * Class formAction
@@ -70,6 +71,7 @@ class FormAction {
 
             $fe = $this->initActionFormElement($fe);
 
+            // Only process FE elements of types listed in $feTypeList. Skip all other
             if (false === Support::findInSet($fe[FE_TYPE], $feTypeList)) {
                 continue;
             }
@@ -83,6 +85,11 @@ class FormAction {
                 continue;
             }
 
+            if ($fe[FE_TYPE] === FE_TYPE_SENDMAIL) {
+                $this->sendMail($fe);
+                //no further processing of current element necessary.
+                continue;
+            }
             // Preparation for Log, Debug
             $this->store->setVar(SYSTEM_FORM_ELEMENT, Logger::formatFormElementName($fe), STORE_SYSTEM);
 
@@ -93,16 +100,21 @@ class FormAction {
     }
 
     /**
-     * Set all necessary keys
+     * Set all necessary keys - subsequent 'isset()' are not necessary anymore.
      *
      * @param array $fe
      * @return array
      */
     private function initActionFormElement(array $fe) {
-        $list = [FE_TYPE, FE_SLAVE_ID, FE_SQL_VALIDATE, FE_SQL_INSERT, FE_SQL_UPDATE, FE_SQL_DELETE, FE_EXPECT_RECORDS, FE_REQUIRED_LIST, FE_MESSAGE_FAIL];
+
+        $list = [FE_TYPE, FE_SLAVE_ID, FE_SQL_VALIDATE, FE_SQL_INSERT, FE_SQL_UPDATE, FE_SQL_DELETE, FE_EXPECT_RECORDS,
+            FE_REQUIRED_LIST, FE_MESSAGE_FAIL, FE_SENDMAIL_TO, FE_SENDMAIL_CC, FE_SENDMAIL_BCC, FE_SENDMAIL_FROM, FE_SENDMAIL_SUBJECT, FE_SENDMAIL_REPLY_TO,
+            FE_SENDMAIL_FLAG_AUTO_SUBMIT, FE_SENDMAIL_GR_ID, FE_SENDMAIL_X_ID];
+
         foreach ($list as $key) {
             Support::setIfNotSet($fe, $key);
         }
+
         return $fe;
     }
 
@@ -152,6 +164,27 @@ class FormAction {
         return true;
     }
 
+    /**
+     * @param array $feSpecAction
+     */
+    private function sendMail(array $feSpecAction) {
+
+        $mail[SENDMAIL_IDX_RECEIVER] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_TO]);
+        $mail[SENDMAIL_IDX_SENDER] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_FROM]);
+        $mail[SENDMAIL_IDX_SUBJECT] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_SUBJECT]);
+        $mail[SENDMAIL_IDX_BODY] = $this->evaluate->parse($feSpecAction[FE_VALUE]);
+        $mail[SENDMAIL_IDX_REPLY_TO] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_REPLY_TO]);
+        $mail[SENDMAIL_IDX_FLAG_AUTO_SUBMIT] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_FLAG_AUTO_SUBMIT]) === 'on' ? 'on' : 'off';
+        $mail[SENDMAIL_IDX_GR_ID] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_GR_ID]);
+        $mail[SENDMAIL_IDX_X_ID] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_X_ID]);
+        $mail[SENDMAIL_IDX_RECEIVER_CC] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_CC]);
+        $mail[SENDMAIL_IDX_RECEIVER_BCC] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_BCC]);
+        $mail[SENDMAIL_IDX_SRC] = "FormId: " . $feSpecAction['formId'] . ", FormElementId: " . $feSpecAction['id'];
+
+        // Mail: send
+        new Sendmail($mail);
+    }
+
     /**
      * If there is a query defined in fe.parameter.FE_SQL_VALIDATE: fire them.
      * Count the selected records and compare them with fe.parameter.FE_EXPECT_RECORDS.
diff --git a/extension/qfq/qfq/report/Report.php b/extension/qfq/qfq/report/Report.php
index 48891401f..b8dcb4e7b 100644
--- a/extension/qfq/qfq/report/Report.php
+++ b/extension/qfq/qfq/report/Report.php
@@ -46,10 +46,6 @@ class Report {
      * @var string
      */
     private $dbAlias = '';
-    /**
-     * @var Log
-     */
-    private $log = null;
 
     // frArray[10.50.5.sql][select ...]
     private $frArray = array();
@@ -77,11 +73,6 @@ class Report {
      */
     private $db = null;
 
-    /**
-     * @var Sendmail
-     */
-    private $sendmail = null;
-
     /**
      * @var array
      */
@@ -145,12 +136,6 @@ class Report {
         // Merged URL-Parameter (key1, id etc...) in resultArray.
         $this->variables->resultArray = array_merge($this->variables->resultArray, array("global." => $this->variables->collectGlobalVariables()));
 
-        // Create Logclass.
-        $this->log = new Log($this->variables->resultArray['global.']);
-
-        // Create sendmail Class. Take care to prepare a fr_log instance.
-        $this->sendmail = new Sendmail($this->log);
-
     }
 
     /**
@@ -165,8 +150,6 @@ class Report {
         $this->indexArray = array();
         $this->levelCount = 0;
 
-        $this->log->set_fr_error($this->fr_error);
-
         // Iteration over Bodytext
         $ttLineArray = explode("\n", $bodyText);
 
@@ -189,6 +172,7 @@ class Report {
      * Example: 10.50.5.sql = select * from person
      *
      * @param    string $ttLine : line to split in level, command, content
+     * @throws SyntaxReportException
      * @return    void
      */
     private function parseFRLine($ttLine) {
@@ -647,32 +631,32 @@ class Report {
                 if (empty($columnValue))
                     break;
 
-                $tmp = explode("|", $columnValue, 3);
+                $mailarr = explode("|", $columnValue, 3);
 
                 // Fake values for tmp[1], tmp[2] to suppress access errors.
-                $tmp[] = '';
-                $tmp[] = '';
+                $mailarr[] = '';
+                $mailarr[] = '';
 
-                if (empty($tmp[0]))
+                if (empty($mailarr[0]))
                     break;
-                $attribute = Support::doAttribute('src', $tmp[0]);
-                $attribute .= Support::doAttribute('alt', $tmp[1]);
+                $attribute = Support::doAttribute('src', $mailarr[0]);
+                $attribute .= Support::doAttribute('alt', $mailarr[1]);
 
-                $content .= '<img ' . $attribute . '>' . $tmp[2];
+                $content .= '<img ' . $attribute . '>' . $mailarr[2];
                 break;
 
             case "mailto":
                 // "<email address>|[Real Name]"  renders to (encrypted via JS): <a href="mailto://<email address>"><email address></a> OR <a href="mailto://<email address>">[Real Name]</a>
-                $tmp = explode("|", $columnValue, 2);
-                if (empty($tmp[0]))
+                $mailarr = explode("|", $columnValue, 2);
+                if (empty($mailarr[0]))
                     break;
 
-                $t1 = explode("@", $tmp[0], 2);
+                $t1 = explode("@", $mailarr[0], 2);
                 $content .= "<script language=javascript><!--" . chr(10);
-                if (empty($tmp[1])) $tmp[1] = $tmp[0];
+                if (empty($mailarr[1])) $mailarr[1] = $mailarr[0];
 
-                $content .= 'var contact = "' . substr($tmp[1], 0, 2) . '"' . chr(10);
-                $content .= 'var contact1 = "' . substr($tmp[1], 2) . '"' . chr(10);
+                $content .= 'var contact = "' . substr($mailarr[1], 0, 2) . '"' . chr(10);
+                $content .= 'var contact1 = "' . substr($mailarr[1], 2) . '"' . chr(10);
                 $content .= 'var email = "' . $t1[0] . '"' . chr(10);
                 $content .= 'var emailHost = "' . $t1[1] . '"' . chr(10);
 
@@ -682,18 +666,35 @@ class Report {
                 break;
 
             case "sendmail":
-                // 'Absender|Empfaenger, mehrere mit Komma getrennt|Betreff|Mailinhalt'
-                $tmp = explode("|", $columnValue, 4);
-                if (count($tmp) < 4) {
+                // '<receiver1>,<receiver2>,...|<sender>|<subject>|<body>|<reply-to>|<flag autosubmit: on /off>'
+                $mailarr = explode("|", $columnValue);
+                if (count($mailarr) < 4) {
                     throw new SyntaxReportException ("Too few parameter for sendmail: $columnValue", ERROR_TOO_FEW_PARAMETER_FOR_SENDMAIL, null, __FILE__, __LINE__, $this->fr_error);
                 }
 
-                $mail['receiver'] = $tmp[0];
-                $mail['sender'] = $tmp[1];
-                $mail['subject'] = $tmp[2];
-                $mail['body'] = $tmp[3];
+                if (!isset($mailarr[SENDMAIL_IDX_REPLY_TO]))
+                    $mailarr[SENDMAIL_IDX_REPLY_TO] = '';
+
+                if (!isset($mailarr[SENDMAIL_IDX_FLAG_AUTO_SUBMIT]))
+                    $mailarr[SENDMAIL_IDX_FLAG_AUTO_SUBMIT] = 'off';
+
+                if (!isset($mailarr[SENDMAIL_IDX_GR_ID]))
+                    $mailarr[SENDMAIL_IDX_GR_ID] = '0';
+
+                if (!isset($mailarr[SENDMAIL_IDX_X_ID]))
+                    $mailarr[SENDMAIL_IDX_X_ID] = '0';
+
+                if (!isset($mailarr[SENDMAIL_IDX_RECEIVER_CC]))
+                    $mailarr[SENDMAIL_IDX_RECEIVER_CC] = '';
+
+                if (!isset($mailarr[SENDMAIL_IDX_RECEIVER_BCC]))
+                    $mailarr[SENDMAIL_IDX_RECEIVER_BCC] = '';
+
+                $mailarr[SENDMAIL_IDX_SRC] = "Report: T3 pageId=" . $this->store->getVar('pageId', STORE_TYPO3) .
+                    ", T3 ttcontentId=" . $this->store->getVar('ttcontentUid', STORE_TYPO3) .
+                    ", Level=" . $full_level;
 
-                $content = $this->sendmail->sendmail($mail);
+                new Sendmail($mailarr);
                 break;
 
             case "vertical":
@@ -705,11 +706,11 @@ class Report {
 
                 # width
                 $width = $arr[2] ? $arr[2] : "1em";
-                $tmp = "width:$width; ";
+                $mailarr = "width:$width; ";
 
                 # height
                 if ($arr[3])
-                    $tmp .= "height:" . $arr[3] . "; ";
+                    $mailarr .= "height:" . $arr[3] . "; ";
 
                 # tag
                 if ($arr[4]) {
diff --git a/extension/qfq/qfq/report/Sendmail.php b/extension/qfq/qfq/report/Sendmail.php
index 7780b90bd..02706543f 100644
--- a/extension/qfq/qfq/report/Sendmail.php
+++ b/extension/qfq/qfq/report/Sendmail.php
@@ -4,57 +4,102 @@ namespace qfq;
 
 //use qfq;
 
-require_once(__DIR__ . '/Define.php');
-require_once(__DIR__ . '/Error.php');
-
+require_once(__DIR__ . '/../Constants.php');
+require_once(__DIR__ . '/../Database.php');
 
 class Sendmail {
 
     /**
-     * @var Log
+     * Sends a mail as specified in $mailarr.
+     * If there is no receiver specified as 'TO': no mail is sent. This is ok and no error.
+     * Logs every send mail as a record in table `mailLog`. Additionally a `grId` and a `xId` can be specified
+     * to assing the logentry to a specific action.
+     * The log record also contains some information which generates the mail (form/formelement or QFQ query).
+     *
+     * Structure mailarr:
+     *    SENDMAIL_IDX_RECEIVER             email address(es)
+     *    SENDMAIL_IDX_SENDER               email address
+     *    SENDMAIL_IDX_SUBJECT              string
+     *    SENDMAIL_IDX_BODY                 string
+     *    SENDMAIL_IDX_REPLY_TO             optional: email address
+     *    SENDMAIL_IDX_FLAG_AUTO_SUBMIT     optional: 'on'|'off'
+     *    SENDMAIL_IDX_GR_ID                optional: integer
+     *    SENDMAIL_IDX_X_ID                 optional: integer
+     *
+     * @param $mailarr
+     * @throws UserFormException
      */
-    private $Log;
+    public function __construct(array $mailarr) {
+
+        // If there is no 'Receiver': do not send a mail.
+        if (!isset($mailarr[SENDMAIL_IDX_RECEIVER]) || $mailarr[SENDMAIL_IDX_RECEIVER] === '') {
+            return;
+        }
+
+        if (count($mailarr) < 4 || $mailarr[SENDMAIL_IDX_SENDER] === '' || $mailarr[SENDMAIL_IDX_SUBJECT] === '' || $mailarr[SENDMAIL_IDX_BODY] === '') {
+            throw new UserFormException("Error sendmail missing one of: receiver, sender, subject or body", ERROR_SENDMAIL_MISSING_VALUE);
+        }
+
+        $header = $this->buildHeader($mailarr);
+        if (!(mb_send_mail($mailarr[SENDMAIL_IDX_RECEIVER], $mailarr[SENDMAIL_IDX_SUBJECT], $mailarr[SENDMAIL_IDX_BODY], $header, "-f " . $mailarr[SENDMAIL_IDX_SENDER]))) {
+            throw new UserFormException("Error sendmail failed.", ERROR_SENDMAIL);
+        }
+
+        $this->mailLog($mailarr, $header);
+    }
 
     /**
-     * Constructor:
-     *
-     * @param Log $fr_log
+     * @param $mailarr
+     * @return string
      */
+    private function buildHeader($mailarr) {
+
+        // "\r\n" needs to be enclosed in double ticks to correctly converted to 0x0d 0x0a,
+        $header = "From: " . $mailarr[SENDMAIL_IDX_SENDER] . "\r\n";
+
+        if (isset($mailarr[SENDMAIL_IDX_REPLY_TO]) && $mailarr[SENDMAIL_IDX_REPLY_TO] != '') {
+            $header .= "Reply-To: " . $mailarr[SENDMAIL_IDX_REPLY_TO] . "\r\n";
+        }
+
+        if (isset($mailarr[SENDMAIL_IDX_FLAG_AUTO_SUBMIT]) && $mailarr[SENDMAIL_IDX_FLAG_AUTO_SUBMIT] === 'on') {
+            $header .= "Auto-Submitted: auto-send\r\n";
+        }
 
-    public function __construct(Log $fr_log) {
+        if (isset($mailarr[SENDMAIL_IDX_RECEIVER_CC]) && $mailarr[SENDMAIL_IDX_RECEIVER_CC] != '') {
+            $header .= "Cc: " . $mailarr[SENDMAIL_IDX_RECEIVER_CC] . "\r\n";
+        }
+
+        if (isset($mailarr[SENDMAIL_IDX_RECEIVER_BCC]) && $mailarr[SENDMAIL_IDX_RECEIVER_BCC] != '') {
+            $header .= "Bcc: " . $mailarr[SENDMAIL_IDX_RECEIVER_BCC] . "\r\n";
+        }
 
-        $this->Log = $fr_log;
+        return $header;
     }
 
     /**
-     * Send an email. Mail delivery should work - else the mails might disappear
-     * Seperate lines in the body with '\r\n'
-     * RC: if RC==0 Returns Output, else 'RC - Output'
+     * Creates a new MailLog Record based on $mailArr / $header.
      *
-     * @param    array $mailarr : $mailarr['receiver'] (multiple with comma), $mailarr['sender'], $mailarr['subject'], $mailarr['body']
-     * @return string
+     * @param array $mailarr
+     * @param $headers
+     * @throws CodeException
+     * @throws DbException
      */
+    private function mailLog(array $mailarr, $header) {
 
-    public function sendmail($mailarr) {
-        $status = 'E';
-
-        // sending only if there is a receiver !
-        if ($mailarr['receiver']) {
-            // if(mail($receiver,$subject,$message, "From: ".$sender."\nX-Mailer: PHP/ . $phpversion()", "-f ".$sender))
-            if (mail($mailarr['receiver'], $mailarr['subject'], $mailarr['body'], "X-Mailer: PHP/" . phpversion() . "\r\nFrom: " . $mailarr['sender'] . ".\r\n", "-f " . $mailarr['sender'])) {
-                $msg = "Mail has been sent";
-                $status = 'I';
-            } else {
-                $msg = "Sending Mail not accepted";
-            }
-        } else {
-            $msg = "Mail not sent: missing receiver";
-        }
+        $log = array();
 
-        // Log every mail
-        $this->Log->log_mail("form", $status, $msg, $mailarr);
+        // Log
+        $log[SENDMAIL_IDX_RECEIVER] = $mailarr[SENDMAIL_IDX_RECEIVER];
+        $log[SENDMAIL_IDX_SENDER] = $mailarr[SENDMAIL_IDX_SENDER];
+        $log[SENDMAIL_IDX_SUBJECT] = $mailarr[SENDMAIL_IDX_SUBJECT];
+        $log[SENDMAIL_IDX_BODY] = $mailarr[SENDMAIL_IDX_BODY];
+        $log[4] = $header;
+        $log[5] = $mailarr[SENDMAIL_IDX_GR_ID];
+        $log[6] = $mailarr[SENDMAIL_IDX_X_ID];
+        $log[7] = $mailarr[SENDMAIL_IDX_SRC];
 
-        return ($msg);
-    } // sendmail()
+        $db = new Database();
+        $db->sql('INSERT INTO MailLog (`receiver`, `sender`, `subject`, `body`, `header`, `grId`, `xId`, `src`) VALUES ( ?, ? ,?, ?, ? ,?, ?, ? )', ROW_REGULAR, $log);
 
+    }
 }
diff --git a/extension/qfq/sql/formEditor.sql b/extension/qfq/sql/formEditor.sql
index e0ed256dc..22ccc0e5e 100644
--- a/extension/qfq/sql/formEditor.sql
+++ b/extension/qfq/sql/formEditor.sql
@@ -38,6 +38,7 @@ CREATE TABLE IF NOT EXISTS `Form` (
   DEFAULT CHARSET = utf8
   AUTO_INCREMENT = 0;
 
+
 #--
 #-- Triggers `Form`
 #--
@@ -70,7 +71,7 @@ CREATE TABLE IF NOT EXISTS `FormElement` (
   `type` ENUM('checkbox', 'date', 'datetime', 'dateJQW', 'datetimeJQW', 'extra', 'gridJQW', 'text',
               'editor', 'time', 'note', 'password', 'radio', 'select', 'subrecord', 'upload', 'fieldset', 'pill',
                          'beforeLoad', 'beforeSave', 'beforeInsert', 'beforeUpdate', 'beforeDelete', 'afterLoad',
-                         'afterSave', 'afterInsert', 'afterUpdate', 'afterDelete', 'sendmail')              NOT NULL  DEFAULT 'text',
+              'afterSave', 'afterInsert', 'afterUpdate', 'afterDelete', 'sendMail') NOT NULL  DEFAULT 'text',
   `subrecordOption` SET('edit', 'delete', 'new')                                                            NOT NULL  DEFAULT '',
   `checkType`       ENUM('alnumx', 'digit', 'email', 'min|max', 'min|max date', 'pattern', 'allbut', 'all') NOT NULL  DEFAULT 'alnumx',
   `checkPattern`    VARCHAR(255)                                                                            NOT NULL  DEFAULT '',
@@ -208,7 +209,7 @@ VALUES
   (2, 'modeSql', 'Mode sql', 'show', 'text', 'all', 'native', 170, '70,2', 255, '', '', '', '', '', '', 100, '', 'no', '', '', '', ''),
   (2, 'class', 'Class', 'show', 'select', 'all', 'native', 180, 0, 255, '', '', '{{class:FSRD0:alnumx}}', '', '', '', 100, '', 'yes', '', '', '', ''),
   (2, 'type', 'Type', 'show', 'select', 'all', 'native', 190, 0, 255, '', '', '', '', '',
-   'itemList={{SELECT IF( "{{class:FRD0:alnumx}}"="native","checkbox,date,time,datetime,dateJQW,datetimeJQW,extra,gridJQW,text,editor,note,password,radio,select,subrecord,upload", IF("{{class:FRD0:alnumx}}"="action","beforeLoad,beforeSave,beforeInsert,beforeUpdate,beforeDelete,afterLoad,afterSave,afterInsert,afterUpdate,afterDelete,sendmail", "fieldset,pill")  ) }}',
+   'itemList={{SELECT IF( "{{class:FRD0:alnumx}}"="native","checkbox,date,time,datetime,dateJQW,datetimeJQW,extra,gridJQW,text,editor,note,password,radio,select,subrecord,upload", IF("{{class:FRD0:alnumx}}"="action","beforeLoad,beforeSave,beforeInsert,beforeUpdate,beforeDelete,afterLoad,afterSave,afterInsert,afterUpdate,afterDelete,sendMail", "fieldset,pill")  ) }}',
    100, '', 'yes', '', '', '', ''),
   (2, 'subrecordOption', 'Subrecord Option', 'show', 'checkbox', 'all', 'native', 200, 0, 0, '', '', '', '', '', '', 100, '', 'no', '', '', '',
    ''),
@@ -227,8 +228,8 @@ VALUES
   (2, 'note', 'note', 'show', 'text', 'all', 'native', 450, 0, 255, '', '', '', '', '', '', 102, '', 'no', '', '', '', ''),
   (2, 'tooltip', 'Tooltip', 'show', 'text', 'all', 'native', 460, 0, 255, '', '', '', '', '', '', 102, '', 'no', '', '', '', ''),
   (2, 'placeholder', 'Placeholder', 'show', 'text', 'all', 'native', 470, 0, 255, '', '', '', '', '', '', 102, '', 'no', '', '', '', ''),
-  (2, 'value', 'value', 'show', 'text', 'all', 'native', 500, 0, 255, '', '', '', '', '', '', 103, '', 'no', '', '', '', ''),
-  (2, 'sql1', 'sql1', 'show', 'text', 'all', 'native', 510, '70,5', 255, '', '', '', '', '', '', 103, '', 'no', '', '', '', ''),
+  (2, 'value', 'value', 'show', 'text', 'all', 'native', 500, '40,2', 255, '', '', '', '', '', '', 103, '', 'no', '', '', '', ''),
+  (2, 'sql1', 'sql1', 'show', 'text', 'all', 'native', 510, '40,5', 255, '', '', '', '', '', '', 103, '', 'no', '', '', '', ''),
   (2, 'parameter', 'Parameter', 'show', 'text', 'all', 'native', 520, '40,4', 255, '', '', '', '', '', '', 103, '',
    'no', '', '', '', ''),
   (2, 'clientJs', 'ClientJS', 'show', 'text', 'all', 'native', 530, 0, 255, '', '', '', '', '', '', 103, '', 'no', '', '', '', ''),
@@ -239,3 +240,39 @@ VALUES
   (2, 'created', 'Created', 'readonly', 'text', 'all', 'native', 630, 0, 20, '', '', '', '', '', '', 104, '', 'no', '',
    '', '', '');
 
+
+# ----------------------------------------
+# MailLog
+
+DROP TABLE IF EXISTS `MailLog`;
+CREATE TABLE `MailLog` (
+  `id`       INT(11)      NOT NULL  AUTO_INCREMENT,
+  `grId`     INT(11)      NOT NULL  DEFAULT '0',
+  `xId`      INT(11)      NOT NULL  DEFAULT '0',
+  `receiver` TEXT         NOT NULL  DEFAULT '',
+  `sender`   VARCHAR(255) NOT NULL  DEFAULT '',
+  `subject`  VARCHAR(255) NOT NULL  DEFAULT '',
+  `body`     TEXT         NOT NULL  DEFAULT '',
+  `header`   VARCHAR(255) NOT NULL  DEFAULT '',
+  `attach`   VARCHAR(255) NOT NULL  DEFAULT '',
+  `src`      VARCHAR(255) NOT NULL  DEFAULT '',
+  `modified` TIMESTAMP    NOT NULL  DEFAULT CURRENT_TIMESTAMP,
+  `created`  DATETIME     NOT NULL  DEFAULT '0000-00-00 00:00:00',
+
+  PRIMARY KEY (`id`)
+)
+  ENGINE = InnoDB
+  DEFAULT CHARSET = utf8
+  AUTO_INCREMENT = 0;
+
+
+#--
+#-- Triggers `MailLog`
+#--
+#DROP TRIGGER IF EXISTS `on_MailLog_update_modified`;
+#DELIMITER //
+#CREATE TRIGGER `on_MailLog_update_modified` BEFORE UPDATE ON `MailLog`
+#FOR EACH ROW SET NEW.modified =
+#current_timestamp()
+#//
+#DELIMITER ;
-- 
GitLab