1<?php 2 3namespace dokuwiki\plugin\struct\meta; 4 5/** 6 * Class AggregationList 7 * 8 * @package dokuwiki\plugin\struct\meta 9 */ 10class AggregationList extends Aggregation 11{ 12 /** @var int number of all results */ 13 protected $resultColumnCount; 14 15 /** @inheritdoc */ 16 public function __construct($id, $mode, \Doku_Renderer $renderer, SearchConfig $searchConfig) 17 { 18 parent::__construct($id, $mode, $renderer, $searchConfig); 19 $this->resultColumnCount = count($this->columns); 20 } 21 22 /** @inheritdoc */ 23 public function render($showNotFound = false) 24 { 25 if ($this->result) { 26 $nestedResult = new NestedResult($this->result); 27 $root = $nestedResult->getRoot($this->data['nesting'], $this->data['index']); 28 $this->renderNode($root); 29 } elseif ($showNotFound) { 30 $this->renderer->cdata($this->helper->getLang('none')); 31 } 32 } 33 34 /** 35 * Recursively render the result tree 36 * 37 * @param NestedValue $node 38 * @return void 39 */ 40 protected function renderNode(NestedValue $node) 41 { 42 $self = $node->getValueObject(); // null for root node 43 $children = $node->getChildren(); 44 $results = $node->getResultRows(); 45 46 // all our content is in a listitem, unless we are the root node 47 if ($self) { 48 $this->renderer->listitem_open($node->getDepth() + 1); // levels are 1 based 49 } 50 51 // render own value if available 52 if ($self) { 53 $this->renderer->listcontent_open(); 54 $this->renderListItem([$self], $node->getDepth(), true); // zero based depth 55 $this->renderer->listcontent_close(); 56 } 57 58 // render children or results as sub-list 59 if ($children || $results) { 60 $this->renderer->listu_open(); 61 62 foreach ($children as $child) { 63 $this->renderNode($child); 64 } 65 66 foreach ($results as $result) { 67 $this->renderer->listitem_open($node->getDepth() + 2); // levels are 1 based, this is one deeper 68 $this->renderer->listcontent_open(); 69 $this->renderListItem($result, $node->getDepth() + 1); // zero based depth, one deeper 70 $this->renderer->listcontent_close(); 71 $this->renderer->listitem_close(); 72 } 73 74 $this->renderer->listu_close(); 75 } 76 77 // close listitem if opened 78 if ($self) { 79 $this->renderer->listitem_close(); 80 } 81 } 82 83 /** 84 * Render the content of a single list item 85 * 86 * @param Value[] $resultrow 87 * @param int $depth The current nesting depth (zero based) 88 * @param bool $showEmpty show a placeholder for empty values? 89 */ 90 protected function renderListItem($resultrow, $depth, $showEmpty = false) 91 { 92 $sepbyheaders = $this->searchConfig->getConf()['sepbyheaders']; 93 $headers = $this->searchConfig->getConf()['headers']; 94 95 foreach ($resultrow as $index => $value) { 96 $column = $index + $depth; // the resultrow is shifted by the nesting depth 97 if ($sepbyheaders && !empty($headers[$column])) { 98 $header = $headers[$column]; 99 } else { 100 $header = ''; 101 } 102 103 if ($this->mode === 'xhtml') { 104 $this->renderValueXHTML($value, $header, $showEmpty); 105 } else { 106 $this->renderValueGeneric($value, $header, $showEmpty); 107 } 108 } 109 } 110 111 /** 112 * Render the given Value in a XHTML renderer 113 * @param Value $value 114 * @param string $header 115 * @param bool $showEmpty 116 * @return void 117 */ 118 protected function renderValueXHTML($value, $header, $showEmpty = false) 119 { 120 $attributes = [ 121 'data-struct-column' => strtolower($value->getColumn()->getFullQualifiedLabel()), 122 'data-struct-type' => strtolower($value->getColumn()->getType()->getClass()), 123 'class' => 'li', // default dokuwiki content wrapper 124 ]; 125 126 $this->renderer->doc .= sprintf('<div %s>', buildAttributes($attributes)); // wrapper 127 if ($header !== '') { 128 $this->renderer->doc .= sprintf('<span class="struct_header">%s</span> ', hsc($header)); 129 } 130 $this->renderer->doc .= '<div class="struct_value">'; 131 if ($value->isEmpty() && $showEmpty) { 132 $this->renderer->doc .= '<span class="struct_na">' . $this->helper->getLang('na') . '</span>'; 133 } else { 134 $value->render($this->renderer, $this->mode); 135 } 136 $this->renderer->doc .= '</div>'; 137 $this->renderer->doc .= '</div> '; // wrapper 138 } 139 140 /** 141 * Render the given Value in any non-XHTML renderer 142 * @param Value $value 143 * @param string $header 144 * @return void 145 */ 146 protected function renderValueGeneric($value, $header, $showEmpty = false) 147 { 148 $this->renderer->listcontent_open(); 149 if ($header !== '') $this->renderer->cdata($header . ' '); 150 if ($value->isEmpty() && $showEmpty) { 151 $this->renderer->cdata($this->helper->getLang('na')); 152 } else { 153 $value->render($this->renderer, $this->mode); 154 } 155 $this->renderer->listcontent_close(); 156 } 157} 158