public static function Run($strFormId, $strAlternateHtmlFile = null) { // Ensure strFormId is a class $objClass = new $strFormId(); // Ensure strFormId is a subclass of QForm if (!$objClass instanceof QForm) { throw new QCallerException('Object is not a subclass of QForm (note, it can NOT be a subclass of QFormBase): ' . $strFormId); } // See if we can get a Form Class out of PostData $objClass = null; if (array_key_exists('Qform__FormId', $_POST) && $_POST['Qform__FormId'] == $strFormId && array_key_exists('Qform__FormState', $_POST)) { $strPostDataState = $_POST['Qform__FormState']; if ($strPostDataState) { // We might have a valid form state -- let's see by unserializing this object $objClass = QForm::Unserialize($strPostDataState); } } if ($objClass) { global ${$strFormId}; ${$strFormId} = $objClass; $objClass->strCallType = $_POST['Qform__FormCallType']; $objClass->intFormStatus = QFormBase::FormStatusUnrendered; if ($objClass->strCallType == QCallType::Ajax) { QApplication::$RequestMode = QRequestMode::Ajax; } // Globalize and Set Variable global ${$strFormId}; ${$strFormId} = $objClass; // Iterate through all the control modifications $strModificationArray = explode("\n", trim($_POST['Qform__FormUpdates'])); if ($strModificationArray) { foreach ($strModificationArray as $strModification) { $strModification = trim($strModification); if ($strModification) { $intPosition = strpos($strModification, ' '); $strControlId = substr($strModification, 0, $intPosition); $strModification = substr($strModification, $intPosition + 1); $intPosition = strpos($strModification, ' '); if ($intPosition !== false) { $strProperty = substr($strModification, 0, $intPosition); $strValue = substr($strModification, $intPosition + 1); } else { $strProperty = $strModification; $strValue = null; } switch ($strProperty) { case 'Parent': if ($strValue) { if ($strValue == $objClass->FormId) { $objClass->objControlArray[$strControlId]->SetParentControl(null); } else { $objClass->objControlArray[$strControlId]->SetParentControl($objClass->objControlArray[$strValue]); } } else { // Remove all parents $objClass->objControlArray[$strControlId]->SetParentControl(null); $objClass->objControlArray[$strControlId]->SetForm(null); $objClass->objControlArray[$strControlId] = null; unset($objClass->objControlArray[$strControlId]); } break; default: if (array_key_exists($strControlId, $objClass->objControlArray)) { $objClass->objControlArray[$strControlId]->__set($strProperty, $strValue); } break; } } } } // Clear the RenderedCheckableControlArray $objClass->blnRenderedCheckableControlArray = array(); $strCheckableControlList = trim($_POST['Qform__FormCheckableControls']); $strCheckableControlArray = explode(' ', $strCheckableControlList); foreach ($strCheckableControlArray as $strCheckableControl) { $objClass->blnRenderedCheckableControlArray[trim($strCheckableControl)] = true; } // Iterate through all the controls foreach ($objClass->objControlArray as $objControl) { // If they were rendered last time and are visible (and if ServerAction, enabled), then Parse its post data if ($objControl->Visible && ($objClass->strCallType == QCallType::Ajax || $objControl->Enabled) && $objControl->RenderMethod) { // Call each control's ParsePostData() $objControl->ParsePostData(); } // Reset the modified/rendered flags and the validation // in ALL controls $objControl->ResetFlags(); $objControl->ValidationReset(); } // Trigger Run Event (if applicable) $objClass->Form_Run(); // Trigger Load Event (if applicable) $objClass->Form_Load(); // Trigger a triggered control's Server- or Ajax- action (e.g. PHP method) here (if applicable) $objClass->TriggerActions(); } else { // We have no form state -- Create Brand New One $objClass = new $strFormId(); // Setup HTML Include File Path, based on passed-in strAlternateHtmlFile (if any) try { $objClass->HtmlIncludeFilePath = $strAlternateHtmlFile; } catch (QCallerException $objExc) { $objExc->IncrementOffset(); throw $objExc; } global ${$strFormId}; ${$strFormId} = $objClass; // By default, this form is being created NOT via a PostBack // So there is no CallType $objClass->strCallType = QCallType::None; $objClass->strFormId = $strFormId; $objClass->intFormStatus = QFormBase::FormStatusUnrendered; $objClass->objControlArray = array(); $objClass->objGroupingArray = array(); // Globalize and Set Variable global ${$strFormId}; ${$strFormId} = $objClass; // Trigger Run Event (if applicable) $objClass->Form_Run(); // Trigger Create Event (if applicable) $objClass->Form_Create(); } // Trigger PreRender Event (if applicable) $objClass->Form_PreRender(); // Render the Page switch ($objClass->strCallType) { case QCallType::Ajax: // Must use AJAX-based renderer $objClass->RenderAjax(); break; case QCallType::Server: case QCallType::None: case '': // Server/Postback or New Page // Make sure all controls are marked as not being on the page yet foreach ($objClass->objControlArray as $objControl) { $objControl->ResetOnPageStatus(); } // Use Standard Rendering $objClass->Render(); break; default: throw new Exception('Unknown Form CallType: ' . $objClass->strCallType); } // Ensure that RenderEnd() was called during the Render process switch ($objClass->intFormStatus) { case QFormBase::FormStatusUnrendered: throw new QCallerException('$this->RenderBegin() is never called in the HTML Include file'); case QFormBase::FormStatusRenderBegun: throw new QCallerException('$this->RenderEnd() is never called in the HTML Include file'); case QFormBase::FormStatusRenderEnded: break; default: throw new QCallerException('FormStatus is in an unknown status'); } // Tigger Exit Event (if applicable) $objClass->Form_Exit(); }
/** * This method initializes the actual layout of the form * It runs in all cases including initial form (the time when Form_Create is run) as well as on * trigger actions (QServerAction, QAjaxAction, QServerControlAction and QAjaxControlAction) * * It is responsible for implementing the logic and sequence in which page wide checks are done * such as running Form_Validate and Control validations for every control of the page and their * child controls. Checking for an existing FormState and loading them before trigerring any action * is also a responsibility of this method. * @param string $strFormClass The class of the form to create when creating a new form. * @param string|null $strAlternateHtmlFile location of the alternate HTML template file. * @param string|null $strFormId The html id to use for the form. If null, $strFormClass will be used. * * @throws QCallerException * @throws QInvalidFormStateException * @throws Exception */ public static function Run($strFormClass, $strAlternateHtmlFile = null, $strFormId = null) { // See if we can get a Form Class out of PostData $objClass = null; if ($strFormId === null) { $strFormId = $strFormClass; } if (array_key_exists('Qform__FormId', $_POST) && $_POST['Qform__FormId'] == $strFormId && array_key_exists('Qform__FormState', $_POST)) { $strPostDataState = $_POST['Qform__FormState']; if ($strPostDataState) { // We might have a valid form state -- let's see by unserializing this object $objClass = QForm::Unserialize($strPostDataState); } // If there is no QForm Class, then we have an Invalid Form State if (!$objClass) { self::InvalidFormState(); } } if ($objClass) { // Globalize global $_FORM; $_FORM = $objClass; $objClass->strCallType = $_POST['Qform__FormCallType']; $objClass->intFormStatus = QFormBase::FormStatusUnrendered; if ($objClass->strCallType == QCallType::Ajax) { QApplication::$RequestMode = QRequestMode::Ajax; } // Cleanup ajax post data if the encoding does not match, since ajax data is always utf-8 if ($objClass->strCallType == QCallType::Ajax && QApplication::$EncodingType != 'UTF-8') { foreach ($_POST as $key => $val) { if (substr($key, 0, 6) != 'Qform_') { $_POST[$key] = iconv('UTF-8', QApplication::$EncodingType, $val); } } } if (!empty($_POST['Qform__FormParameter'])) { $_POST['Qform__FormParameter'] = self::UnpackPostVar($_POST['Qform__FormParameter']); } // Decode custom post variables from server calls if (!empty($_POST['Qform__AdditionalPostVars'])) { $val = self::UnpackPostVar($_POST['Qform__AdditionalPostVars']); $_POST = array_merge($_POST, $val); } // Iterate through all the control modifications if (!empty($_POST['Qform__FormUpdates'])) { $controlUpdates = $_POST['Qform__FormUpdates']; if (is_string($controlUpdates)) { // Server post is encoded, ajax not encoded $controlUpdates = self::UnpackPostVar($controlUpdates); } if (!empty($controlUpdates)) { foreach ($controlUpdates as $strControlId => $params) { foreach ($params as $strProperty => $strValue) { switch ($strProperty) { case 'Parent': if ($strValue) { if ($strValue == $objClass->FormId) { $objClass->objControlArray[$strControlId]->SetParentControl(null); } else { $objClass->objControlArray[$strControlId]->SetParentControl($objClass->objControlArray[$strValue]); } } else { // Remove all parents $objClass->objControlArray[$strControlId]->SetParentControl(null); $objClass->objControlArray[$strControlId]->SetForm(null); $objClass->objControlArray[$strControlId] = null; unset($objClass->objControlArray[$strControlId]); } break; default: if (array_key_exists($strControlId, $objClass->objControlArray)) { $objClass->objControlArray[$strControlId]->__set($strProperty, $strValue); } break; } } } } } // Set the RenderedCheckableControlArray if (!empty($_POST['Qform__FormCheckableControls'])) { $vals = $_POST['Qform__FormCheckableControls']; if (is_string($vals)) { // Server post is encoded, ajax not encoded $vals = self::UnpackPostVar($vals); } $objClass->checkableControlValues = $vals; } else { $objClass->checkableControlValues = []; } // This is original code. In an effort to minimize changes, // we aren't going to touch the server calls for now if ($objClass->strCallType != QCallType::Ajax) { foreach ($objClass->objControlArray as $objControl) { // If they were rendered last time and are visible // (and if ServerAction, enabled), then Parse its post data if ($objControl->Visible && $objControl->Enabled && $objControl->RenderMethod) { // Call each control's ParsePostData() $objControl->ParsePostData(); } // Reset the modified/rendered flags and the validation // in ALL controls $objControl->ResetFlags(); } } else { // Ajax post. Only send data to controls specified in the post to save time. $previouslyFoundArray = array(); $controls = $_POST; $controls = array_merge($controls, $objClass->checkableControlValues); foreach ($controls as $key => $val) { if ($key == 'Qform__FormControl') { $strControlId = $val; } elseif (substr($key, 0, 6) == 'Qform_') { continue; // ignore this form data } else { $strControlId = $key; } if (($intOffset = strpos($strControlId, '_')) !== false) { // the first break is the control id $strControlId = substr($strControlId, 0, $intOffset); } if (($objControl = $objClass->GetControl($strControlId)) && !isset($previouslyFoundArray[$strControlId])) { if ($objControl->Visible && $objControl->RenderMethod) { // Call each control's ParsePostData() $objControl->ParsePostData(); } $previouslyFoundArray[$strControlId] = true; } } } // Only if our action is validating, we are going to reset the validation state of all the controls if (isset($_POST['Qform__FormControl']) && isset($objClass->objControlArray[$_POST['Qform__FormControl']])) { $objControl = $objClass->objControlArray[$_POST['Qform__FormControl']]; if ($objControl->CausesValidation) { $objClass->ResetValidationStates(); } } // Trigger Run Event (if applicable) $objClass->Form_Run(); // Trigger Load Event (if applicable) $objClass->Form_Load(); // Trigger a triggered control's Server- or Ajax- action (e.g. PHP method) here (if applicable) $objClass->TriggerActions(); } else { // We have no form state -- Create Brand New One $objClass = new $strFormClass(); // Globalize global $_FORM; $_FORM = $objClass; // Setup HTML Include File Path, based on passed-in strAlternateHtmlFile (if any) try { $objClass->HtmlIncludeFilePath = $strAlternateHtmlFile; } catch (QCallerException $objExc) { $objExc->IncrementOffset(); throw $objExc; } // By default, this form is being created NOT via a PostBack // So there is no CallType $objClass->strCallType = QCallType::None; $objClass->strFormId = $strFormId; $objClass->intFormStatus = QFormBase::FormStatusUnrendered; $objClass->objControlArray = array(); $objClass->objGroupingArray = array(); // Trigger Run Event (if applicable) $objClass->Form_Run(); // Trigger Create Event (if applicable) $objClass->Form_Create(); $objClass->Form_Initialize(); if (defined('__DESIGN_MODE__') && __DESIGN_MODE__ == 1) { // Attach custom event to dialog to handle right click menu items sent by form $dlg = new QModelConnectorEditDlg($objClass, 'qconnectoreditdlg'); $dlg->AddAction(new QOnEvent('qdesignerclick'), new QAjaxAction('ctlDesigner_Click', null, null, 'ui')); } } // Trigger PreRender Event (if applicable) $objClass->Form_PreRender(); // Render the Page switch ($objClass->strCallType) { case QCallType::Ajax: // Must use AJAX-based renderer $objClass->RenderAjax(); break; case QCallType::Server: case QCallType::None: case '': // Server/Postback or New Page // Make sure all controls are marked as not being on the page yet foreach ($objClass->objControlArray as $objControl) { $objControl->ResetOnPageStatus(); } // Use Standard Rendering $objClass->Render(); // Ensure that RenderEnd() was called during the Render process switch ($objClass->intFormStatus) { case QFormBase::FormStatusUnrendered: throw new QCallerException('$this->RenderBegin() is never called in the HTML Include file'); case QFormBase::FormStatusRenderBegun: throw new QCallerException('$this->RenderEnd() is never called in the HTML Include file'); case QFormBase::FormStatusRenderEnded: break; default: throw new QCallerException('FormStatus is in an unknown status'); } break; default: throw new Exception('Unknown Form CallType: ' . $objClass->strCallType); } // Once all the controls have been set up, and initialized, remember them. $objClass->SaveControlState(); // Tigger Exit Event (if applicable) $objClass->Form_Exit(); }