* r= (table.id for a single record form) * keySemId,keySemIduser * */ /** * Class Qfq * @package qfq */ class QuickFormQuery { /** * @var \qfq\Store instantiated class */ protected $store = null; /** * @var Database instantiated class */ protected $db = null; /** * @var Evaluate instantiated class */ protected $eval = null; protected $formSpec = array(); protected $feSpecAction = array(); // Form Definition: copy of the loaded form protected $feSpecNative = array(); // FormEelement Definition: all formElement.class='action' of the loaded form /** * @var array */ private $t3data = array(); // FormEelement Definition: all formElement.class='native' of the loaded form private $phpUnit = false; /* * TODO: * Preparation: setup logging, database access, record locking * fill stores * Check permission_create / permission_update * Multi: iterate over all records, Single: activate record * Check mode: Load | Save * doActions 'Before' * Do all FormElements * doActions 'After' */ /** * Construct the Form Class and Store too. This is the base initialization moment. * * As a result of instantiating of Form, the class Store will initially called the first time and therefore instantiated automatically. * Store might throw an exception, in case the URL-passed SIP is invalid. * * @param string $bodytext */ public function __construct(array $t3data = array(), $phpUnit = false) { $this->phpUnit = $phpUnit; mb_internal_encoding("UTF-8"); // session.cache_expire // session.cookie_lifetime // session.gc_maxlifetime // $arr1['sessionname'] = session_name(); // $arr1['session.auto_start'] = ini_get('session.auto_start'); // $arr1['session.gc_maxlifetime'] = ini_get('session.gc_maxlifetime'); // $arr1['session.cookie_lifetime'] = ini_get('session.cookie_lifetime'); // $arr1['session.name'] = ini_get('session.name'); // Refresh the session even if no new data saved. $_SESSION['LAST_ACTIVITY'] = time(); set_error_handler("\\qfq\\ErrorHandler::exception_error_handler"); if (!isset($t3data['bodytext'])) $t3data['bodytext'] = ''; if (!isset($t3data['uid'])) $t3data['uid'] = 0; $btp = new BodytextParser(); $t3data['bodytext'] = $btp->process($t3data['bodytext']); $this->t3data = $t3data; $bodytext = $this->t3data['bodytext']; $this->store = Store::getInstance($bodytext, $phpUnit); $this->store->setVar(TYPO3_TT_CONTENT_UID, $t3data['uid'], STORE_TYPO3); $this->db = new Database(); $this->eval = new Evaluate($this->store, $this->db); } /** * Returns the defined forwardMode and set, if necessary, $forwardPage * * @param $forwardPage * @return mixed */ public function getForwardMode(&$forwardPage) { $forwardPage = $this->formSpec['forwardPage']; return $this->formSpec['forwardMode']; } /** * Main entrypoint for display content: form or report * * @return string */ public function process() { $html = ''; if ($this->store->getVar(TYPO3_DEBUG_SHOW_BODY_TEXT, STORE_TYPO3) === '1') { $html .= Support::appendTooltip('', $this->t3data['bodytext']); } $html .= $this->doForm(FORM_LOAD); $html .= $this->doReport(); $class = $this->store->getVar(SYSTEM_CSS_CLASS_QFQ_CONTAINER, STORE_SYSTEM); if ($class) $html = Support::wrapTag("
", $html); return $html; } /** * Process form. There * * @return string * @throws CodeException * @throws UserFormException */ private function doForm($mode) { $data = ''; $foundInStore = ''; // Fill STORE_FORM if ($mode === FORM_UPDATE || $mode === FORM_SAVE) { $fillStoreForm = new FillStoreForm(); $fillStoreForm->process(); } $formName = $this->loadFormSpecification($mode, $foundInStore); if ($formName === false) return ''; $sipFound = $this->validateForm($foundInStore); if (!$sipFound) { $this->store->createSipAfterFormLoad($formName); } $this->store->fillStoreTableDefaultColumnType($this->formSpec['tableName']); switch ($mode) { case FORM_LOAD: case FORM_UPDATE: switch ($this->formSpec['render']) { case 'plain': $build = new BuildFormPlain($this->formSpec, $this->feSpecAction, $this->feSpecNative); break; case 'table': $build = new BuildFormTable($this->formSpec, $this->feSpecAction, $this->feSpecNative); break; case 'bootstrap': $build = new BuildFormBootstrap($this->formSpec, $this->feSpecAction, $this->feSpecNative); break; default: throw new CodeException("This statement should never be reached", ERROR_CODE_SHOULD_NOT_HAPPEN); } $data = $build->process($mode); break; case FORM_SAVE: $save = new Save($this->formSpec, $this->feSpecAction, $this->feSpecNative); $save->process(); break; default: throw new CodeException("This statement should never be reached", ERROR_CODE_SHOULD_NOT_HAPPEN); } return $data; } /** * Load form. Evaluates form. Load FormElements. * * After processing: * Loaded Form is in $this->formSpec * Loaded 'action' FormElements are in $this->feSpecAction * Loaded 'native' FormElements are in $this->feSpecNative * * @param string $mode FORM_LOAD|FORM_SAVE|FORM_UPDATE * @param string $foundInStore * @return bool|string if found the formName, else 'false'. * @throws CodeException * @throws DbException * @throws UserFormException */ private function loadFormSpecification($mode, &$foundInStore = '') { // formName if (false === ($formName = $this->getFormName($mode, $foundInStore))) { return false; } $this->store->setVar(SYSTEM_FORM, $formName, STORE_SYSTEM); // Check if there is a recordId specified in Bodytext - as variable or query. $rTmp = $this->store->getVar(CLIENT_RECORD_ID, STORE_TYPO3, SANITIZE_ALLOW_ALL); if (false !== $rTmp && !is_int($rTmp)) { $rTmp = $this->eval->parse($rTmp); $this->store->setVar(CLIENT_RECORD_ID, $rTmp, STORE_TYPO3); } // Load form $form = $this->db->sql("SELECT * FROM Form AS f WHERE f.name LIKE ? AND f.deleted='no'", ROW_EXPECT_1, [$formName], 'Form not found or multiple forms with the same name.'); $this->formSpec = $this->eval->parseArray($form); HelperFormElement::explodeParameter($this->formSpec); # Set defaults: if (!isset($this->formSpec['class'])) $this->formSpec['class'] = ''; // Clear $this->store->setVar(SYSTEM_FORM_ELEMENT, '', STORE_SYSTEM); // FE: Action $this->feSpecAction = $this->eval->parseArray($this->db->sql(SQL_FORM_ELEMENT_ALL_CONTAINER, ROW_REGULAR, ['no', $this->formSpec["id"], 'action'])); HelperFormElement::explodeParameterInArrayElements($this->feSpecAction); // FE: Native & 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"; switch ($mode) { case FORM_LOAD: $this->feSpecNative = $this->db->sql(SQL_FORM_ELEMENT_SPECIFIC_CONTAINER, ROW_REGULAR, ['no', $this->formSpec["id"], 'native,container', 0]); break; case FORM_SAVE: case FORM_UPDATE: $this->feSpecNative = $this->db->sql(SQL_FORM_ELEMENT_ALL_CONTAINER, ROW_REGULAR, ['no', $this->formSpec["id"], 'native']); break; default: } HelperFormElement::explodeParameterInArrayElements($this->feSpecNative); return $formName; } /** * Get the formName from STORE_TYPO3 (bodytext), STORE_SIP or by STORE_CLIENT (URL). * * FORM_LOAD: * Specified in T3 body text with form= Returned Store:Typo3 * Specified in T3 body text with form={{form}} ':FSRD' Returned Store:SIP * Specified in T3 body text with form={{form:C:ALNUMX}} Returned Store:Client * Specified in T3 body text with form={{SELECT registrationFormName FROM Conference WHERE id={{conferenceId:S0}} }} * Specified in T3 body text with form={{SELECT registrationFormName FROM Conference WHERE id={{conferenceId:C0:DIGIT}} }} * Specified in SIP * * FORM_SAVE: * Specified in SIP * * * @param string $mode FORM_LOAD|FORM_SAVE|FORM_UPDATE * @param string $foundInStore * @return array|bool|mixed|null|string Formname (Form.name) or FALSE, if no formname found. * @throws CodeException * @throws UserFormException */ private function getFormName($mode, &$foundInStore = '') { $dummy = array(); switch ($mode) { case FORM_LOAD: $store = STORE_TYPO3; break; case FORM_SAVE: case FORM_UPDATE: $store = STORE_SIP; break; default: throw new CodeException("Unknown mode: $mode.", ERROR_UNKNOWN_MODE); } $storeFormName = $this->store->getVar(SIP_FORM, $store, '', $foundInStore); $formName = $this->eval->parse($storeFormName, 0, $dummy, $foundInStore); // If the formname is '': no formname name. if ($formName === '') return false; // If the formname is surrounded by single ticks: the token (typically 'form') has not been replaced by a value. if ($formName[0] === "'" && $formName[strlen($formName) - 1] === "'") { return false; } return $formName; } /** * Check if loading of the given form is permitted. If not, throw an exception. * * @param $formNameFoundInStore * @return bool 'true' if SIP exists, else 'false' * @throws CodeException * @throws UserFormException * @internal param $foundInStore */ private function validateForm($formNameFoundInStore) { // Retrieve record_id either from SIP (prefered) or via URL $r = $this->store->getVar(SIP_RECORD_ID, STORE_SIP . STORE_TYPO3 . STORE_CLIENT, '', $recordIdFoundInStore); // If there is a record_id>0: EDIT else NEW: 'sip','logged_in','logged_out','always','never' $permitMode = ($r > 0) ? $this->formSpec['permitEdit'] : $this->formSpec['permitNew']; $feUserLoggedIn = isset($GLOBALS["TSFE"]->fe_user->user["uid"]) && $GLOBALS["TSFE"]->fe_user->user["uid"] > 0; $sipFound = $this->store->getVar(SIP_SIP, STORE_SIP) !== false; if ($sipFound) { if (($formNameFoundInStore === STORE_CLIENT) || ($recordIdFoundInStore === STORE_CLIENT)) { throw new UserFormException("SIP exist but FORM or RECORD_ID are given by CLIENT.", ERROR_SIP_EXIST_BUT_OTHER_PARAM_GIVEN_BY_CLIENT); } } switch ($permitMode) { case FORM_PERMISSION_SIP: if (!$sipFound || $formNameFoundInStore !== STORE_SIP || $recordIdFoundInStore !== STORE_SIP) { throw new UserFormException("SIP Parameter needed for this form.", ERROR_SIP_NEEDED_FOR_THIS_FORM); } break; case FORM_PERMISSION_LOGGED_IN: if (!$feUserLoggedIn) { throw new UserFormException("User not logged in.", ERROR_USER_NOT_LOGGED_IN); } break; case FORM_PERMISSION_LOGGED_OUT: if ($feUserLoggedIn) { throw new UserFormException("User logged in.", ERROR_USER_LOGGED_IN); } break; case FORM_PERMISSION_ALWAYS: break; case FORM_PERMISSION_NEVER: throw new UserFormException("Loading form forbidden.", ERROR_FORM_FORBIDDEN); default: throw new CodeException("Unknown permission mode: '" . $permitMode . "'", ERROR_FORM_UNKNOWN_PERMISSION_MODE); } // Form Definition valid? if ($this->formSpec['multiMode'] !== 'none' && $this->formSpec['multiSql'] === '') { throw new UserFormException("MultiMode selected, but MultiSQL missing", ERROR_MULTI_SQL_MISSING); } return $sipFound; } /** * Process the SQL Queries from bodytext. Return the output. * * @return string */ private function doReport() { $report = new Report($this->t3data, $this->store->getVar(SYSTEM_SESSION_NAME, STORE_SYSTEM), $this->eval, $this->phpUnit); $html = $report->process(); return $html; } /** * Save the current form. * * @return string */ public function saveForm() { $html = $this->doForm(FORM_SAVE); return $html; } /** * Update FormElements and form values. Receives the current form values via POST. * * @return array * @throws CodeException */ public function updateForm() { $json = $this->doForm(FORM_UPDATE); return $json; } /** * Delete a record (tablename and recordid are given) or process a 'delete form' * * @throws CodeException * @throws DbException * @throws UserFormException */ public function delete() { #TODO: implement 'delete form' // simple delete: table and recordId are given $recordId = $this->store->getVar(SIP_RECORD_ID, STORE_SIP); $table = $this->store->getVar(SIP_TABLE, STORE_SIP); if ($recordId === false || $recordId < 1 || $table === false || $table === '') { throw new UserFormException("Invalid or missing parameter: recordId=$recordId, table=$table", ERROR_INVALID_OR_MISSING_PARAMETER); } $this->db->sql("DELETE FROM $table WHERE id = ? LIMIT 1", ROW_REGULAR, [$recordId]); } }