Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature(views): lists can be rendered as tables
The list view functions now accept `table` as a `list_type` value. This outputs a bordered, one-column table with no headings. Columns and their headings are specified via `Elgg\Views\TableColumn` objects. The `ColumnFactory` class (accessible via `elgg()->table_columns`) includes methods for creating columns based on core views, or properties/methods. Newest users admin page now shows other useful info Fixes Elgg#7684 Fixes Elgg#9629
- Loading branch information
Showing
31 changed files
with
826 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<?php | ||
namespace Elgg\Views; | ||
|
||
/** | ||
* A renderer for a column of table cells and a header | ||
*/ | ||
interface TableColumn { | ||
|
||
/** | ||
* Get the rendered heading cell as HTML. Cell will be auto-wrapped with a TH element if the | ||
* returned string doesn't begin with "<th" or "<td". | ||
* | ||
* @return string e.g. "Title" or "<th>Title</th>". You must filter/escape any user content. | ||
*/ | ||
public function renderHeading(); | ||
|
||
/** | ||
* Render a value cell as HTML. Cell will be auto-wrapped with a TD element if the returned | ||
* string doesn't begin with "<th" or "<td". | ||
* | ||
* @param mixed $item Object/row from which to pull the value | ||
* @param string $type Type of object | ||
* @param array $item_vars Parameters from the listing function | ||
* | ||
* @return string e.g. "My Great Title" or "<td>My Great Title</td>". You must filter/escape any user content. | ||
*/ | ||
public function renderCell($item, $type, $item_vars); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
<?php | ||
namespace Elgg\Views\TableColumn; | ||
|
||
use Elgg\Views\TableColumn; | ||
|
||
/** | ||
* Table column rendered by a function | ||
*/ | ||
class CallableColumn implements TableColumn { | ||
|
||
/** | ||
* @var string | ||
*/ | ||
private $heading; | ||
|
||
/** | ||
* @var callable | ||
*/ | ||
private $renderer; | ||
|
||
/** | ||
* Constructor | ||
* | ||
* @param callable $renderer Rendering function | ||
* @param string $heading Heading | ||
*/ | ||
public function __construct(callable $renderer, $heading) { | ||
$this->renderer = $renderer; | ||
$this->heading = $heading; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function renderHeading() { | ||
return $this->heading; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function renderCell($item, $type, $item_vars) { | ||
return call_user_func($this->renderer, $item, $type, $item_vars); | ||
} | ||
} |
165 changes: 165 additions & 0 deletions
165
engine/classes/Elgg/Views/TableColumn/ColumnFactory.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
<?php | ||
namespace Elgg\Views\TableColumn; | ||
|
||
use Elgg\Values; | ||
use Elgg\Views\TableColumn; | ||
|
||
/** | ||
* Factory for table column objects | ||
* | ||
* `elgg_list_entities()` can output tables by specifying `$options['list_type'] = 'table'` and | ||
* by providing an array of TableColumn objects to `$options['columns']`. This service, available | ||
* as `elgg()->table_columns` provides methods to create column objects based around existing views | ||
* like `page/components/column/*`, properties, or methods. | ||
* | ||
* Numerous pre-existing methods are provided via `__call()` magic. See this method to find out how | ||
* to add your own methods, override the existing ones, or completely replace a method via hook. | ||
* | ||
* @internal Use elgg()->table_columns to access the instance of this. | ||
* | ||
* @method TableColumn admin($heading = null, $vars = []) | ||
* @method TableColumn banned($heading = null, $vars = []) | ||
* @method TableColumn container($heading = null, $vars = []) | ||
* @method TableColumn excerpt($heading = null, $vars = []) | ||
* @method TableColumn icon($heading = null, $vars = []) | ||
* @method TableColumn item($heading = null, $vars = []) | ||
* @method TableColumn language($heading = null, $vars = []) | ||
* @method TableColumn link($heading = null, $vars = []) | ||
* @method TableColumn owner($heading = null, $vars = []) | ||
* @method TableColumn time_created($heading = null, $vars = []) | ||
* @method TableColumn time_updated($heading = null, $vars = []) | ||
* @method TableColumn description($heading = null) | ||
* @method TableColumn email($heading = null) | ||
* @method TableColumn name($heading = null) | ||
* @method TableColumn type($heading = null) | ||
* @method TableColumn user($heading = null, $vars = []) | ||
* @method TableColumn username($heading = null) | ||
* @method TableColumn getSubtype($heading = null) | ||
* @method TableColumn getDisplayName($heading = null) | ||
* @method TableColumn getMimeType($heading = null) | ||
* @method TableColumn getSimpleType($heading = null) | ||
*/ | ||
class ColumnFactory { | ||
|
||
/** | ||
* Make a column from one of the page/components/column/* views. | ||
* | ||
* @param string $name Column name (view will be "page/components/column/$name") | ||
* @param string $heading Optional heading | ||
* @param array $vars View vars (item, item_vars, and type will be merged in) | ||
* | ||
* @return ViewColumn | ||
*/ | ||
public function fromView($name, $heading = null, $vars = []) { | ||
$view = "page/components/column/$name"; | ||
|
||
if (!is_string($heading)) { | ||
if (elgg_language_key_exists("table_columns:fromView:$name")) { | ||
$heading = elgg_echo("table_columns:fromView:$name"); | ||
} else { | ||
$title = str_replace('_', ' ', $name); | ||
$heading = elgg_ucwords($title); | ||
} | ||
} | ||
|
||
return new ViewColumn($view, $heading, $vars); | ||
} | ||
|
||
/** | ||
* Make a column by reading a property of the item | ||
* | ||
* @param string $name Property name. e.g. "description", "email", "type" | ||
* @param string $heading Heading | ||
* | ||
* @return CallableColumn | ||
*/ | ||
public function fromProperty($name, $heading = null) { | ||
if (!is_string($heading)) { | ||
if (elgg_language_key_exists("table_columns:fromProperty:$name")) { | ||
$heading = elgg_echo("table_columns:fromProperty:$name"); | ||
} else { | ||
$title = str_replace('_', ' ', $name); | ||
$heading = elgg_ucwords($title); | ||
} | ||
} | ||
|
||
$renderer = function ($item) use ($name) { | ||
return $item->{$name}; | ||
}; | ||
|
||
return new CallableColumn($renderer, $heading); | ||
} | ||
|
||
/** | ||
* Make a column by calling a method on the item | ||
* | ||
* @param string $name Method name. e.g. "getSubtype", "getDisplayName" | ||
* @param string $heading Heading | ||
* @param array $args Method arguments | ||
* | ||
* @return CallableColumn | ||
*/ | ||
public function fromMethod($name, $heading = null, $args = []) { | ||
if (!is_string($heading)) { | ||
if (elgg_language_key_exists("table_columns:fromMethod:$name")) { | ||
$heading = elgg_echo("table_columns:fromMethod:$name"); | ||
} else { | ||
$title = str_replace('_', ' ', $name); | ||
$heading = elgg_ucwords($title); | ||
} | ||
} | ||
|
||
$renderer = function ($item) use ($name, $args) { | ||
return call_user_func_array([$item, $name], $args); | ||
}; | ||
|
||
return new CallableColumn($renderer, $heading); | ||
} | ||
|
||
/** | ||
* Provide additional methods via hook and specified language keys. | ||
* | ||
* First, the hook `table_columns:call` is called. Details in `docs/guides/hooks-list.rst`. | ||
* | ||
* Then it checks existence of 3 language keys in order to defer processing to a local method: | ||
* | ||
* - "table_columns:fromView:$name" -> uses $this->fromView($name, ...). | ||
* - "table_columns:fromProperty:$name" -> uses $this->fromProperty($name, ...). | ||
* - "table_columns:fromMethod:$name" -> uses $this->fromMethod($name, ...). | ||
* | ||
* See the `from*()` methods for details. | ||
* | ||
* @param string $name Method name | ||
* @param array $arguments Arguments | ||
* | ||
* @return TableColumn | ||
*/ | ||
public function __call($name, $arguments) { | ||
// allow hook to hijack magic methods | ||
$column = elgg_trigger_plugin_hook('table_columns:call', $name, [ | ||
'arguments' => $arguments, | ||
]); | ||
if ($column instanceof TableColumn) { | ||
return $column; | ||
} | ||
|
||
if (elgg_language_key_exists("table_columns:fromView:$name")) { | ||
array_unshift($arguments, $name); | ||
return call_user_func_array([$this, 'fromView'], $arguments); | ||
} | ||
|
||
if (elgg_language_key_exists("table_columns:fromProperty:$name")) { | ||
array_unshift($arguments, $name); | ||
return call_user_func_array([$this, 'fromProperty'], $arguments); | ||
} | ||
|
||
if (elgg_language_key_exists("table_columns:fromMethod:$name")) { | ||
array_unshift($arguments, $name); | ||
return call_user_func_array([$this, 'fromMethod'], $arguments); | ||
} | ||
|
||
// empty column and error | ||
_elgg_services()->logger->error(__CLASS__ . ": No method defined '$name'"); | ||
return new CallableColumn([Values::class, 'getNull'], ''); | ||
} | ||
} |
Oops, something went wrong.