Skip to content

Commit 812a20f

Browse files
authored
Value aggregation type (#386)
* Allow a single value to be used inline in the page * Removed debug line * Added short form of value syntax - {{$s.f}} * Refactor inline parsing into a new class and add filtering for page ID * Allow recovery if the user has used single quotes * Fixed metadata rendering when using $INFO['id'] * Handle no result better * Add configuration for whether to show a no records text * Linter fixes * Match array style with other entries * Simplify tokenising with -> separator * Replace use of GLOBALS array * Revert to dots, remove block syntax * Suggestion of future syntax * Use space-separated parameters * Explanatory comments * Increase sort order * Don't lint for errors required by DokuWiki * Move syntax description to class documentation * Draft InlineConfigParser test * Docstring alignment
1 parent 4336569 commit 812a20f

File tree

7 files changed

+456
-0
lines changed

7 files changed

+456
-0
lines changed

_test/InlineConfigParser.test.php

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
namespace dokuwiki\plugin\struct\test;
4+
5+
use dokuwiki\plugin\struct\meta;
6+
7+
/**
8+
* Tests for parsing the inline aggregation config for the struct plugin
9+
*
10+
* @group plugin_struct
11+
* @group plugins
12+
*
13+
*/
14+
// phpcs:ignore Squiz.Classes.ValidClassName
15+
class InlineConfigParser_struct_test extends StructTest
16+
{
17+
// phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
18+
public function test_simple()
19+
{
20+
// Same initial setup as ConfigParser.test
21+
$inline = '"testtable, another, foo bar"."%pageid%, count" ';
22+
$inline .= '?sort: ^count sort: "%pageid%, ^bam" align: "r,l,center,foo"';
23+
// Add InlineConfigParser-specific tests:
24+
$inline .= '& "%pageid% != start" | "count = 1"';
25+
26+
$configParser = new meta\InlineConfigParser($inline);
27+
$actual_config = $configParser->getConfig();
28+
29+
$expected_config = [
30+
'align' => ['right', 'left', 'center', null],
31+
'cols' => ['%pageid%', 'count'],
32+
'csv' => true,
33+
'dynfilters' => false,
34+
'filter' => [
35+
['%pageid%', '!=', 'start', 'AND'],
36+
['count', '=', '1', 'OR' ],
37+
],
38+
'headers' => [null, null],
39+
'limit' => 0,
40+
'rownumbers' => false,
41+
'schemas' => [
42+
['testtable', '' ],
43+
['another', '' ],
44+
['foo', 'bar'],
45+
],
46+
'sepbyheaders' => false,
47+
'sort' => [
48+
['count', false],
49+
['%pageid%', true ],
50+
['bam', false],
51+
],
52+
'summarize' => false,
53+
'target' => '',
54+
'widths' => [],
55+
];
56+
57+
$this->assertEquals($expected_config, $actual_config);
58+
}
59+
}

conf/default.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
$conf['bottomoutput'] = 0;
44
$conf['topoutput'] = 0;
55
$conf['disableDeleteSerial'] = 0;
6+
$conf['show_not_found'] = 1;

conf/metadata.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
$meta['bottomoutput'] = ['onoff'];
44
$meta['topoutput'] = ['onoff'];
55
$meta['disableDeleteSerial'] = ['onoff'];
6+
$meta['show_not_found'] = ['onoff'];

lang/en/settings.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
<?php
2+
23
$lang['bottomoutput'] = 'Display data at the bottom of the page';
34
$lang['topoutput'] = 'Display data at the top of the page';
45
$lang['disableDeleteSerial'] = 'Disable delete button for serial data';
6+
$lang['show_not_found'] = 'Show the default text when no results are returned for struct value syntax';

meta/AggregationValue.php

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
<?php
2+
3+
namespace dokuwiki\plugin\struct\meta;
4+
5+
/**
6+
* Class AggregationValue
7+
*
8+
* @package dokuwiki\plugin\struct\meta
9+
* @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
10+
* @author Iain Hallam <iain@nineworlds.net>
11+
*/
12+
class AggregationValue
13+
{
14+
15+
/**
16+
* @var string the page id of the page this is rendered to
17+
*/
18+
protected $id;
19+
/**
20+
* @var string the Type of renderer used
21+
*/
22+
protected $mode;
23+
/**
24+
* @var Doku_Renderer the DokuWiki renderer used to create the output
25+
*/
26+
protected $renderer;
27+
/**
28+
* @var SearchConfig the configured search - gives access to columns etc.
29+
*/
30+
protected $searchConfig;
31+
32+
/**
33+
* @var Column the column to be displayed
34+
*/
35+
protected $column;
36+
37+
/**
38+
* @var Value[][] the search result
39+
*/
40+
protected $result;
41+
42+
/**
43+
* @var int number of all results
44+
*/
45+
protected $resultCount;
46+
47+
/**
48+
* @todo we might be able to get rid of this helper and move this to SearchConfig
49+
* @var helper_plugin_struct_config
50+
*/
51+
protected $helper;
52+
53+
/**
54+
* Initialize the Aggregation renderer and executes the search
55+
*
56+
* You need to call @see render() on the resulting object.
57+
*
58+
* @param string $id
59+
* @param string $mode
60+
* @param Doku_Renderer $renderer
61+
* @param SearchConfig $searchConfig
62+
*/
63+
public function __construct($id, $mode, \Doku_Renderer $renderer, SearchConfig $searchConfig)
64+
{
65+
// Parameters
66+
$this->id = $id;
67+
$this->mode = $mode;
68+
$this->renderer = $renderer;
69+
$this->searchConfig = $searchConfig;
70+
71+
// Search info
72+
$this->data = $this->searchConfig->getConf();
73+
$columns = $this->searchConfig->getColumns();
74+
$this->column = $columns[0];
75+
76+
// limit to first result
77+
$this->searchConfig->setLimit(1);
78+
$this->searchConfig->setOffset(0);
79+
80+
// Run the search
81+
$result = $this->searchConfig->execute();
82+
$this->resultCount = $this->searchConfig->getCount();
83+
84+
// Change from two-dimensional array with one entry to one-dimensional array
85+
$this->result = $result[0];
86+
87+
// Load helper
88+
$this->helper = plugin_load('helper', 'struct_config');
89+
}
90+
91+
/**
92+
* Create the output on the renderer
93+
*
94+
* @param int $show_not_found Whether to display the default text for no records
95+
*/
96+
public function render($show_not_found = 0)
97+
{
98+
$this->startScope();
99+
100+
// Check that we actually got a result
101+
if ($this->resultCount) {
102+
$this->renderValue($this->result);
103+
} else {
104+
if ($show_not_found) {
105+
$this->renderer->cdata($this->helper->getLang('none'));
106+
}
107+
}
108+
109+
$this->finishScope();
110+
111+
return;
112+
}
113+
114+
/**
115+
* Adds additional info to document and renderer in XHTML mode
116+
*
117+
* @see finishScope()
118+
*/
119+
protected function startScope()
120+
{
121+
// wrapping span
122+
if ($this->mode != 'xhtml') {
123+
return;
124+
}
125+
$this->renderer->doc .= "<span class=\"structaggregation valueaggregation\">";
126+
}
127+
128+
/**
129+
* Closes anything opened in startScope()
130+
*
131+
* @see startScope()
132+
*/
133+
protected function finishScope()
134+
{
135+
// wrapping span
136+
if ($this->mode != 'xhtml') {
137+
return;
138+
}
139+
$this->renderer->doc .= '</span>';
140+
}
141+
142+
/**
143+
* @param $resultrow
144+
*/
145+
protected function renderValue($resultrow)
146+
{
147+
// @var Value $value
148+
foreach ($resultrow as $column => $value) {
149+
if ($value->isEmpty()) {
150+
continue;
151+
}
152+
if ($this->mode == 'xhtml') {
153+
$type = 'struct_' . strtolower($value->getColumn()->getType()->getClass());
154+
$this->renderer->doc .= '<span class="' . $type . '">';
155+
}
156+
$value->render($this->renderer, $this->mode);
157+
if ($this->mode == 'xhtml') {
158+
$this->renderer->doc .= '</span>';
159+
}
160+
}
161+
}
162+
}

meta/InlineConfigParser.php

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
<?php
2+
3+
namespace dokuwiki\plugin\struct\meta;
4+
5+
/**
6+
* Class InlineConfigParser
7+
*
8+
* Wrapper to convert inline syntax to full before instantiating ConfigParser
9+
*
10+
* {{$schema.field}}
11+
* {{$pageid.schema.field}}
12+
* {{$... ? filter: ... and: ... or: ...}} or {{$... ? & ... | ...}}
13+
* TODO: {{$... ? sum}} or {{$... ? +}}
14+
* TODO: {{$... ? default: ...}} or {{$... ? ! ...}}
15+
* Colons following key words must have no space preceding them.
16+
* If no page ID or filter is supplied, filter: "%pageid% = $ID$" is added.
17+
* Any component can be placed in double quotes (needed to allow space, dot or question mark in components).
18+
*
19+
* @package dokuwiki\plugin\struct\meta
20+
*/
21+
class InlineConfigParser extends ConfigParser
22+
{
23+
24+
/**
25+
* Parser constructor.
26+
*
27+
* parses the given inline configuration
28+
*
29+
* @param string $inline
30+
*/
31+
public function __construct($inline)
32+
{
33+
// Start to build the main config array
34+
$lines = array(); // Config lines to pass to full parser
35+
36+
// Extract components
37+
$parts = explode('?', $inline, 2);
38+
$n_parts = count($parts);
39+
$components = str_getcsv(trim($parts[0]), '.');
40+
$n_components = count($components);
41+
42+
// Extract parameters if given
43+
if ($n_parts == 2) {
44+
$filtering = false; // Whether to filter result to current page
45+
$parameters = str_getcsv(trim($parts[1]), ' ');
46+
$n_parameters = count($parameters);
47+
48+
// Process parameters and add to config lines
49+
for ($i = 0; $i < $n_parameters; $i++) {
50+
$p = trim($parameters[$i]);
51+
switch ($p) {
52+
// Empty (due to extra spaces)
53+
case '':
54+
// Move straight to next parameter
55+
continue 2;
56+
break;
57+
// Pass full text ending in : straight to config
58+
case $p[-1] == ':' ? $p : '':
59+
if (in_array($p, ['filter', 'where', 'filterand', 'and', 'filteror','or'])) {
60+
$filtering = true;
61+
}
62+
$lines[] = $p . ' ' . trim($parameters[$i + 1]);
63+
$i++;
64+
break;
65+
// Short alias for filterand
66+
case '&':
67+
$filtering = true;
68+
$lines[] = 'filterand: ' . trim($parameters[$i + 1]);
69+
$i++;
70+
break;
71+
// Short alias for filteror
72+
case '|':
73+
$filtering = true;
74+
$lines[] = 'filteror: ' . trim($parameters[$i + 1]);
75+
$i++;
76+
break;
77+
default:
78+
// Move straight to next parameter
79+
continue 2;
80+
break;
81+
}
82+
}
83+
}
84+
85+
// Check whether a page was specified
86+
if (count($components) == 3) {
87+
// At least page, schema and field supplied
88+
$lines[] = 'schema: ' . trim($components[1]);
89+
$lines[] = 'field: ' . trim($components[2]);
90+
$lines[] = 'filter: %pageid% = ' . trim($components[0]);
91+
} elseif (count($components) == 2) {
92+
// At least schema and field supplied
93+
$lines[] = 'schema: ' . trim($components[0]);
94+
$lines[] = 'field: ' . trim($components[1]);
95+
if (! $filtering) {
96+
$lines[] = 'filter: %pageid% = $ID$';
97+
}
98+
}
99+
100+
// Call original ConfigParser's constructor
101+
parent::__construct($lines);
102+
}
103+
}

0 commit comments

Comments
 (0)