1ea5ad12aSMichael Grosse<?php 2ea5ad12aSMichael Grosse 3ea5ad12aSMichael Grossenamespace dokuwiki\plugin\struct\meta; 4ea5ad12aSMichael Grosse 5ea5ad12aSMichael Grosse/** 6ea5ad12aSMichael Grosse * Class AggregationList 7ea5ad12aSMichael Grosse * 8ea5ad12aSMichael Grosse * @package dokuwiki\plugin\struct\meta 9ea5ad12aSMichael Grosse */ 10d6d97f60SAnna Dabrowskaclass AggregationList 11d6d97f60SAnna Dabrowska{ 12ea5ad12aSMichael Grosse /** 13ea5ad12aSMichael Grosse * @var string the page id of the page this is rendered to 14ea5ad12aSMichael Grosse */ 15ea5ad12aSMichael Grosse protected $id; 16ea5ad12aSMichael Grosse /** 17ea5ad12aSMichael Grosse * @var string the Type of renderer used 18ea5ad12aSMichael Grosse */ 19ea5ad12aSMichael Grosse protected $mode; 20ea5ad12aSMichael Grosse /** 21ea5ad12aSMichael Grosse * @var \Doku_Renderer the DokuWiki renderer used to create the output 22ea5ad12aSMichael Grosse */ 23ea5ad12aSMichael Grosse protected $renderer; 24ea5ad12aSMichael Grosse /** 25ea5ad12aSMichael Grosse * @var SearchConfig the configured search - gives access to columns etc. 26ea5ad12aSMichael Grosse */ 27ea5ad12aSMichael Grosse protected $searchConfig; 28ea5ad12aSMichael Grosse 29ea5ad12aSMichael Grosse /** 30ea5ad12aSMichael Grosse * @var Column[] the list of columns to be displayed 31ea5ad12aSMichael Grosse */ 32ea5ad12aSMichael Grosse protected $columns; 33ea5ad12aSMichael Grosse 34ea5ad12aSMichael Grosse /** 35ea5ad12aSMichael Grosse * @var Value[][] the search result 36ea5ad12aSMichael Grosse */ 37ea5ad12aSMichael Grosse protected $result; 38ea5ad12aSMichael Grosse 39ea5ad12aSMichael Grosse /** 40ea5ad12aSMichael Grosse * @var int number of all results 41ea5ad12aSMichael Grosse */ 42ea5ad12aSMichael Grosse protected $resultColumnCount; 43ea5ad12aSMichael Grosse 44ea5ad12aSMichael Grosse /** 45ea5ad12aSMichael Grosse * Initialize the Aggregation renderer and executes the search 46ea5ad12aSMichael Grosse * 470549dcc5SAndreas Gohr * You need to call @param string $id 48ea5ad12aSMichael Grosse * @param string $mode 49ea5ad12aSMichael Grosse * @param \Doku_Renderer $renderer 50ea5ad12aSMichael Grosse * @param SearchConfig $searchConfig 510549dcc5SAndreas Gohr * @see render() on the resulting object. 520549dcc5SAndreas Gohr * 53ea5ad12aSMichael Grosse */ 54d6d97f60SAnna Dabrowska public function __construct($id, $mode, \Doku_Renderer $renderer, SearchConfig $searchConfig) 55d6d97f60SAnna Dabrowska { 56ea5ad12aSMichael Grosse $this->id = $id; 57ea5ad12aSMichael Grosse $this->mode = $mode; 58ea5ad12aSMichael Grosse $this->renderer = $renderer; 59ea5ad12aSMichael Grosse $this->searchConfig = $searchConfig; 60ea5ad12aSMichael Grosse $this->data = $searchConfig->getConf(); 61ea5ad12aSMichael Grosse $this->columns = $searchConfig->getColumns(); 62ea5ad12aSMichael Grosse 63ea5ad12aSMichael Grosse $this->result = $this->searchConfig->execute(); 64ea5ad12aSMichael Grosse $this->resultColumnCount = count($this->columns); 65ea5ad12aSMichael Grosse $this->resultPIDs = $this->searchConfig->getPids(); 66ea5ad12aSMichael Grosse } 67ea5ad12aSMichael Grosse 68ea5ad12aSMichael Grosse /** 69ea5ad12aSMichael Grosse * Create the list on the renderer 70ea5ad12aSMichael Grosse */ 71d6d97f60SAnna Dabrowska public function render() 72d6d97f60SAnna Dabrowska { 73*5bc00e11SAndreas Gohr $nestedResult = new NestedResult($this->result); 74*5bc00e11SAndreas Gohr $root = $nestedResult->getRoot($this->data['nesting']); 75ea5ad12aSMichael Grosse 76ea5ad12aSMichael Grosse $this->startScope(); 77*5bc00e11SAndreas Gohr $this->renderNode($root); 78*5bc00e11SAndreas Gohr $this->finishScope(); 79*5bc00e11SAndreas Gohr } 80ea5ad12aSMichael Grosse 81*5bc00e11SAndreas Gohr /** 82*5bc00e11SAndreas Gohr * Recursively render the result tree 83*5bc00e11SAndreas Gohr * 84*5bc00e11SAndreas Gohr * @param NestedValue $node 85*5bc00e11SAndreas Gohr * @return void 86*5bc00e11SAndreas Gohr */ 87*5bc00e11SAndreas Gohr protected function renderNode(NestedValue $node) 88*5bc00e11SAndreas Gohr { 89*5bc00e11SAndreas Gohr $self = $node->getValueObject(); // null for root node 90*5bc00e11SAndreas Gohr $children = $node->getChildren(); 91*5bc00e11SAndreas Gohr $results = $node->getResultRows(); 92*5bc00e11SAndreas Gohr 93*5bc00e11SAndreas Gohr // all our content is in a listitem, unless we are the root node 94*5bc00e11SAndreas Gohr if ($self) { 95*5bc00e11SAndreas Gohr $this->renderer->listitem_open($node->getDepth() + 1); // levels are 1 based 96*5bc00e11SAndreas Gohr } 97*5bc00e11SAndreas Gohr 98*5bc00e11SAndreas Gohr // render own value if available 99*5bc00e11SAndreas Gohr if ($self) { 100*5bc00e11SAndreas Gohr $this->renderer->listcontent_open(); 101*5bc00e11SAndreas Gohr $this->renderListItem([$self], $node->getDepth()); // zero based depth 102*5bc00e11SAndreas Gohr $this->renderer->listcontent_close(); 103*5bc00e11SAndreas Gohr } 104*5bc00e11SAndreas Gohr 105*5bc00e11SAndreas Gohr // render children or results as sub-list 106*5bc00e11SAndreas Gohr if ($children || $results) { 1072c6e107cSMichael Grosse $this->renderer->listu_open(); 108ea5ad12aSMichael Grosse 109*5bc00e11SAndreas Gohr foreach ($children as $child) { 110*5bc00e11SAndreas Gohr $this->renderNode($child); 111*5bc00e11SAndreas Gohr } 112*5bc00e11SAndreas Gohr 113*5bc00e11SAndreas Gohr foreach ($results as $result) { 114*5bc00e11SAndreas Gohr $this->renderer->listitem_open($node->getDepth() + 2); // levels are 1 based, this is one deeper 1152c6e107cSMichael Grosse $this->renderer->listcontent_open(); 116*5bc00e11SAndreas Gohr $this->renderListItem($result, $node->getDepth() + 1); // zero based depth, one deeper 1172c6e107cSMichael Grosse $this->renderer->listcontent_close(); 1182c6e107cSMichael Grosse $this->renderer->listitem_close(); 119ea5ad12aSMichael Grosse } 120ea5ad12aSMichael Grosse 1212c6e107cSMichael Grosse $this->renderer->listu_close(); 122*5bc00e11SAndreas Gohr } 123ea5ad12aSMichael Grosse 124*5bc00e11SAndreas Gohr // close listitem if opened 125*5bc00e11SAndreas Gohr if ($self) { 126*5bc00e11SAndreas Gohr $this->renderer->listitem_close(); 127*5bc00e11SAndreas Gohr } 128ea5ad12aSMichael Grosse } 129ea5ad12aSMichael Grosse 130ea5ad12aSMichael Grosse /** 131ea5ad12aSMichael Grosse * Adds additional info to document and renderer in XHTML mode 132ea5ad12aSMichael Grosse * 133ea5ad12aSMichael Grosse * @see finishScope() 134ea5ad12aSMichael Grosse */ 135d6d97f60SAnna Dabrowska protected function startScope() 136d6d97f60SAnna Dabrowska { 137ea5ad12aSMichael Grosse // wrapping div 138ea5ad12aSMichael Grosse if ($this->mode != 'xhtml') return; 139ea5ad12aSMichael Grosse $this->renderer->doc .= "<div class=\"structaggregation listaggregation\">"; 140ea5ad12aSMichael Grosse } 141ea5ad12aSMichael Grosse 142ea5ad12aSMichael Grosse /** 143ea5ad12aSMichael Grosse * Closes anything opened in startScope() 144ea5ad12aSMichael Grosse * 145ea5ad12aSMichael Grosse * @see startScope() 146ea5ad12aSMichael Grosse */ 147d6d97f60SAnna Dabrowska protected function finishScope() 148d6d97f60SAnna Dabrowska { 149ea5ad12aSMichael Grosse // wrapping div 150ea5ad12aSMichael Grosse if ($this->mode != 'xhtml') return; 151ea5ad12aSMichael Grosse $this->renderer->doc .= '</div>'; 152ea5ad12aSMichael Grosse } 153ea5ad12aSMichael Grosse 154*5bc00e11SAndreas Gohr 155ea5ad12aSMichael Grosse /** 156*5bc00e11SAndreas Gohr * Render the content of a single list item 157*5bc00e11SAndreas Gohr * 158*5bc00e11SAndreas Gohr * @param Value[] $resultrow 159*5bc00e11SAndreas Gohr * @param int $depth The current nesting depth (zero based) 160ea5ad12aSMichael Grosse */ 161*5bc00e11SAndreas Gohr protected function renderListItem($resultrow, $depth) 162d6d97f60SAnna Dabrowska { 163ea5ad12aSMichael Grosse $sepbyheaders = $this->searchConfig->getConf()['sepbyheaders']; 164ea5ad12aSMichael Grosse $headers = $this->searchConfig->getConf()['headers']; 165ea5ad12aSMichael Grosse 166*5bc00e11SAndreas Gohr foreach ($resultrow as $index => $value) { 167*5bc00e11SAndreas Gohr if ($value->isEmpty()) continue; 168*5bc00e11SAndreas Gohr $column = $index + $depth; // the resultrow is shifted by the nesting depth 169ea5ad12aSMichael Grosse if ($sepbyheaders && !empty($headers[$column])) { 170*5bc00e11SAndreas Gohr $header = $headers[$column]; 1712c6e107cSMichael Grosse } else { 172*5bc00e11SAndreas Gohr $header = ''; 173*5bc00e11SAndreas Gohr } 174*5bc00e11SAndreas Gohr 175*5bc00e11SAndreas Gohr if ($this->mode === 'xhtml') { 176*5bc00e11SAndreas Gohr $this->renderValueXHTML($value, $header); 177*5bc00e11SAndreas Gohr } else { 178*5bc00e11SAndreas Gohr $this->renderValueGeneric($value, $header); 179ea5ad12aSMichael Grosse } 1802c6e107cSMichael Grosse } 1812c6e107cSMichael Grosse } 182*5bc00e11SAndreas Gohr 183*5bc00e11SAndreas Gohr /** 184*5bc00e11SAndreas Gohr * Render the given Value in a XHTML renderer 185*5bc00e11SAndreas Gohr * @param Value $value 186*5bc00e11SAndreas Gohr * @param string $header 187*5bc00e11SAndreas Gohr * @return void 188*5bc00e11SAndreas Gohr */ 189*5bc00e11SAndreas Gohr protected function renderValueXHTML($value, $header) 190*5bc00e11SAndreas Gohr { 191*5bc00e11SAndreas Gohr $attributes = [ 192*5bc00e11SAndreas Gohr 'data-struct-column' => strtolower($value->getColumn()->getFullQualifiedLabel()), 193*5bc00e11SAndreas Gohr 'data-struct-type' => strtolower($value->getColumn()->getType()->getClass()), 194*5bc00e11SAndreas Gohr 'class' => 'li', // default dokuwiki content wrapper 195*5bc00e11SAndreas Gohr ]; 196*5bc00e11SAndreas Gohr 197*5bc00e11SAndreas Gohr $this->renderer->doc .= sprintf('<div %s>', buildAttributes($attributes)); // wrapper 198*5bc00e11SAndreas Gohr if ($header !== '') { 199*5bc00e11SAndreas Gohr $this->renderer->doc .= sprintf('<span class="struct_header">%s</span> ', hsc($header)); 200*5bc00e11SAndreas Gohr } 201*5bc00e11SAndreas Gohr $this->renderer->doc .= '<div class="struct_value">'; 202ea5ad12aSMichael Grosse $value->render($this->renderer, $this->mode); 203ea5ad12aSMichael Grosse $this->renderer->doc .= '</div>'; 204*5bc00e11SAndreas Gohr $this->renderer->doc .= '</div> '; // wrapper 205ea5ad12aSMichael Grosse } 206*5bc00e11SAndreas Gohr 207*5bc00e11SAndreas Gohr /** 208*5bc00e11SAndreas Gohr * Render the given Value in any non-XHTML renderer 209*5bc00e11SAndreas Gohr * @param Value $value 210*5bc00e11SAndreas Gohr * @param string $header 211*5bc00e11SAndreas Gohr * @return void 212*5bc00e11SAndreas Gohr */ 213*5bc00e11SAndreas Gohr protected function renderValueGeneric($value, $header) 214*5bc00e11SAndreas Gohr { 215*5bc00e11SAndreas Gohr $this->renderer->listcontent_open(); 216*5bc00e11SAndreas Gohr if ($header !== '') $this->renderer->cdata($header . ' '); 217*5bc00e11SAndreas Gohr $value->render($this->renderer, $this->mode); 218*5bc00e11SAndreas Gohr $this->renderer->listcontent_close(); 219ea5ad12aSMichael Grosse } 220ea5ad12aSMichael Grosse} 221