-
Notifications
You must be signed in to change notification settings - Fork 344
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
UI Filters (Implementation) #1253
Changes from 88 commits
81d79cb
b243563
60e3e00
d3da70f
8d3a133
c2e3a55
fa9cf08
ac62423
e65b82c
679f9d4
12bb6c7
7b964cb
6980bc1
1a84294
8e2ba4d
c22dc23
98257d6
3e97cce
3d624d0
c6a76e2
e7dfba2
ee51ef2
f88d8ce
92e52c7
64303ce
1f94a72
8f683c9
7e862be
a4b6374
5fc92ae
f3ff476
d35d21d
3cd3e77
e1775cd
14dd515
da06741
bd7365c
fa50bc8
61dceeb
bd24bf3
ea195e5
51e956c
c2ea99d
1c71b4e
424b0da
c25355e
b51fc9e
4e953e7
ce0a9dd
5a8c21b
c1c688d
c2c1678
cba40b2
3b18241
a7bc273
d3dcb30
b1d21f0
bf9c1b6
26e9643
617fcec
863fc27
0123164
8790363
403bc91
085b554
35c32b8
e950746
239e5f6
a1c2a37
b8a6df1
e025a72
759c9e5
4083023
104dcc1
9bc415e
cd279da
5cda8e8
1a61065
efcd6f5
9896a1c
eb2bdc0
500848f
232d3f9
6dcaf7a
b3596c6
c266690
ed7bb0b
18a6a88
dfa2477
8ef5fd0
4f4d361
f91745b
77b6ba3
3a89a35
c0a07e0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# UI Filter Service | ||
|
||
The filter service wraps the KS element of Standard Input Filters. It's main purpose is to manage the state of the filter and it's inputs in the user session. | ||
|
||
The basics methods are `$DIC->uiService()->filter()->standard()` to get a filter instance and `$DIC->uiService()->filter()->getData($filter)` to retrieve the data from the filter. | ||
|
||
This service is most probably a temporary solution until session and similar dependencies are handled differently by the UI framework. | ||
|
||
``` | ||
|
||
class fooGUI { | ||
|
||
function showListAndFilter() { | ||
|
||
//Step 0: Declare dependencies | ||
global $DIC; | ||
$ui = $DIC->ui()->factory(); | ||
$renderer = $DIC->ui()->renderer(); | ||
|
||
//Step 1: Define some input fields to plug into the filter. | ||
$title_input = $ui->input()->field()->text("Title"); | ||
$select = $ui->input()->field()->select("Selection", ["one" => "One", "two" => "Two", "three" => "Three"]); | ||
$with_def = $ui->input()->field()->text("With Default")->withValue("Def.Value"); | ||
$init_hide = $ui->input()->field()->text("Hidden initially"); | ||
|
||
//Step 2: Define the filter and attach the inputs. | ||
$action = $DIC->ctrl()->getLinkTargetByClass("ilsystemstyledocumentationgui", "entries", "", true); | ||
$filter = $DIC->uiService()->filter()->standard("filter_ID", $action, [ | ||
"title" => $title_input, | ||
"select" => $select, | ||
"with_def" => $with_def, | ||
"init_hide" => $init_hide, | ||
], | ||
[true, true, true, false], true, true); | ||
|
||
//Step 3: Get filter data | ||
$filter_data = $DIC->uiService()->filter()->getData($filter); | ||
|
||
//Step 4: Render the filter | ||
return $renderer->render($filter)."Filter Data: ".print_r($filter_data, true); | ||
} | ||
|
||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thx for the example in the readme. |
||
|
||
|
||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
<?php | ||
|
||
/* Copyright (c) 1998-2018 ILIAS open source, Extended GPL, see docs/LICENSE */ | ||
|
||
/** | ||
* Request adapter for filter | ||
* | ||
* @author [email protected] | ||
* @ingroup ServicesUI | ||
*/ | ||
class ilUIFilterRequestAdapter | ||
{ | ||
const CMD_PARAMETER = "cmdFilter"; | ||
const RENDER_INPUT_BASE = "__filter_status_"; | ||
|
||
/** | ||
* @var \Psr\Http\Message\ServerRequestInterface | ||
*/ | ||
protected $request; | ||
|
||
/** | ||
* query params | ||
* @var array | ||
*/ | ||
protected $params; | ||
|
||
/** | ||
* post data | ||
* @var array|null | ||
*/ | ||
protected $post; | ||
|
||
/** | ||
* Constructor | ||
*/ | ||
public function __construct(\Psr\Http\Message\ServerRequestInterface $request) | ||
{ | ||
$this->request = $request; | ||
$this->params = $this->request->getQueryParams(); | ||
$this->post = $this->request->getParsedBody(); | ||
} | ||
|
||
/** | ||
* Get filter command | ||
* @return string | ||
*/ | ||
public function getFilterCmd(): string | ||
{ | ||
if (isset($this->params[self::CMD_PARAMETER])) | ||
{ | ||
return (string) $this->params[self::CMD_PARAMETER]; | ||
} | ||
return ""; | ||
} | ||
|
||
/** | ||
* Has an input field been rendered in current post request? | ||
* | ||
* @param $input_id | ||
* @return bool | ||
*/ | ||
public function isInputRendered($input_id): bool | ||
{ | ||
if (isset($this->params[self::RENDER_INPUT_BASE . $input_id]) && | ||
$this->params[self::RENDER_INPUT_BASE . $input_id] === "1") | ||
{ | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
/** | ||
* Get filter with request data | ||
* | ||
* @param \ILIAS\UI\Component\Input\Container\Filter\Standard $filter | ||
* @return \ILIAS\UI\Component\Input\Container\Filter\Standard | ||
*/ | ||
public function getFilterWithRequest(\ILIAS\UI\Component\Input\Container\Filter\Standard $filter): \ILIAS\UI\Component\Input\Container\Filter\Standard | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not using the "use" statement in the very top, would make this a bit more readable |
||
{ | ||
return $filter->withRequest($this->request); | ||
} | ||
|
||
/** | ||
* Get action for filter command | ||
* | ||
* @param string $base_action | ||
* @param string $filter_cmd | ||
* @return string | ||
*/ | ||
public function getAction(string $base_action, string $filter_cmd): string | ||
{ | ||
return $base_action."&".self::CMD_PARAMETER."=".$filter_cmd; | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,226 @@ | ||
<?php | ||
|
||
/* Copyright (c) 1998-2018 ILIAS open source, Extended GPL, see docs/LICENSE */ | ||
|
||
/** | ||
* Filter service. Wraps around KS filter container. | ||
* | ||
* @author [email protected] | ||
* @ingroup ServiceUI | ||
*/ | ||
class ilUIFilterService | ||
{ | ||
// command constants | ||
const CMD_TOGGLE_ON = "toggleOn"; | ||
const CMD_TOGGLE_OFF = "toggleOff"; | ||
const CMD_EXPAND = "expand"; | ||
const CMD_COLLAPSE = "collapse"; | ||
const CMD_APPLY = "apply"; | ||
const CMD_RESET = "reset"; | ||
|
||
|
||
/** | ||
* @var ilUIService | ||
*/ | ||
protected $service; | ||
|
||
/** | ||
* @var \ILIAS\DI\UIServices | ||
*/ | ||
protected $ui; | ||
|
||
/** | ||
* @var ilUIFilterServiceSessionGateway | ||
*/ | ||
protected $session; | ||
|
||
/** | ||
* @var ilUIFilterRequestAdapter | ||
*/ | ||
protected $request; | ||
|
||
/** | ||
* Constructor | ||
* @param ilUIService $service | ||
* @param ilUIServiceDependencies $deps | ||
*/ | ||
public function __construct(ilUIService $service, ilUIServiceDependencies $deps) | ||
{ | ||
$this->service = $service; | ||
$this->session = $deps->getSession(); | ||
$this->request = $deps->getRequest(); | ||
$this->ui = $deps->ui(); | ||
} | ||
|
||
|
||
/** | ||
* Get standard filter instance | ||
* | ||
* @param string $filter_id | ||
* @param string $base_action | ||
* @param ILIAS\UI\Component\Input\Field\FilterInput[] $inputs | ||
* @param bool[] $is_input_initially_rendered | ||
* @param bool $is_activated | ||
* @param bool $is_expanded | ||
* @return \ILIAS\UI\Component\Input\Container\Filter\Standard | ||
*/ | ||
public function standard($filter_id, $base_action, array $inputs, array $is_input_initially_rendered, | ||
$is_activated = false, $is_expanded = false): \ILIAS\UI\Component\Input\Container\Filter\Standard | ||
{ | ||
$ui = $this->ui->factory(); | ||
|
||
// write expand, activation, rendered inputs info to session | ||
$this->writeFilterStatusToSession($filter_id, $inputs); | ||
|
||
// handle the reset command | ||
$this->handleReset($filter_id); | ||
|
||
// determine activation/expand status | ||
$is_activated = $this->session->isActivated($filter_id, $is_activated); | ||
$is_expanded = $this->session->isExpanded($filter_id, $is_expanded); | ||
|
||
// put data from session into filter | ||
$inputs_with_session_data = []; | ||
$is_input_initially_rendered_with_session = []; | ||
foreach ($inputs as $input_id => $i) | ||
{ | ||
// rendering information | ||
$rendered = | ||
$this->session->isRendered($filter_id, $input_id, current($is_input_initially_rendered)); | ||
$is_input_initially_rendered_with_session[] = $rendered; | ||
next($is_input_initially_rendered); | ||
|
||
// values | ||
$val = $this->session->getValue($filter_id, $input_id); | ||
if ($rendered && !is_null($val)) | ||
{ | ||
$i = $i->withValue($val); | ||
} | ||
$inputs_with_session_data[$input_id] = $i; | ||
} | ||
|
||
// get the filter | ||
$filter = $ui->input()->container()->filter()->standard( | ||
$this->request->getAction($base_action, self::CMD_TOGGLE_ON), | ||
$this->request->getAction($base_action, self::CMD_TOGGLE_OFF), | ||
$this->request->getAction($base_action, self::CMD_EXPAND), | ||
$this->request->getAction($base_action, self::CMD_COLLAPSE), | ||
$this->request->getAction($base_action, self::CMD_APPLY), | ||
$this->request->getAction($base_action, self::CMD_RESET), | ||
$inputs_with_session_data, | ||
$is_input_initially_rendered_with_session, | ||
$is_activated, | ||
$is_expanded); | ||
|
||
// handle apply and collapse command | ||
$filter = $this->handleApplyAndCollapse($filter_id, $filter); | ||
|
||
return $filter; | ||
|
||
} | ||
|
||
/** | ||
* Get data | ||
* | ||
* @param \ILIAS\UI\Component\Input\Container\Filter\Standard $filter | ||
* @return array|null | ||
*/ | ||
public function getData(\ILIAS\UI\Component\Input\Container\Filter\Standard $filter) | ||
{ | ||
$result = null; | ||
if (in_array($this->request->getFilterCmd(), | ||
[self::CMD_APPLY, self::CMD_TOGGLE_ON, self::CMD_EXPAND, self::CMD_COLLAPSE]) && $filter->isActivated()) { | ||
$filter = $this->request->getFilterWithRequest($filter); | ||
$result = $filter->getData(); | ||
} | ||
return $result; | ||
} | ||
|
||
/** | ||
* Write filter status to session (filter activated/expanded, inputs being rendered or not) | ||
* @param string $filter_id | ||
* @param array $inputs | ||
*/ | ||
protected function writeFilterStatusToSession($filter_id, $inputs) | ||
{ | ||
if ($this->request->getFilterCmd() == self::CMD_TOGGLE_ON) { | ||
$this->session->writeActivated($filter_id, true); | ||
} | ||
|
||
if ($this->request->getFilterCmd() == self::CMD_TOGGLE_OFF) { | ||
$this->session->writeActivated($filter_id, false); | ||
} | ||
|
||
if ($this->request->getFilterCmd() == self::CMD_EXPAND) { | ||
$this->session->writeExpanded($filter_id, true); | ||
} | ||
|
||
if ($this->request->getFilterCmd() == self::CMD_COLLAPSE) { | ||
$this->handleRendering($filter_id, $inputs); | ||
$this->session->writeExpanded($filter_id, false); | ||
} | ||
|
||
if ($this->request->getFilterCmd() == self::CMD_APPLY) { | ||
$this->handleRendering($filter_id, $inputs); | ||
} | ||
} | ||
|
||
/** | ||
* Handle rendering of inputs to session | ||
* @param string $filter_id | ||
* @param array $inputs | ||
*/ | ||
protected function handleRendering($filter_id, $inputs) | ||
{ | ||
foreach ($inputs as $input_id => $i) | ||
{ | ||
if ($this->request->isInputRendered($input_id)) | ||
{ | ||
$this->session->writeRendered($filter_id, $input_id, true); | ||
} | ||
else | ||
{ | ||
$this->session->writeRendered($filter_id, $input_id, false); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Handle reset command | ||
* | ||
* @param string $filter_id | ||
*/ | ||
protected function handleReset(string $filter_id) | ||
{ | ||
// clear session, if reset is pressed | ||
if ($this->request->getFilterCmd() == self::CMD_RESET) | ||
{ | ||
$this->session->reset($filter_id); | ||
} | ||
} | ||
|
||
|
||
/** | ||
* Handle apply and collapse command | ||
* | ||
* @param string $filter_id | ||
* @param \ILIAS\UI\Component\Input\Container\Filter\Standard $filter | ||
* @return \ILIAS\UI\Component\Input\Container\Filter\Standard | ||
*/ | ||
protected function handleApplyAndCollapse(string $filter_id, \ILIAS\UI\Component\Input\Container\Filter\Standard $filter): \ILIAS\UI\Component\Input\Container\Filter\Standard | ||
{ | ||
if ((in_array($this->request->getFilterCmd(), | ||
[self::CMD_APPLY, self::CMD_COLLAPSE]))) | ||
{ | ||
$filter = $this->request->getFilterWithRequest($filter); | ||
foreach ($filter->getInputs() as $input_id => $i) | ||
{ | ||
$this->session->writeValue($filter_id, $input_id, $i->getValue()); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As far as I could follow the code the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I see. Would it be ok, to make the non numeric keys in the submitted array mandatory? Like in the example?
We could test for these keys. This would fix this issue for now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, there just are no guarantees here. This will work for now, but since the inputs are not meant to be read in this way this could well change. This is what I meant with "against the grain" in my other comment. |
||
} | ||
return $filter; | ||
} | ||
|
||
|
||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This won't render the values that the user entered imo. They are added from POST in
getData
, but the updated$filter
is never retrieved. Thus$filter
here only contains the values that where initially set to the fields.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean "won't" render? Did you try the example in the KS? You enter some data in the "Title" field, hit "Apply" (Do not use "Enter" key, this needs fixing) and the data is retrieved and displayed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just now noticed, that you effectively add the POST-data to the input twice, once in
handleApply
, once ingetData
. This was irritating but should work.