Commit 635a37be authored by Carsten  Rose's avatar Carsten Rose
Browse files

subrecord: Unittest implementd plus some smaller fixes in rendering.

AbstractBuildForm.php: Columns with a name starting with '_... ' has not been surpressed. Fixed. Icons path adjusted.
parent cd9c4fda
......@@ -809,32 +809,30 @@ Type: select
Type: subrecord
^^^^^^^^^^^^^^^
'subrecord' present a list of records (called secondary records), typically to edit, delete or add such records. The list
is defined as a SQL query. The number of records shown is not limited. These formelement will be rendered inside the
form as a HTML table. The list is ordered by 1) formelement.class (action, container, native), 2) formelement.container,
3) formelement.ord
The FormElement type 'subrecord' renders a list of records (so called secondary records), typically to show, edit, delete
or add new records. The list is defined as a SQL query. The number of records shown is not limited. These FormElement
will be rendered inside the form as a HTML table.
* *sql1*: SQL query to select records. E.g.::
{{!SELECT a.id AS id, CONCAT(a.street, a.streetnumber) AS a, a.city AS b, a.zip AS c FROM Address AS a}}
# Notice the **exclamation mark** after '{{' - this is necessary to return an array of elements, instead of a single string.
* Notice the **exclamation mark** after '{{' - this is necessary to return an array of elements, instead of a single string.
* Exactly one column **'id'** has to exist; it specifies the primary record for the target form.
In case the id should no be visible to the user, it has to be named **'_id'**.
* Columnname: *[title=]<title>[|<number>][|width=<number>][|nostrip][|icon][|url][|mailto][|_rowClass][|_rowTitle]*
* Exactly one column 'id' has to exist; it specifies the primary record for the target form.
In case the id should no be visible to the user, it has to be named *'_id'*.
* Columnname: *<title>[|<number>][|width=<number>][|nostrip][|icon][|url][|mailto|_rowClass]*
* *<number>*: any 'digit only' will be treated as '''width'''.
* *width=<number>*: max. number of chars displayed per cell in the column.
* All but the first parameter are position independet.
* *<number>*: Max. number of chars displayed per cell in the column. Any 'digit only' will be treated as '''width'''.
Default max width: 20.
* *width=<number>*: identical with <number>.
* *nostrip*: by default, html tags will be stripped off the cell content before rendering. This protects the table
layout. 'nostrip' deactivates the cleaning to make links, images, ... possible.
* *icon*: the cell value contains the name of an icon in *typo3conf/ext/qfq/Resources/Public/icons*. Empty cell values
will omit an html image tag (=nothing renderd in the cell).
will omit an html image tag (=nothing rendered in the cell).
* *mailto*: value will be rendered as a mailto link.
* *url*: value will be rendered as a link.
* *title=<text>* or '<none of the above>': column '''title'''.
* The parameters are position independet.
* Examples::
SELECT note1 AS 'Comment', note2 AS 'Comment\|50' , note3 AS 'title=Comment\|width=100\|nostrip', note4 AS '50\|Comment',
......@@ -867,11 +865,11 @@ Type: subrecord
* Example: *detail=id:personId,&12:xId,&{{a}}:personId*
* By default, the given value will overwrite values on the target record. In most situations, this is the wished behaviour.
* Exceptions of the default behaviour have to be defined on the target form in the corresponding formelement in the
field *value*. E.g. `{{<columnname>:RS0}}` - For existing records, the store `R` will provide a value. For new
records, store `R` is empty and store S will be searched for a value: the value defined in `detail` will be choosen. At
last the store '0' is defined as a fallback.
* *source table column name*: E.g. A person form is opened with person.id=5 (r=5). The definition `detail=id:personId` and `form=address`
maps person.id to address.personId. On the target record, the column personId becomes '5'.
field *value* by changing the default Store priority definition. E.g. `{{<columnname>:RS0}}` - For existing records,
the store `R` will provide a value. For new records, store `R` is empty and store S will be searched for a value:
the value defined in `detail` will be choosen. At last the store '0' is defined as a fallback.
* *source table column name*: E.g. A person form is opened with person.id=5 (r=5). The definition `detail=id:personId`
and `form=address` maps person.id to address.personId. On the target record, the column personId becomes '5'.
* *Constant '&'*: Indicate a 'constant' value. E.g. `&12:xId` or `{{...}}` (all possibilities, incl. further SELECT
statements) might be used.
......@@ -2566,19 +2564,18 @@ Best Practise: Chart
====================
* QFQ delivers a chart JavaScript lib: https://github.com/nnnick/Chart.js.git. Docs: http://www.chartjs.org/docs/
* The library is not sourced in the HTML page by automatically. To do it, either include the lib
* The library is not sourced in the HTML page automatically. To do it, either include the lib
`typo3conf/ext/qfq/Resources/Public/JavaScript/Chart.min.js`:
* in the specific tt_content record (shown below in the example) or
* system wide via Typo3 Template record.
* By splitting HTML and JavaScript code over several lines, take care not accidently to create a 'nesting'-end token. Check
the line after `10.tail =`. It's '}' alone on one line. This is a valid 'nesting'-end token!. There are two options to
circumwait this:
* By splitting HTML and JavaScript code over several lines, take care not accidently to create a 'nesting'-end token.
Check the line after `10.tail =`. It's '}' alone on one line. This is a valid 'nesting'-end token!. There are two options
to circumvent this:
* Do not nest the HTML & JavaScript code - this is not human readable.
* Select '<' / '>' as token (check the first line).
* Don't nest the HTML & JavaScript code - bad workaround, this is not human readable.
* Select different nesting token, e.g. '<' / '>' (check the first line on the following example).
::
......
......@@ -1352,7 +1352,7 @@ abstract class AbstractBuildForm {
}
/**
* Constuct a HTML table of the subrecord data.
* Construct a HTML table of the subrecord data.
* Column syntax definition: https://wikiit.math.uzh.ch/it/projekt/qfq/qfq-jqwidgets/Documentation#Type:_subrecord
*
* @param array $formElement
......@@ -1405,6 +1405,7 @@ abstract class AbstractBuildForm {
if ($flagDelete)
$columns .= '<th></th>';
// Table head
$html = Support::wrapTag('<tr>', $columns);
foreach ($formElement['sql1'] as $row) {
......@@ -1592,6 +1593,11 @@ abstract class AbstractBuildForm {
break;
}
// Don't render Columns starting with '_...'.
if (substr($columnName, 0, 1) === '_') {
continue;
}
$flagWidthLimit = true;
$control['width'][$columnName] = SUBRECORD_COLUMN_WIDTH;
......@@ -1664,7 +1670,7 @@ abstract class AbstractBuildForm {
$cell = substr($cell, 0, $control['width'][$columnName]);
if (isset($control['icon'][$columnName])) {
$cell = ($cell === '') ? '' : "<image src='fileadmin/icons/$cell'>";
$cell = ($cell === '') ? '' : "<image src='" . PATH_ICONS . "/$cell'>";
}
if (isset($control['mailto'][$columnName])) {
......
......@@ -58,7 +58,8 @@ define("DFLT_UPLOAD_BASE_DIR", "fileadmin");
define("DFLT_UPLOAD_TMP_DIR", "fileadmin/tempfiles");
define("DFLT_UPLOAD_TMP_TTL", "300");
define("PATH_ICONS", "typo3conf/ext/qfq/Resources/Public/icons/");
//define("PATH_ICONS", "typo3conf/ext/qfq/Resources/Public/icons/");
const PATH_ICONS = 'typo3conf/ext/qfq/Resources/Public/icons';
// Definitions to allow successfull include of ext_localconf.
//define( 'TYPO3_MODE', '1' );
//define( 'FORMREPORT', '1' );
......
......@@ -1019,7 +1019,7 @@ EOF;
$vars[NAME_ALT_TEXT] = "Bullet " . $value;
}
$vars[NAME_IMAGE] = PATH_ICONS . "bullet-" . $value . '.gif';
$vars[NAME_IMAGE] = PATH_ICONS . "/bullet-" . $value . '.gif';
$vars[NAME_IMAGE_TITLE] = $value;
$vars[NAME_LINK_CLASS_DEFAULT] = NO_CLASS;
......@@ -1042,7 +1042,7 @@ EOF;
$vars[NAME_ALT_TEXT] = "Checked " . $value;
}
$vars[NAME_IMAGE] = PATH_ICONS . "checked-" . $value . '.gif';
$vars[NAME_IMAGE] = PATH_ICONS . "/checked-" . $value . '.gif';
$vars[NAME_IMAGE_TITLE] = $value;
$vars[NAME_LINK_CLASS_DEFAULT] = NO_CLASS;
......
......@@ -15,7 +15,9 @@ require_once(__DIR__ . '/../../qfq/store/Store.php');
*/
class BuildFormPlainTest extends AbstractDatabaseTest {
/**
*
*/
public function testGetProcessFilter() {
$build = new \qfq\BuildFormPlain(array(), array(), array());
......@@ -37,6 +39,9 @@ class BuildFormPlainTest extends AbstractDatabaseTest {
}
/**
*
*/
public function testWrapItem() {
$build = new \qfq\BuildFormPlain(array(), array(), array());
......@@ -53,6 +58,9 @@ class BuildFormPlainTest extends AbstractDatabaseTest {
$this->assertEquals('', $result);
}
/**
*
*/
public function testBuildLabel() {
$build = new \qfq\BuildFormPlain(array(), array(), array());
......@@ -60,46 +68,18 @@ class BuildFormPlainTest extends AbstractDatabaseTest {
$this->assertEquals('<label for="myLabel:123" class="control-label" >Hello World</label>', $result);
}
/**
*
*/
public function testBuildInput() {
$form = array();
$formElement = array();
$json = array();
$this->setFormFormElement($form, $formElement);
$this->templateFormNFormElement($form, $formElement);
$build = new \qfq\BuildFormPlain($form, array(), [$formElement]);
// $formElement = $this->db->sql("SELECT * FROM FormElement AS fe WHERE fe.id=114", ROW_EXACT_1);
// $this->assertEquals('', $formElement);
// $formElement = [
// 'id' => 123,
// 'formId' => 2,
// 'feIdContainer' => 0,
// 'enabled' => 'yes',
// 'name' => 'name',
// 'label' => 'Name',
// 'mode' => 'show',
// 'class' => 'native',
// 'type' => 'input',
// 'value' => '',
// 'sql1' => '',
// 'parameter' => '',
// 'debug' => 'no',
// 'deleted' => 'no',
//
// 'size' => '',
// 'maxLength' => '',
// 'tooltip' => '',
// 'placeholder' => '',
// 'checkType' => '',
// 'checkPattern' => '',
//
// 'tabindex' => 0
// ];
$result = $build->buildInput($formElement, 'name:1', '', $json);
$this->assertEquals('<input name="name:1" class="form-control" type="input" maxlength="255" value="" data-hidden="no" data-disabled="no" data-required="no" ><div class="help-block with-errors"></div>', $result);
$this->assertEquals([FE_MODE_HIDDEN => '', 'disabled' => false, FE_MODE_REQUIRED => '', 'form-element' => 'name:1', 'value' => ''], $json);
......@@ -191,7 +171,10 @@ class BuildFormPlainTest extends AbstractDatabaseTest {
$this->assertEquals([FE_MODE_HIDDEN => '', 'disabled' => false, FE_MODE_REQUIRED => '', 'form-element' => 'name:1', 'value' => 'Hello World', 'disabled' => false], $json);
}
private function setFormFormElement(array &$form, array &$formElement) {
/**
*
*/
private function templateFormNFormElement(array &$form, array &$formElement) {
$form = [
'id' => '1',
'name' => 'form',
......@@ -252,7 +235,7 @@ class BuildFormPlainTest extends AbstractDatabaseTest {
$formElement = array();
$json = array();
$this->setFormFormElement($form, $formElement);
$this->templateFormNFormElement($form, $formElement);
$build = new \qfq\BuildFormPlain($form, array(), [$formElement]);
......@@ -261,11 +244,14 @@ class BuildFormPlainTest extends AbstractDatabaseTest {
$result = $build->buildInput($formElement, 'name:1', '', $json);
}
/**
*
*/
public function testGetKeyValueListFromSqlEnumSpec() {
$form = array();
$formElement = array();
$this->setFormFormElement($form, $formElement);
$this->templateFormNFormElement($form, $formElement);
$formElement['name'] = 'deleted';
$build = new \qfq\BuildFormPlain($form, array(), [$formElement]);
......@@ -370,7 +356,7 @@ class BuildFormPlainTest extends AbstractDatabaseTest {
$form = array();
$formElement = array();
$this->setFormFormElement($form, $formElement);
$this->templateFormNFormElement($form, $formElement);
$build = new \qfq\BuildFormPlain($form, array(), [$formElement]);
......@@ -378,6 +364,109 @@ class BuildFormPlainTest extends AbstractDatabaseTest {
$build->getKeyValueListFromSqlEnumSpec($formElement, $keys, $values);
}
/**
*
*/
public function testBuildSubrecord() {
$form = array();
$formElement = array();
$json = array();
$this->templateFormNFormElement($form, $formElement);
// CheckType
$build = new \qfq\BuildFormPlain($form, array(), [$formElement]);
// id: 1, firstName: John, name: Doe
$formElement['sql1'] = $this->db->sql('SELECT id, name, firstName FROM Person ORDER BY id LIMIT 2');
$result = $build->buildSubrecord($formElement, 'name:1', '', $json);
$this->assertEquals('<table class="table table-hover"><tr><th>id</th><th>name</th><th>firstName</th></tr><tr class="record" ><td>1</td><td>Doe</td><td>John</td></tr><tr class="record" ><td>2</td><td>Smith</td><td>Jane</td></tr></table>', $result);
// _id: 1, name: Doe,
$formElement['sql1'] = $this->db->sql('SELECT id AS "_id", name FROM Person ORDER BY id LIMIT 2');
$result = $build->buildSubrecord($formElement, 'name:1', '', $json);
$this->assertEquals('<table class="table table-hover"><tr><th>name</th></tr><tr class="record" ><td>Doe</td></tr><tr class="record" ><td>Smith</td></tr></table>', $result);
// _id: 1, name: Doe, title: PERSON
$formElement['sql1'] = $this->db->sql('SELECT id AS "_id", name AS "PERSON" FROM Person ORDER BY id LIMIT 2');
$result = $build->buildSubrecord($formElement, 'name:1', '', $json);
$this->assertEquals('<table class="table table-hover"><tr><th>PERSON</th></tr><tr class="record" ><td>Doe</td></tr><tr class="record" ><td>Smith</td></tr></table>', $result);
// _id: 1, "This is a much longer text than necessary": Default max:20
$formElement['sql1'] = $this->db->sql('SELECT id AS "_id", "This is a much longer text than necessary" FROM Person ORDER BY id LIMIT 1');
$result = $build->buildSubrecord($formElement, 'name:1', '', $json);
$this->assertEquals('<table class="table table-hover"><tr><th>This is a much longe</th></tr><tr class="record" ><td>This is a much longe</td></tr></table>', $result);
// _id: 1, name: Jo (width:2)
$formElement['sql1'] = $this->db->sql('SELECT id AS "_id", name AS "2" FROM Person ORDER BY id LIMIT 2');
$result = $build->buildSubrecord($formElement, 'name:1', '', $json);
$this->assertEquals('<table class="table table-hover"><tr><th></th></tr><tr class="record" ><td>Do</td></tr><tr class="record" ><td>Sm</td></tr></table>', $result);
// _id: 1, name: Jo (width:2)
$formElement['sql1'] = $this->db->sql('SELECT id AS "_id", name AS "2|PERSON" FROM Person ORDER BY id LIMIT 2');
$result = $build->buildSubrecord($formElement, 'name:1', '', $json);
$this->assertEquals('<table class="table table-hover"><tr><th>PE</th></tr><tr class="record" ><td>Do</td></tr><tr class="record" ><td>Sm</td></tr></table>', $result);
// _id: 1, name: Doe ('width':3)
$formElement['sql1'] = $this->db->sql('SELECT id AS "_id", name AS "Name|width=3" FROM Person ORDER BY id LIMIT 2');
$result = $build->buildSubrecord($formElement, 'name:1', '', $json);
$this->assertEquals('<table class="table table-hover"><tr><th>Nam</th></tr><tr class="record" ><td>Doe</td></tr><tr class="record" ><td>Smi</td></tr></table>', $result);
// _id: 1, name: Doe (width:3, title:PERSON)
$formElement['sql1'] = $this->db->sql('SELECT id AS "_id", name AS "3|title=PERSON" FROM Person ORDER BY id LIMIT 2');
$result = $build->buildSubrecord($formElement, 'name:1', '', $json);
$this->assertEquals('<table class="table table-hover"><tr><th>PER</th></tr><tr class="record" ><td>Doe</td></tr><tr class="record" ><td>Smi</td></tr></table>', $result);
// _id: 1, name: <b>Doe</b>
$formElement['sql1'] = $this->db->sql('SELECT id AS "_id", CONCAT("<b>", name, "</b>") AS "Name" FROM Person ORDER BY id LIMIT 2');
$result = $build->buildSubrecord($formElement, 'name:1', '', $json);
$this->assertEquals('<table class="table table-hover"><tr><th>Name</th></tr><tr class="record" ><td>Doe</td></tr><tr class="record" ><td>Smith</td></tr></table>', $result);
// _id: 1, name: <b>Doe</b> , nostrip
$formElement['sql1'] = $this->db->sql('SELECT id AS "_id", CONCAT("<b>", name, "</b>") AS "Name|nostrip" FROM Person ORDER BY id LIMIT 2');
$result = $build->buildSubrecord($formElement, 'name:1', '', $json);
$this->assertEquals('<table class="table table-hover"><tr><th>Name</th></tr><tr class="record" ><td><b>Doe</b></td></tr><tr class="record" ><td><b>Smith</b></td></tr></table>', $result);
// _id: 1, icon: bullet-green.gif
$formElement['sql1'] = $this->db->sql('SELECT id AS "_id", "bullet-green.gif" AS "Status|icon" FROM Person ORDER BY id LIMIT 2');
$result = $build->buildSubrecord($formElement, 'name:1', '', $json);
$this->assertEquals('<table class="table table-hover"><tr><th>Status</th></tr><tr class="record" ><td><image src=\'typo3conf/ext/qfq/Resources/Public/icons/bullet-green.gif\'></td></tr><tr class="record" ><td><image src=\'typo3conf/ext/qfq/Resources/Public/icons/bullet-green.gif\'></td></tr></table>', $result);
// _id: 1, mailto: john@doe.com
$formElement['sql1'] = $this->db->sql('SELECT id AS "_id", "john@doe.com" AS "EMail|mailto" FROM Person ORDER BY id LIMIT 2');
$result = $build->buildSubrecord($formElement, 'name:1', '', $json);
$this->assertEquals('<table class="table table-hover"><tr><th>EMail</th></tr><tr class="record" ><td><a href="mailto:john@doe.com" >john@doe.com</a></td></tr><tr class="record" ><td><a href="mailto:john@doe.com" >john@doe.com</a></td></tr></table>', $result);
// _id: 1, url: www.uzh.ch
$formElement['sql1'] = $this->db->sql('SELECT id AS "_id", "www.uzh.ch" AS "URL|url" FROM Person ORDER BY id LIMIT 2');
$result = $build->buildSubrecord($formElement, 'name:1', '', $json);
$this->assertEquals('<table class="table table-hover"><tr><th>URL</th></tr><tr class="record" ><td><a href="www.uzh.ch" >www.uzh.ch</a></td></tr><tr class="record" ><td><a href="www.uzh.ch" >www.uzh.ch</a></td></tr></table>', $result);
// _id: 1, name: Doe, _rowclass (text)
$formElement['sql1'] = $this->db->sql('SELECT id AS "_id", name, IF(id=1,"text-warning", "text-danger") AS _rowClass FROM Person ORDER BY id LIMIT 2');
$result = $build->buildSubrecord($formElement, 'name:1', '', $json);
$this->assertEquals('<table class="table table-hover"><tr><th>name</th></tr><tr class="record text-warning" ><td>Doe</td></tr><tr class="record text-danger" ><td>Smith</td></tr></table>', $result);
// _id: 1, name: Doe, _rowClass (text & background)
$formElement['sql1'] = $this->db->sql('SELECT id AS "_id", name, IF(id=1,"text-warning active", "text-danger success") AS _rowClass FROM Person ORDER BY id LIMIT 2');
$result = $build->buildSubrecord($formElement, 'name:1', '', $json);
$this->assertEquals('<table class="table table-hover"><tr><th>name</th></tr><tr class="record text-warning active" ><td>Doe</td></tr><tr class="record text-danger success" ><td>Smith</td></tr></table>', $result);
// _id: 1, name: Doe, _rowTitle
$formElement['sql1'] = $this->db->sql('SELECT id AS "_id", name, firstName AS _rowTitle FROM Person ORDER BY id LIMIT 2');
$result = $build->buildSubrecord($formElement, 'name:1', '', $json);
$this->assertEquals('<table class="table table-hover"><tr><th>name</th></tr><tr class="record" title="John" ><td>Doe</td></tr><tr class="record" title="Jane" ><td>Smith</td></tr></table>', $result);
// _id: 1, name: Doe, title, width, nostrip
//TODO: Test zum laufen bringen
// $formElement['sql1'] = $this->db->sql('SELECT id AS "_id", name, "<b>This again is a very long text</b>" AS "title=Important|width=10|nostrip" FROM Person ORDER BY id LIMIT 2');
// $result = $build->buildSubrecord($formElement, 'name:1', '', $json);
// $this->assertEquals('<table class="table table-hover"><tr><th>name</th></tr><tr class="record" title="John" ><td>Doe</td></tr><tr class="record" title="Jane" ><td>Smith</td></tr></table>', $result);
}
/**
* @throws Exception
*/
......@@ -386,10 +475,7 @@ class BuildFormPlainTest extends AbstractDatabaseTest {
parent::setUp();
$this->executeSQLFile(__DIR__ . '/fixtures/Generic.sql', true);
// $this->executeSQLFile(__DIR__ . '/fixtures/TestFormEditor.sql', true);
// Defaults
$this->store->setVar('name', 'varchar(255)', STORE_TABLE_COLUMN_TYPES, true);
......@@ -397,11 +483,8 @@ class BuildFormPlainTest extends AbstractDatabaseTest {
$GLOBALS["TSFE"] = new FakeTSFEBuildPlain();
// $this->form = new \qfq\QuickFormQuery(['bodytext' => "form=form\nr=3", 'uid' => 1234], true);
$form = new \qfq\QuickFormQuery(['bodytext' => "form=form\nr=3", 'uid' => 1234], true);
// this is necessary to initialize SIP
// $content = $this->form->process();
$form->process();
}
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment