added classes config parameter

This allows users to set custom CSS classes on aggregations potentially
restyling them differently for different use cases.

This makes startScope and finishScope part of the public API of the
Aggregation class. It should no longer be called within render() but is
instead called outside.

This might potentially break plugins implementing their own
aggregations. Needs to be checked
This commit is contained in:
Andreas Gohr
2023-06-26 15:09:40 +02:00
parent 00f71f1792
commit af0ce8d2fb
14 changed files with 107 additions and 103 deletions

View File

@ -21,7 +21,8 @@ class ConfigParserTest extends StructTest
"cols : %pageid%, count",
"sort : ^count",
"sort : %pageid%, ^bam",
"align : r,l,center,foo"
"align : r,l,center,foo",
"class : foo, bar",
];
$configParser = new meta\ConfigParser($lines);
@ -85,6 +86,7 @@ class ConfigParserTest extends StructTest
'align' => ['right', 'left', 'center', null],
'nesting' => 0,
'index' => 0,
'classes' => ['struct-custom-foo', 'struct-custom-bar'],
];
$this->assertEquals($expected_config, $actual_config);

View File

@ -53,6 +53,7 @@ class InlineConfigParserTest extends StructTest
'widths' => [],
'nesting' => 0,
'index' => 0,
'classes' => [],
];
$this->assertEquals($expected_config, $actual_config);

View File

@ -231,6 +231,9 @@ class SearchConfigParameterTest extends StructTest
],
'rownumbers' => '1',
'limit' => '5',
'nesting' => 0,
'index' => 0,
'classes' => [],
];
$R = new \Doku_Renderer_xhtml();
@ -238,7 +241,9 @@ class SearchConfigParameterTest extends StructTest
$INPUT->set(meta\SearchConfigParameters::$PARAM_OFFSET, 5);
$searchConfig = new meta\SearchConfig($data);
$aggregationTable = new meta\AggregationTable('test_pagination', 'xhtml', $R, $searchConfig);
$aggregationTable->startScope();
$aggregationTable->render();
$aggregationTable->finishScope();
$rev = time();

View File

@ -73,9 +73,67 @@ abstract class Aggregation
}
/**
* Create the table on the renderer
* Return the list of classes that should be added to the scope when rendering XHTML
*
* @return string[]
*/
public function getScopeClasses()
{
// we're all aggregations
$classes = ['structaggregation'];
// which type of aggregation are we?
$class = get_class($this);
$class = substr($class, strrpos($class, "\\") + 1);
$class = strtolower($class);
$classes[] = 'struct' . $class;
// config options
if ($this->data['nesting']) {
$classes[] = 'is-nested';
}
if ($this->data['index']) {
$classes[] = 'is-indexed';
}
// custom classes
$classes = array_merge($classes, $this->data['classes']);
return $classes;
}
/**
* Render the actual output to the renderer
*
* @param bool $showNotFound show a not found message when no data available?
*/
abstract public function render($showNotFound = false);
/**
* Adds additional info to document and renderer in XHTML mode
*
* Called before render()
*
* @see finishScope()
*/
public function startScope()
{
if ($this->mode == 'xhtml') {
$classes = $this->getScopeClasses();
$this->renderer->doc .= '<div class="' . join(' ', $classes) . '">';
}
}
/**
* Closes anything opened in startScope()
*
* Called after render()
*
* @see startScope()
*/
public function finishScope()
{
if ($this->mode == 'xhtml') {
$this->renderer->doc .= '</div>';
}
}
}

View File

@ -32,37 +32,11 @@ class AggregationCloud extends Aggregation
public function render($showNotFound = false)
{
$this->sortResults();
$this->startScope();
$this->startList();
foreach ($this->result as $result) {
$this->renderTag($result);
}
$this->finishList();
$this->finishScope();
}
/**
* Adds additional info to document and renderer in XHTML mode
*
* @see finishScope()
*/
protected function startScope()
{
// wrapping div
if ($this->mode != 'xhtml') return;
$this->renderer->doc .= "<div class=\"structcloud\">";
}
/**
* Closes the table and anything opened in startScope()
*
* @see startScope()
*/
protected function finishScope()
{
// wrapping div
if ($this->mode != 'xhtml') return;
$this->renderer->doc .= '</div>';
}
/**

View File

@ -25,7 +25,7 @@ class AggregationEditorTable extends AggregationTable
*
* @see finishScope()
*/
protected function startScope()
public function startScope()
{
// unique identifier for this aggregation
$this->renderer->info['struct_table_hash'] = md5(var_export($this->data, true));
@ -39,7 +39,10 @@ class AggregationEditorTable extends AggregationTable
$config = hsc(json_encode($config));
// wrapping div
$this->renderer->doc .= "<div class=\"structaggregation structaggregationeditor\"
$classes = $this->getScopeClasses();
$classes[] = 'structaggregationeditor';
$classes = join(' ', $classes);
$this->renderer->doc .= "<div class=\"$classes\"
data-schema=\"$table\" data-searchconf=\"$config\">";
// unique identifier for this aggregation

View File

@ -22,7 +22,6 @@ class AggregationList extends Aggregation
/** @inheritdoc */
public function render($showNotFound = false)
{
$this->startScope();
if ($this->result) {
$nestedResult = new NestedResult($this->result);
$root = $nestedResult->getRoot($this->data['nesting'], $this->data['index']);
@ -30,7 +29,6 @@ class AggregationList extends Aggregation
} elseif ($showNotFound) {
$this->renderer->cdata($this->helper->getLang('none'));
}
$this->finishScope();
}
/**
@ -82,31 +80,6 @@ class AggregationList extends Aggregation
}
}
/**
* Adds additional info to document and renderer in XHTML mode
*
* @see finishScope()
*/
protected function startScope()
{
// wrapping div
if ($this->mode != 'xhtml') return;
$this->renderer->doc .= "<div class=\"structaggregation listaggregation\">";
}
/**
* Closes anything opened in startScope()
*
* @see startScope()
*/
protected function finishScope()
{
// wrapping div
if ($this->mode != 'xhtml') return;
$this->renderer->doc .= '</div>';
}
/**
* Render the content of a single list item
*

View File

@ -31,13 +31,10 @@ class AggregationTable extends Aggregation
// abort early if there are no results at all (not filtered)
if (!$this->resultCount && !$this->isDynamicallyFiltered() && $showNotFound) {
$this->startScope();
$this->renderer->cdata($this->helper->getLang('none'));
$this->finishScope();
return;
}
$this->startScope();
$this->renderActiveFilters();
$rendercontext = array(
@ -57,7 +54,6 @@ class AggregationTable extends Aggregation
// export handle
$this->renderExportControls();
$this->finishScope();
}
/**
@ -98,14 +94,12 @@ class AggregationTable extends Aggregation
*
* @see finishScope()
*/
protected function startScope()
public function startScope()
{
// unique identifier for this aggregation
$this->renderer->info['struct_table_hash'] = md5(var_export($this->data, true));
// wrapping div
if ($this->mode != 'xhtml') return;
$this->renderer->doc .= "<div class=\"structaggregation\">";
parent::startScope();
}
/**
@ -113,16 +107,14 @@ class AggregationTable extends Aggregation
*
* @see startScope()
*/
protected function finishScope()
public function finishScope()
{
// remove identifier from renderer again
if (isset($this->renderer->info['struct_table_hash'])) {
unset($this->renderer->info['struct_table_hash']);
}
// wrapping div
if ($this->mode != 'xhtml') return;
$this->renderer->doc .= '</div>';
parent::finishScope();
}
/**

View File

@ -33,8 +33,6 @@ class AggregationValue extends Aggregation
*/
public function render($show_not_found = 0)
{
$this->startScope();
// Check that we actually got a result
if ($this->resultCount) {
$this->renderValue($this->result[0]); // only one result
@ -43,36 +41,6 @@ class AggregationValue extends Aggregation
$this->renderer->cdata($this->helper->getLang('none'));
}
}
$this->finishScope();
}
/**
* Adds additional info to document and renderer in XHTML mode
*
* @see finishScope()
*/
protected function startScope()
{
// wrapping span
if ($this->mode != 'xhtml') {
return;
}
$this->renderer->doc .= "<span class=\"structaggregation valueaggregation\">";
}
/**
* Closes anything opened in startScope()
*
* @see startScope()
*/
protected function finishScope()
{
// wrapping span
if ($this->mode != 'xhtml') {
return;
}
$this->renderer->doc .= '</span>';
}
/**

View File

@ -41,6 +41,7 @@ class ConfigParser
'csv' => true,
'nesting' => 0,
'index' => 0,
'classes' => array(),
);
// parse info
foreach ($lines as $line) {
@ -125,6 +126,10 @@ class ConfigParser
case 'index':
$this->config['index'] = (int) $val;
break;
case 'class':
case 'classes':
$this->config['classes'] = $this->parseClasses($val);
break;
default:
$data = array('config' => &$this->config, 'key' => $key, 'val' => $val);
$ev = new \Doku_Event('PLUGIN_STRUCT_CONFIGPARSER_UNKNOWNKEY', $data);
@ -312,4 +317,21 @@ class ConfigParser
}
return $values;
}
/**
* Ensure custom classes are valid and don't clash
*
* @param string $line
* @return string[]
*/
protected function parseClasses($line)
{
$classes = $this->parseValues($line);
$classes = array_map(function ($class) {
$class = str_replace(' ', '_', $class);
$class = preg_replace('/[^a-zA-Z0-9_]/', '', $class);
return 'struct-custom-' . $class;
}, $classes);
return $classes;
}
}

View File

@ -314,7 +314,7 @@ form.struct_newschema {
.dokuwiki .structaggregation {
&.listaggregation > ul li div {
&.structaggregationlist > ul li div {
display: inline;
p {
@ -380,7 +380,7 @@ form.struct_newschema {
}
}
.dokuwiki .structcloud {
.dokuwiki .structaggregationcloud {
ul {
text-align: center;

View File

@ -94,7 +94,9 @@ class syntax_plugin_struct_cloud extends DokuWiki_Syntax_Plugin
try {
$search = new SearchCloud($data);
$cloud = new AggregationCloud($INFO['id'], $mode, $renderer, $search);
$cloud->startScope();
$cloud->render();
$cloud->finishScope();
if ($mode == 'metadata') {
/** @var Doku_Renderer_metadata $renderer */
$renderer->meta['plugin']['struct']['hasaggregation'] = $search->getCacheFlag();

View File

@ -117,7 +117,9 @@ class syntax_plugin_struct_table extends DokuWiki_Syntax_Plugin
/** @var Aggregation $table */
$table = new $this->tableclass($mainId, $format, $renderer, $search);
$table->startScope();
$table->render(true);
$table->finishScope();
if ($format === 'metadata') {
/** @var Doku_Renderer_metadata $renderer */

View File

@ -104,7 +104,9 @@ class syntax_plugin_struct_value extends DokuWiki_Syntax_Plugin
/** @var AggregationValue $value */
$value = new AggregationValue($INFO['id'], $mode, $renderer, $search);
$value->startScope();
$value->render($show_not_found);
$value->finishScope();
if ($mode == 'metadata') {
/** @var Doku_Renderer_metadata $renderer */