Newer
Older
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
* <Empty>: do nothing
* 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
LOAD
----
* When qfq starts,
* (Form) Looking for a formname at:
1. Typo3 Bodytext Element,
2. For the 'SIP' ($_GET['s'])
3. $_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>.[<number.>].sql statements
* Access code 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->feNative and $this->feAction.
* The Form specificaton (table form) will be evaluated direct after loading.
* The FormElement specification will be evaluated later on in BuildForm*.php
SAVE
----
* Via wrapper api/save.php
* SID must be supplied via FORM POST
* The SID supplies the <formname> and the <recordid>
* form.render: plain/table/bootstrap
* Client will handle the response of save.php.
* Optional redirection initiated by client.
DELETE
------
* Via wrapper api/delete.php
* The element who should dissappear after successfull deleting: class=record
* Button:
* class=record-delete
* Button: data-sip={{SIP}}
* Three possible variants with delete links:
* Form: main record
* HTML Code:
<button id="delete-button" type="button" class="btn btn-default navbar-btn" ><span class="glyphicon glyphicon-trash"></span></button>
* Form: subrecord, one delete button per record
* Report: typially inside a table, but maybe different.
* HTML Code:
<button type="button" class="record-delete" data-sip={{SIP}} ><span class="glyphicon glyphicon-trash"></span></button>
USER INTERFACE
==============
Button status
-------------
* Form modified:
* Buttons enabled: Save, Close, New, Delete
* Button disable: -
* Form not modified:
* Buttons enabled: Close, New, Delete
* Button disabled: Save
Save Button
-----------
* User presses the button
* Current formelements and data will be reloaded.
* Process server reponse 'redirect':
* 'client': No redirect.
* 'no': No redirect.
* 'url': Redirect to URL
* Failure: Happens on communication errors, if data validation fails, form actions fails or saving data fails.
* Show error message.
* Client: Ignore server reponse 'redirect'. Client stays on current page.
Close Button
------------
* User presses the button
* Form not modified by user: Client redirects to previous page.
* Form modified by user: Ask User 'You have unsaved changes. Do you want to close?'
* Yes: Client redirects to previous page.
* No: Client stays on current page.
* Save & Close:
* Client reset all validation states
* Client validates HTML Form
* Client submits form to server.
* Success: Process server response 'redirect':
* 'client': Client shows previous page.
* 'no': No redirect.
* 'url': Redirect to URL
* Failure: Happens on communication errors, if data validation fails, form actions fails or saving data fails.
* Show error message.
* Client: No redirect. Ignore server reponse 'redirect'.
Delete Button: Main record
--------------------------
* User presses the button. Ask User 'Do you really want to delete the record?
* Yes:
* Delete record on server.
* Process server reponse 'redirect':
* 'client': Client redirects to previous page.
* 'no': Error message.
* 'url': Redirect to URL
* No:
* Client does not delete record on server.
* No redirect. Client stays on current page.
New Button
----------
* User presses the button
* Form not modified by user: Client redirects to href url.
* Form modified by user: Ask User 'You have unsaved changes. Do you want to save first?'
* Yes:
* Client reset all validation states
* Client validates HTML Form
* Form is submitted to server
* Success:
* Client: Ignore server reponse 'redirect'. Client redirects to href url.
* Failure: Happens on communication errors, if data validation fails, form actions fails or saving data fails.
* Show error message.
* Client: Ignore server reponse 'redirect'. Client stays on current page.
* No:
* Client does not save the modified record.
* Client redirects to href url.
* Cancel:
* Client does not save the modified record.
* Client stays on current page.
File Handling: Upload
---------------------
* No previous uploaded file present
1. User presses the Browse button
1. User selects file
1. File is uploaded to qfq immediately
1. Browse button gets disabled and hidden
1. File delete button is shown
1. User cancels file selection
1. no action
* Previous uploaded file present
1. User deletes file
1. File delete button gets disabled and hidden
1. Browse button gets enabled and displayed
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
Form Build (load)
.................
* The upload functionality consist of three elements
* 1) A <div> tag with a) an optional filename of an earlier uploaded file plus and b) a trash Button.
* 2) The 'browse' button (<input type='file' name='_upload_<feName>'>). This element will not be send by post.
* 3) A HTML hidden element with name=<feName> containing the <sipUpload>.
* A new uniq SIP (sipUpload) will be created for every upload formElement. These 'sipUpload' will be assigned to the browse button and to the delete button.
* The individual sipUpload is necessary to correctly handle multiple simustaenously forms when using r=0. Also, through this uniq id it's easy to distinguish between asynchron uploaded files.
* The SIP contains the '_FILES' information submitted during the upload.
* Via the hidden element <feName> 'save()' access the form individual upload status informations.
Upload to server, before 'save'
...............................
* 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 during the upload time.
* On success 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 $_SESSION['X'][<uploadSip>][FILES_TMP_NAME] to $_SESSION['X'][<uploadSip>][FILES_TMP_NAME].cached
* The upload action will be saved in the user session.
* $_SESSION['X'][<uploadSip>][FILES_TMP_NAME|FILES_NAME|FILES_ERROR|FILE_SIZE]
* Clicks the user on delete button.
* In the usersession a flagDelete will be set: $_SESSION['X'][<uploadSip>]['flagDelete']='1'
* An optional previous upload file (still not saved on the final place) will be deleted.
* An optional existing variable $_SESSION['X'][<uploadSip>][FILES_TMP_NAME] will be deleted. The 'flagDelete' must not be change - it's later needed to detect to delete earlier uploaded files.
Form save
.........
* Before building the insert/update, process all 'uploads'.
* Get every uniq sipUpload to every upload formElement. Get the corresponding temporary uploaded filename.
* If $_SESSION['X'][<uploadSip>]['flagDelete']='1' is set, delete prefious uploaded file.
* Calculate <destination>
* mv <file>.cached <destination>
* clientvalue[<feName>] = <destination>
* delete $_SESSION['X'][<uploadSip>]
Formelement type: DATE / DATETIME / TIME
----------------------------------------
* 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 wirde 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'
Debug / Log / Errormessages
===========================
* Before firing a SQL or doing processing of an FormElement, set some debugging / error variables:

Carsten Rose
committed
[src] $this->store->setVar(SYSTEM_SQL_RAW, STORE_SYSTEM)

Carsten Rose
committed
<code>
SYSTEM_SQL_RAW
SYSTEM_SQL_FINAL
SYSTEM_SQL_COUNT
SYSTEM_SQL_PARAM_ARRAY
SYSTEM_FORM = CLIENT_FORM; // '<formName> / <formId>'
SYSTEM_FORM_ELEMENT = 'formElement'; // '<formElementName> / <formElementeId>'
SYSTEM_FORM_ELEMENT_COLUMN = 'formElementColumn'; // '<columnname of current processed formElement>'
</code>
* Form.debugShowInfo: yes|no will display a tooltip near beside every formelement and show parse/evaluate as tooltip.

Carsten Rose
committed
* Check to display debug info:
$this->store->getVar(SYSTEM_SHOW_DEBUG_INFO, STORE_SYSTEM) === 'yes'
* Retrieve 'get' or 'post' variables by:
[src] $values = $this->store->getStore(STORE_CLIENT)
Primary Table
=============
* 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
[src] array_keys($this->getStore(STORE_TABLE_COLUMN_TYPES))
* 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
* http://api.typo3.org/
* http://api.typo3.org/typo3cms/62/html/index.html
* https://docs.typo3.org/typo3cms/CoreApiReference/Introduction/Index.html
SIP
===
Page loaded: www.example.com?index.php&id=start&s=badcaffee1234&type=2&L=3, with $_SESSION['badcaffee1234'] => 'form=Person&r=1'
* $_SESSION[$sip] => <urlparam> >> $_SESSION['badcaffee1234'] => 'form=Person&r=1'
* $_SESSION[$urlparam] => <sip> >> $_SESSION['form=Person&r=1'] => 'badcaffee1234'
FormElement
===========
Checkbox
--------
<div class="checkbox">
<label>
<input type="checkbox">label 1
</label>