mirror of
https://github.com/cosmocode/dokuwiki-plugin-struct.git
synced 2025-07-29 11:59:24 +00:00
lists: correctly handle nesting with empty values
empty values are shown as n/a when they are used in nesting, otherwise not shown just as before
This commit is contained in:
@ -46,6 +46,21 @@ class NestedResultTest extends StructTest
|
||||
[['gray', 'yellow'], 'laptop', 'dell', 'latitude'],
|
||||
];
|
||||
|
||||
protected $multiHoleItems = [
|
||||
[['green', 'yellow'], 'car', 'audi', 'a80'],
|
||||
[[], 'car', 'audi', 'a4'],
|
||||
[['black', 'green'], '', 'audi', 'quattro'],
|
||||
[['red', 'black'], 'car', 'bmw', 'i3'],
|
||||
[['blue', 'gray'], 'car', 'bmw', 'mini'],
|
||||
[['red', 'black'], 'car', 'bmw', 'z1'],
|
||||
[['green', 'blue'], 'laptop', 'apple', 'pro 16'],
|
||||
[['red', 'blue'], 'laptop', 'apple', 'air'],
|
||||
[['black', 'red'], 'laptop', 'apple', 'm1'],
|
||||
[[], 'laptop', 'dell', 'xps'],
|
||||
[['blue', 'yellow'], '', 'dell', 'inspiron'],
|
||||
[['gray', 'yellow'], 'laptop', 'dell', 'latitude'],
|
||||
];
|
||||
|
||||
protected $multiMultiItems = [
|
||||
[['metal', 'wood'], ['green', 'yellow'], 'car', 'audi', 'a80'],
|
||||
[['metal', 'wood', 'plastic'], ['yellow', 'blue'], 'car', 'audi', 'a4'],
|
||||
@ -188,6 +203,17 @@ class NestedResultTest extends StructTest
|
||||
);
|
||||
}
|
||||
|
||||
public function testMultiHoles()
|
||||
{
|
||||
$result = $this->makeResult($this->multiHoleItems);
|
||||
$nestedResult = new NestedResult($result);
|
||||
$root = $nestedResult->getRoot(3);
|
||||
$tree = $root->getChildren(); // nest: color, type, brand -> model
|
||||
$this->assertCount(7, $tree, '6 root nodes of colors + 1 n/a expected'); // should have one n/a node
|
||||
$this->assertCount(2, $tree[6]->getChildren(), 'top n/a node should have car, laptop');
|
||||
$this->assertCount(3, $tree[0]->getChildren(), 'black should have car,laptop,n/a');
|
||||
}
|
||||
|
||||
/**
|
||||
* Nest by two multi value levels
|
||||
*/
|
||||
|
@ -65,6 +65,7 @@ $lang['multidropdown'] = 'Hold CTRL or CMD to select multiple values.';
|
||||
$lang['duplicate_label'] = "Label <code>%s</code> already exists in schema, second occurance was renamed to <code>%s</code>.";
|
||||
|
||||
$lang['emptypage'] = 'Struct data has not been saved for an empty page';
|
||||
$lang['na'] = 'n/a';
|
||||
|
||||
$lang['validation_prefix'] = "Field [%s]: ";
|
||||
|
||||
|
@ -51,7 +51,7 @@ class AggregationList extends Aggregation
|
||||
// render own value if available
|
||||
if ($self) {
|
||||
$this->renderer->listcontent_open();
|
||||
$this->renderListItem([$self], $node->getDepth()); // zero based depth
|
||||
$this->renderListItem([$self], $node->getDepth(), true); // zero based depth
|
||||
$this->renderer->listcontent_close();
|
||||
}
|
||||
|
||||
@ -85,14 +85,14 @@ class AggregationList extends Aggregation
|
||||
*
|
||||
* @param Value[] $resultrow
|
||||
* @param int $depth The current nesting depth (zero based)
|
||||
* @param bool $showEmpty show a placeholder for empty values?
|
||||
*/
|
||||
protected function renderListItem($resultrow, $depth)
|
||||
protected function renderListItem($resultrow, $depth, $showEmpty = false)
|
||||
{
|
||||
$sepbyheaders = $this->searchConfig->getConf()['sepbyheaders'];
|
||||
$headers = $this->searchConfig->getConf()['headers'];
|
||||
|
||||
foreach ($resultrow as $index => $value) {
|
||||
if ($value->isEmpty()) continue;
|
||||
$column = $index + $depth; // the resultrow is shifted by the nesting depth
|
||||
if ($sepbyheaders && !empty($headers[$column])) {
|
||||
$header = $headers[$column];
|
||||
@ -101,9 +101,9 @@ class AggregationList extends Aggregation
|
||||
}
|
||||
|
||||
if ($this->mode === 'xhtml') {
|
||||
$this->renderValueXHTML($value, $header);
|
||||
$this->renderValueXHTML($value, $header, $showEmpty);
|
||||
} else {
|
||||
$this->renderValueGeneric($value, $header);
|
||||
$this->renderValueGeneric($value, $header, $showEmpty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -112,9 +112,10 @@ class AggregationList extends Aggregation
|
||||
* Render the given Value in a XHTML renderer
|
||||
* @param Value $value
|
||||
* @param string $header
|
||||
* @param bool $showEmpty
|
||||
* @return void
|
||||
*/
|
||||
protected function renderValueXHTML($value, $header)
|
||||
protected function renderValueXHTML($value, $header, $showEmpty = false)
|
||||
{
|
||||
$attributes = [
|
||||
'data-struct-column' => strtolower($value->getColumn()->getFullQualifiedLabel()),
|
||||
@ -127,7 +128,11 @@ class AggregationList extends Aggregation
|
||||
$this->renderer->doc .= sprintf('<span class="struct_header">%s</span> ', hsc($header));
|
||||
}
|
||||
$this->renderer->doc .= '<div class="struct_value">';
|
||||
$value->render($this->renderer, $this->mode);
|
||||
if ($value->isEmpty() && $showEmpty) {
|
||||
$this->renderer->doc .= '<span class="struct_na">' . $this->helper->getLang('na') . '</span>';
|
||||
} else {
|
||||
$value->render($this->renderer, $this->mode);
|
||||
}
|
||||
$this->renderer->doc .= '</div>';
|
||||
$this->renderer->doc .= '</div> '; // wrapper
|
||||
}
|
||||
@ -138,11 +143,15 @@ class AggregationList extends Aggregation
|
||||
* @param string $header
|
||||
* @return void
|
||||
*/
|
||||
protected function renderValueGeneric($value, $header)
|
||||
protected function renderValueGeneric($value, $header, $showEmpty = false)
|
||||
{
|
||||
$this->renderer->listcontent_open();
|
||||
if ($header !== '') $this->renderer->cdata($header . ' ');
|
||||
$value->render($this->renderer, $this->mode);
|
||||
if ($value->isEmpty() && $showEmpty) {
|
||||
$this->renderer->cdata($this->helper->getLang('na'));
|
||||
} else {
|
||||
$value->render($this->renderer, $this->mode);
|
||||
}
|
||||
$this->renderer->listcontent_close();
|
||||
}
|
||||
}
|
||||
|
@ -111,11 +111,18 @@ class NestedResult
|
||||
$valObj = array_shift($row);
|
||||
if (!$valObj) return; // no more values to nest, usually shouldn't happen
|
||||
|
||||
if ($valObj->getColumn()->isMulti()) {
|
||||
if ($valObj->getColumn()->isMulti() && $valObj->getValue()) {
|
||||
// split up multi values into separate nodes
|
||||
$values = $valObj->getValue();
|
||||
foreach ($values as $value) {
|
||||
$newValue = new Value($valObj->getColumn(), $value);
|
||||
if($values) {
|
||||
foreach ($values as $value) {
|
||||
$newValue = new Value($valObj->getColumn(), $value);
|
||||
$node = $this->getNodeForValue($newValue, $depth);
|
||||
$parent->addChild($node);
|
||||
$this->nestBranch($node, $row, $nesting, $depth + 1);
|
||||
}
|
||||
} else {
|
||||
$newValue = new Value($valObj->getColumn(), ''); // add empty node
|
||||
$node = $this->getNodeForValue($newValue, $depth);
|
||||
$parent->addChild($node);
|
||||
$this->nestBranch($node, $row, $nesting, $depth + 1);
|
||||
|
@ -137,15 +137,26 @@ class NestedValue
|
||||
*/
|
||||
public function sortChildren(NestedValue $a, NestedValue $b)
|
||||
{
|
||||
$compA = join('-', (array)$a->getValueObject()->getCompareValue());
|
||||
$compB = join('-', (array)$b->getValueObject()->getCompareValue());
|
||||
|
||||
// sort empty values to the end
|
||||
if($compA === $compB) {
|
||||
return 0;
|
||||
}
|
||||
if($compA === '') {
|
||||
return 1;
|
||||
}
|
||||
if($compB === '') {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// note: the way NestedResults build the NestedValues, the value object should
|
||||
// always contain a single value only. But since the associated column is still
|
||||
// a multi-value column, getCompareValue() will still return an array.
|
||||
// So here we treat all returns as array and join them with a dash (even though
|
||||
// there should never be more than one value in there)
|
||||
return Sort::strcmp(
|
||||
join('-', (array)$a->getValueObject()->getCompareValue()),
|
||||
join('-', (array)$b->getValueObject()->getCompareValue())
|
||||
);
|
||||
return Sort::strcmp($compA, $compB);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user