formSpec = $formSpec; $this->primaryTableName = Support::setIfNotSet($formSpec, F_TABLE_NAME); $this->db = $db; $this->store = Store::getInstance('', $phpUnit); $this->evaluate = new Evaluate($this->store, $this->db); } /** * @param integer $recordId * @param array $feSpecAction * @param string $feTypeList * On FormLoad: FE_TYPE_BEFORE_LOAD, FE_TYPE_AFTER_LOAD * Before Save: FE_TYPE_BEFORE_SAVE, FE_TYPE_BEFORE_INSERT, FE_TYPE_BEFORE_UPDATE, FE_TYPE_BEFORE_DELETE * After Save: FE_TYPE_AFTER_SAVE, FE_TYPE_AFTER_INSERT, FE_TYPE_AFTER_UPDATE, FE_TYPE_AFTER_DELETE * @return bool: true if there are potential changes on the DB like fired SQL statements, else false. * @throws CodeException * @throws DbException * @throws UserFormException */ public function elements($recordId, array $feSpecAction, $feTypeList) { $flagModified = false; // Iterate over all Action FormElements foreach ($feSpecAction as $fe) { // Preparation for Log, Debug $this->store->setVar(SYSTEM_FORM_ELEMENT, Logger::formatFormElementName($fe), STORE_SYSTEM); $fe = HelperFormElement::initActionFormElement($fe); // Only process FE elements of types listed in $feTypeList. Skip all other if (false === Support::findInSet($fe[FE_TYPE], $feTypeList)) { continue; } // Process templateGroup action elements if (isset($fe[FE_ID_CONTAINER]) && $fe[FE_ID_CONTAINER] > 0) { //TODO: hier muss ein FE_TG_DEFAULT gesetzt werden, im Fall es ist keine maxlength angegeben. // Get native 'templateGroup'-FE - to retrieve MAX_LENGTH $feTemplateGroup = $this->db->sql(SQL_FORM_ELEMENT_TEMPLATE_GROUP_FE_ID, ROW_REGULAR, [$fe[FE_ID_CONTAINER]]); if (isset($feTemplateGroup[0][FE_TYPE]) && $feTemplateGroup[0][FE_TYPE] == FE_TYPE_TEMPLATE_GROUP) { if (count($feTemplateGroup) == 1) { $fe[FE_ID_CONTAINER] = 0; for ($ii = 1; $ii <= $feTemplateGroup[0][FE_MAX_LENGTH]; $ii++) { $feNew = OnArray::arrayValueReplace($fe, FE_TEMPLATE_GROUP_NAME_PATTERN, $ii); $feNew = OnArray::arrayValueReplace($feNew, FE_TEMPLATE_GROUP_NAME_PATTERN_0, $ii - 1); if ($this->elements($recordId, [$feNew], $feTypeList)) { $flagModified = true; } } } else { // At the moment 'action' elements have to point to a templateGroup - nothing else is defined. Break if there is something else throw new UserFormException("Expect a 'templateGroup' record in FormElement.id=" . $fe[FE_ID_CONTAINER], ERROR_RECORD_NOT_FOUND); } continue; // skip to next FormElement } } switch ($fe[FE_TYPE]) { case FE_TYPE_BEFORE_LOAD: case FE_TYPE_AFTER_LOAD: case FE_TYPE_AFTER_DELETE: # Main record is already deleted. Do not try to load it again. break; default: // Always work on recent data: previous actions might have modified the data. $this->fillStoreRecord($this->primaryTableName, $recordId); } if (!$this->checkRequiredList($fe)) { continue; } if (isset($fe[FE_FILL_STORE_LDAP])) { $keyNames = [F_LDAP_SERVER, F_LDAP_BASE_DN, F_LDAP_ATTRIBUTES, F_LDAP_SEARCH, F_LDAP_TIME_LIMIT]; $fe = OnArray::copyArrayItemsIfNotAlreadyExist($this->formSpec, $fe, $keyNames); // Extract necessary elements $config = OnArray::getArrayItems($fe, [FE_LDAP_SERVER, FE_LDAP_BASE_DN, FE_LDAP_SEARCH, FE_LDAP_ATTRIBUTES, FE_LDAP_USE_BIND_CREDENTIALS]); $config = $this->evaluate->parseArray($config); if ($fe[FE_LDAP_USE_BIND_CREDENTIALS] == 1) { $config[SYSTEM_LDAP_1_RDN] = $this->store->getVar(SYSTEM_LDAP_1_RDN, STORE_SYSTEM); $config[SYSTEM_LDAP_1_PASSWORD] = $this->store->getVar(SYSTEM_LDAP_1_PASSWORD, STORE_SYSTEM); } $ldap = new Ldap(); $arr = $ldap->process($config, '', MODE_LDAP_SINGLE); $this->store->setStore($arr, STORE_LDAP, true); } if ($fe[FE_TYPE] === FE_TYPE_SENDMAIL) { $this->sendMail($fe); //no further processing of current element necessary. continue; } $this->validate($fe); $this->doSlave($fe, $recordId); $flagModified = true; } return $flagModified; } /** * Copy the current primary record to STORE_RECORD * * @param $table * @param $recordId * @throws CodeException * @throws DbException * @throws UserFormException */ private function fillStoreRecord($table, $recordId) { if (!is_string($table) || $table === '') { throw new UserFormException(""); } if ($recordId !== false && $recordId > 0) { $record = $this->db->sql("SELECT * FROM $table WHERE id = ?", ROW_EXPECT_1, [$recordId]); $this->store->setStore($record, STORE_RECORD, true); } } /** * Process all FormElements given in the `requiredList` identified by their name. * If none is empty in STORE_FORM return true, else false. * If none FormElement is specified, return true. * * @param array $fe * @return bool true if none FE is specified or all specified are non empty. */ private function checkRequiredList(array $fe) { if (!isset($fe[FE_REQUIRED_LIST]) || $fe[FE_REQUIRED_LIST] === '') { return true; } $arr = explode(',', $fe[FE_REQUIRED_LIST]); foreach ($arr as $key) { $key = trim($key); $val = $this->store->getVar($key, STORE_FORM, SANITIZE_ALLOW_ALL); if ($val === false || $val === '' || $val === '0') { return false; } } 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]) === 'off' ? 'off' : 'on'; $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[FE_FORM_ID] . ", 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. * If match: everything is fine, do nothing. * Else throw UserFormException with error message of fe.parameter.FE_MESSAGE_FAIL * * @param array $fe * @throws UserFormException */ private function validate(array $fe) { // Is there something to check? if ($fe[FE_SQL_VALIDATE] === '') { return; } $expect = $this->evaluate->parse($fe[FE_EXPECT_RECORDS]); if ($fe[FE_MESSAGE_FAIL] === '') { throw new UserFormException("Missing error message. Column: " . FE_MESSAGE_FAIL, ERROR_MISSING_MESSAGE_FAIL); } // Do the check $result = $this->evaluate->parse($fe[FE_SQL_VALIDATE]); if (!is_array($result)) { throw new UserFormException("Expected an array for '" . FE_SQL_VALIDATE . "', got a scalar. Please check for {{!...", ERROR_EXPECTED_ARRAY); } // If there is at least one record count given, who matches: return 'check succeeded' $countRecordsArr = explode(',', $expect); foreach ($countRecordsArr AS $count) { if (count($result) == $count) { return; // check succesfully passed } } $msg = $this->evaluate->parse($fe[FE_MESSAGE_FAIL]); // Replace possible dynamic parts // Throw user defineable error message throw new UserFormException($msg, ERROR_REPORT_FAILED_ACTION); } /** * Create the slave record. First try to evaluate a slaveId. Depending if the slaveId > 0 choose `sqlUpdate` or `sqlInsert` * * @param array $fe * @param int $recordId * @return int * @throws CodeException * @throws DbException * @throws UserFormException */ private function doSlave(array $fe, $recordId) { // Get the slaveId $slaveId = $this->evaluate->parse($fe[FE_SLAVE_ID]); if ($slaveId === '' && $fe[FE_NAME] !== '') { // if the current action element has the same name as a real master record column: take that value as an id $slaveId = $this->store->getVar($fe[FE_NAME], STORE_RECORD); } if ($slaveId === '' || $slaveId === false) { $slaveId = 0; } // Store the slaveId: it's used and replaced in the update statement. $this->store->setVar(VAR_SLAVE_ID, $slaveId, STORE_VAR, true); // If given: fire a sqlBefore query $this->evaluate->parse($fe[FE_SQL_BEFORE]); $doInsert = ($slaveId == 0); $doUpdate = ($slaveId != 0); $doDelete = ($slaveId != 0) && $fe[FE_SQL_DELETE] != ''; $flagHonor = isset($fe[FE_SQL_HONOR_FORM_ELEMENTS]) && $fe[FE_SQL_HONOR_FORM_ELEMENTS] != ''; if ($flagHonor) { $filled = $this->checkFormElements($fe[FE_SQL_HONOR_FORM_ELEMENTS]); $doInsert = $filled && $doInsert; $doUpdate = $filled && $doUpdate; $doDelete = !$filled && $doDelete; } // Fire slave query if ($doInsert) { $slaveId = $this->evaluate->parse($fe[FE_SQL_INSERT]); // Store the slaveId: might be used later $this->store->setVar(VAR_SLAVE_ID, $slaveId, STORE_VAR, true); } if ($doUpdate) { $this->evaluate->parse($fe[FE_SQL_UPDATE]); } // Fire a delete query if ($doDelete) { $this->evaluate->parse($fe[FE_SQL_DELETE]); $slaveId = 0; } // Check if there is a column with the same name as the 'action'-FormElement. if (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]); } // If given: fire a sqlAfter query $this->evaluate->parse($fe[FE_SQL_AFTER]); return $slaveId; } /** * Iterates over list of FormElement-names and check STORE_FORM if there is a corresponding value. If at least one * of the give elements is non empty, return true. If all elements are empty, return false. * * @param string $listOfFormElementNames E.g.: 'city, street, number' * @return bool true if at lease one of the named elements is non empty on STORE_FORM (use SANATIZE_ALLOW_ALL to perform the check) */ private function checkFormElements($listOfFormElementNames) { $arr = explode(',', $listOfFormElementNames); foreach ($arr as $key) { $value = $this->store->getVar(trim($key), STORE_FORM . STORE_EMPTY, SANITIZE_ALLOW_ALL); if ($value != '') { return true; } } return false; } /** * 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_BEFORE, FE_SQL_INSERT, FE_SQL_UPDATE, FE_SQL_DELETE, FE_SQL_AFTER, 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; } }