-
Carsten Rose authoredCarsten Rose authored
Design / Notes / Best Practices for Coding
General
- Class QuickFormQuery is the main entry point called by:
- T3 Extension 'QFQ': called once per tt_content-record. 'bodytext' will be transferred to class QuickFormQuery.
- The 'bodytext' contains:
- Report definiton: 10.sql=SELECT ...
- Form definition (explizit): form=Person
- : do nothing
- The 'bodytext' contains:
- api/save.php: wrapper to receive AJAX post request and instantiate QuickFormQuery.
- api/load.php: not implemented yet.
- Wrapper to receive AJAX get requests.
- delivers data for jqw grid
- delivers data for typeahed fields
- delivers data for select list
- delivers data for depended (user select/unselect former elements) form elements
- T3 Extension 'QFQ': called once per tt_content-record. 'bodytext' will be transferred to class QuickFormQuery.
LOAD
-
When qfq starts,
- (Form) Looking for a formname at:
- Typo3 Bodytext Element,
- For the 'SIP' ($_GET['s'] => _GET['s']]="form=person&r=123")
- $_GET variables 'form' and 'r' (=recordId) - the parameter 'form' has to be allowed in 'Permit URL Parameter' of the specified form. This means: load the form to check, if it is allowed to load the form!?
- If a formname is found, the search stops and the specified form will be processed.
- (Report)
- Process all .[<number.>].sql statements
- (Form) Looking for a formname at:
-
Access variables:
- active/valid formname: [$this->store->setVar(SYSTEM_FORM, $formName, STORE_SYSTEM);]
- SIP: [$this->store->getVar('form', STORE_SIP)]
- All parameters from active SIP: [$this->store->getStore(STORE_SIP)]
- Check Contstants.php for known Store members.
-
In QuickFormQuery.php the whole Form will be copied to
$this->formSpec
and depending on further processing, the elements are available in$this->feSpecNative
and$this->feSpecAction
.- The Form specificaton (table form) will be evaluated direct after loading (no
dynamicUpdate
). - The FormElement specification will be evaluated later in BuildForm*.php:
- $formSpec, and root elements of $feSpecAction & $feSpecNative will be read in QuickFormQuery.php.
- AbstractBuildForm.php/BuildFormBootstrap.php receives a copy during instantiating of that class.
- Processing of FormElements in AbstractBuildForm.php/BuildFormBootstrap.php typically do not change values, especially there is no evaluation, in $feSpecAction & $feSpecNative.
- Dynamic Update: remember, an update(=Form load) is a complete new rendering of the form in that moment. All values/elements/notes/debug are fresh with the latest form content.
- The Form specificaton (table form) will be evaluated direct after loading (no
-
If a form is called without a SIP (form.permitNew='always'), than a SIP is created on the fly (as a parameter in the form).
-
Uniq SIP for mutliple tabs with r=0
- Depending on
r=0
orr>0
a form submit will do an MySQLinsert
orupdate
later during save. - For new records (r=0), clicking on 'save' without closing the form is a tricky situation. Additionally the user might have open multiple tabs (same form, all r=0) and after saving the record (wihtout closing the form) the user expects that it's ok to edit the record again and again. Unfortunately, the initial created SIP (before 'form load') is not uniqe anymore (multiple tabs might contain a saved 'new record'). To guarantee correct saving of r=0 records, a unique on the fly generated SIP is creatd during form load - individually per browser tab.
- Depending on
-
Faking the STORE_TYPO3 for API calls:
- The PHP code api/save.php, api/load.php is called directly, without any TYPO3 Framework. Therefore the Typo3 information 'pageId', 'feUser*', 'beUser*', 'ttContentUid', ... is not available.
- Form load: an additional hidden Formelement '_sipForTypo3Vars' will be created with a subset of the current STORE_TYPO3 values. The workaround with the SIP is useful, cause the same form can be shown on different places (QFQ records) - this is not very likely, but might happen. The 'on the fly rendered' SIP helps to deliver the status. AbstractBuildForm.php: process() > prepareT3VarsForSave() > Store.php: copyT3VarsToSip();
- Form save: FillStoreForm.php: process() > Store: fillTypo3StoreFromSip()
-
Store: STORE_ADDITIONAL_FORM_ELEMENTS
- HTML 'hidden' elements, inside of a checkbox or radio input definition, might disturb Bootstrap CSS classes.
- Therefore HTML elements like 'hidden' can be collected in STORE_ADDITIONAL_FORM_ELEMENTS.
- When the form will be composed of all single parts, the STORE_ADDITIONAL_FORM_ELEMENTS content will be arranged before the regular INPUT Elements.
-
Formular zusammenbauen
-
QuickFormQuery: doForm > loadFormSpecification - laedt den Form Record alle Form Elemente die nicht genested sind: native, pill, fieldset, templateGroup >> $his->formNative.
-
In
$this->feSpecNative
die Parameter Values -
Damit wird '(BuildFormBootstrap / AbstractBuildForm) > process()' aufgerufen.
-
Hier wird AbstractBuildForm->elements() aufgerufen (ein Aufruf fuer alle root elemente).
-
Pro native/container Element (inkl. pill, fieldset, templateGroup) wird $builElementFunctionName aufgerufen.
- buildText()
- ....
- buildFieldSet() << von hier werden alle zum aktuellen 'FieldSet' gehoerenden SubElemente abgearbeitet - via AbstractBuildForm->elements() (damit schliesst sich der Kreis und wird rekursiv)
- buildPill() << von hier werden alle zum aktuellen 'Pill' gehoerenden SubElemente abgearbeitet - via AbstractBuildForm->elements() (damit schliesst sich der Kreis und wird rekursiv)
- buildTemplateGroup() << von hier werden alle zum aktuellen 'Pill' gehoerenden SubElemente abgearbeitet - via AbstractBuildForm->elements() (damit schliesst sich der Kreis und wird rekursiv)
-
Pro Container Element werden alle zugeordneten native/container elemente aufgerufen (beliebig tiefe Verschachtelung).
-
-
SAVE
- Via wrapper api/save.php
- SID must be supplied via FORM POST
- The SID supplies the and the
- form.render: plain/table/bootstrap
- Client will handle the response of save.php.
- Optional redirection initiated by client.
New records ...........
- r=0 (missing 'r' means r=0)
- After saving the SIP content will be updated with the new record. Remember that the SIP in the URL is not the SIP used in the form to identify the form/record. The form use a individual 'new record' SIP.
Existing records ................
- r>0 ('r' have to exist)
DELETE
-
Via wrapper api/delete.php
-
The element who should dissappear after successfull deleting: class=record
-
Button:
-
class=record-delete
-
Button: data-sip={{SIP}}
-
SIP values:
- SIP_RECORD_ID: Mandatory.
- SIP_TABLE: Either SIP_TABLE or SIP_FORM has to be given.
- SIP_FORM: Either SIP_TABLE or SIP_FORM has to be given. Not implemented now.
- SIP_TARGET_URL: Only with SIP_MODE_ANSWER=MODE_HTML - Url to redirect browser to.
- SIP_MODE_ANSWER: MODE_JSON / MODE_HTML. If not given, this means MODE_JSON.
-
-
Three possible variants with delete links:
-
(1) Form: main record
-
HTML Code:
-
(2) Form: subrecord, one delete button per record
-
HTML Code:
-
(3) Report: typially inside a table, but maybe different.
-
Upload
- The upload UI consist of three elements
-
- A tag with a) an optional filename of an earlier uploaded file and b) a trash button.
-
- The 'browse' button (). This element will not be send by post.
-
- A HTML hidden element with name= containing the .
- A new uniq SIP (sipUpload) will be created for every upload formElement. These 'sipUpload' will be assigned to the upload
browse button and to the upload delete button.
- The individual sipUpload is necessary to correctly handle multiple simultaenously forms when using r=0. Also, through this uniq id it's easy to distinguish between asynchron uploaded files.
- The SIP on the server contains the individual '_FILES' information submitted during the upload.
- Via the hidden element 'save()', access to the form individual upload status informations is given.
- If a user open's a file for upload via the browse button, that file is immediately transmitted to the server. The user will see a turning wheel until the upload finished.
- After successfull upload the 'Browse' button disappears and the filename, plus the delete button, will be displayed (client logic).
- The uploaded file will be checked: maxsize, mime type, check script.
- The uploaded file is still temporary. It has been renamed from '[STORE_EXTRA][][FILES_TMP_NAME]' to
'[STORE_EXTRA][][FILES_TMP_NAME].cached'. - The upload action will be saved in the user session: [STORE_EXTRA][][FILES_TMP_NAME] [STORE_EXTRA][][FILES_NAME] [STORE_EXTRA][][FILES_ERROR] [STORE_EXTRA][][FILE_SIZE]
- Clicks the user on the delete button:
- In the usersession a flagDelete will be set: [STORE_EXTRA][][FILES_FLAG_DELETE]='1'
- An optional previous upload file (still not saved on the final place) will be deleted.
- An optional existing variable [STORE_EXTRA][][FILES_TMP_NAME] will be deleted. The 'flagDelete' must not be change - it's later needed to detect to delete, of earlier uploaded files.
- Step 1: insert / update the record.
- Step 2: process all 'uploads'.
- Get every uniq uploadSip (=[STORE_CLIENT][]) of every upload formElement. Get the corresponding temporary uploaded filename.
- If [STORE_EXTRA][][FILES_FLAG_DELETE]='1' is set, delete previous uploaded file.
- If [STORE_EXTRA][][FILES_TMP_NAME]!='': indicates that there is an upload.
- Calculate
- mv .cached
- clientvalue[] =
- delete [STORE_EXTRA][]
- Step 3: update record with final
-
Available Formats:
- 'yyyy-mm-dd' = FORMAT_DATE_INTERNATIONAL.
- 'dd.mm.yyyy' = FORMAT_DATE_GERMAN.
-
The 'DATE_FORMAT' can be specified systemwide in
config.ini
-
The default format is FORMAT_DATE_INTERNATIONAL.
-
Optional: 'dateFormat' can be specified per form element in
form.parameter
- this overwrites 'systemwide'. -
If there is no placeholder defined on the form element, the defined dateFormat is shown as placeholder.
-
Browser:
- checks the input with a system regexp.
- regexp might be user defined. If given, do not use system regexp!
- No min/max check.
-
Server:
- check with system wide regexp
- regexp might be user defined. If given, do not use system regexp!
- Do min/max check.
-
MySQL data: 1000-01-01 - 9999-12-31 and 0000-00-00
-
MySQL time: 00:00:00 - 23:59:59
-
datetime format: 'DATE TIME'
-
Before firing a SQL or doing processing of an FormElement, set some debugging / error variables:
[src] $this->store->setVar(SYSTEM_SQL_RAW, STORE_SYSTEM)
-
Available fields:
SYSTEM_SQL_RAW SYSTEM_SQL_FINAL SYSTEM_SQL_COUNT SYSTEM_SQL_PARAM_ARRAY SYSTEM_FORM = CLIENT_FORM; // ' / ' SYSTEM_FORM_ELEMENT = 'formElement'; // ' / ' SYSTEM_FORM_ELEMENT_COLUMN = 'formElementColumn'; // ''
-
Form.debugShowInfo: yes|no will display a tooltip near beside every formelement and show parse/evaluate as tooltip.
-
Check to display debug info:
$this->store->getVar(SYSTEM_SHOW_DEBUG_INFO, STORE_SYSTEM) === 'yes'
-
The formLog button (top of FormEditor) open's the same page with SIP parameter:
- form=
- _formLogMode=logAll|logSession
-
If a form is opened with '_formLogMode' SIP parameter, the form is not shown.
-
Instead:
- An empty formLogFile ist created
- Filename: $this->formSpec[formLogFileSession|formLogFileAll]='fileadmin/protected/log/form..log' (all) or form..log is set.
- A HTML element with htmlid='formLog-1' is rendered together with the AJAX monitor code to the current logFile.
-
If a form is instantiated (load, save, udpate, delete), the system checks if corresponding lock files exist.
- Correspondig expired logfiles will be deleted.
- Active logfiles will be filled.
-
Exception types:
- Code
- Db
- User Form
- user Report
- plus an Errorhandler which throws exceptions
-
Exceptions inside of an API call delivers the error code and msg as JSON to the client.
-
Typo3 suppress E_NOTICE (e.g. undefined index). To catch E_NOTICE in QFQ, it will be temporaly enabled in QfqCongroller.php.
- Retrieve 'get' or 'post' variables by:
- For the primary table all informations are available in STORE_TABLE_DEFAULT and STORE_TABLE_COLUMN_TYPES.
- Get all columns of the primary table by
- Get the recent record in STORE_RECORD and the parent record (multiforms) in STORE_PARENT_RECORD.
-
PageId: $this->store->getVar(TYPO3_PAGE_ID, STORE_TYPO3)
- Based on: $GLOBALS["TSFE"]->id current Page
-
$GLOBALS["TSFE"]->fe_user->user["uid"] fe_user_uid
-
https://wiki.typo3.org/Extbase_HowTos
- Old: $this->cObj->data['bodytext']
- New: $contentObject = $this->configurationManager->getContentObject(); $configuration = $contentObject->data['bodytext'];
-
Verzeichnisstruktur Extension: https://docs.typo3.org/typo3cms/CoreApiReference/ExtensionArchitecture/FilesAndLocations/Index.html
-
https://docs.typo3.org/typo3cms/CoreApiReference/Introduction/Index.html
-
sip] => >> $_SESSION['qfq']['badcaffee1234'] => 'form=Person&r=1'
-
urlparam] => >> $_SESSION['qfq']['form=Person&r=1'] => 'badcaffee1234'
Upload to server, before 'save' ...............................
Form save .........
See PROTOCOL.md > Download
Shows files instantly in the browser. Via AJAX request a user defined HTML element will be replaced by the content of the file. 'frequency' and mode 'replace' / 'append' can be configured. Only files in document root are acessible.
tbd
On request (by user in FormEditor) a 'formLog' mode can be activated per individual form. The mode will be disabled after FORM_LOG_EXPIRE (typically 1800 seconds).
[src] $values = $this->store->getStore(STORE_CLIENT)
[src] array_keys($this->getStore(STORE_TABLE_COLUMN_TYPES))
Page loaded: www.example.com?index.php&id=start&s=badcaffee1234&type=2&L=3, with $_SESSION['badcaffee1234'] => 'form=Person&r=1'
<div class="checkbox"> <label> <input type="checkbox">label 1 </label> </div>
- A
-