From 7cfdf80a14064d4885ef0f113e1ee4ffc0ee16c9 Mon Sep 17 00:00:00 2001 From: envoyr Date: Fri, 25 Feb 2022 20:52:09 +0100 Subject: [PATCH] update listing and add pagination --- admin_admins.php | 10 +- admin_customers.php | 7 +- admin_domains.php | 7 +- admin_ipsandports.php | 7 +- admin_plans.php | 7 +- lib/Froxlor/UI/Collection.php | 19 +- lib/Froxlor/UI/Listing.php | 12 +- lib/Froxlor/UI/Pagination.php | 257 +++++------------- lib/Froxlor/UI/Paging.php | 1 + .../admin/tablelisting.domains.php | 2 + ssl_certificates.php | 7 +- templates/Froxlor/table/pagination.html.twig | 28 ++ templates/Froxlor/table/table.html.twig | 9 +- 13 files changed, 145 insertions(+), 228 deletions(-) create mode 100644 templates/Froxlor/table/pagination.html.twig diff --git a/admin_admins.php b/admin_admins.php index b22b7c21..72009a7f 100644 --- a/admin_admins.php +++ b/admin_admins.php @@ -32,19 +32,17 @@ if ($page == 'admins' && $userinfo['change_serversettings'] == '1') { if ($action == '') { $log->logAction(\Froxlor\FroxlorLogger::ADM_ACTION, LOG_NOTICE, "viewed admin_admins"); - $admin_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/admin/tablelisting.admins.php'; try { - // get filtered collection - $list = (new \Froxlor\UI\Collection(\Froxlor\Api\Commands\Admins::class, $userinfo)) - ->withPagination($admin_list_data['admin_list']['columns']) - ->getList(); + $admin_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/admin/tablelisting.admins.php'; + $collection = (new \Froxlor\UI\Collection(\Froxlor\Api\Commands\Admins::class, $userinfo)) + ->withPagination($admin_list_data['admin_list']['columns']); } catch (Exception $e) { \Froxlor\UI\Response::dynamic_error($e->getMessage()); } UI::twigBuffer('user/table.html.twig', [ - 'listing' => \Froxlor\UI\Listing::format($list, $admin_list_data['admin_list']), + 'listing' => \Froxlor\UI\Listing::format($collection, $admin_list_data['admin_list']), ]); UI::twigOutputBuffer(); } elseif ($action == 'su') { diff --git a/admin_customers.php b/admin_customers.php index a7b43f19..b32aac0c 100644 --- a/admin_customers.php +++ b/admin_customers.php @@ -34,16 +34,15 @@ if ($page == 'customers' && $userinfo['customers'] != '0') { try { $customer_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/admin/tablelisting.customers.php'; - $list = (new \Froxlor\UI\Collection(\Froxlor\Api\Commands\Customers::class, $userinfo)) + $collection = (new \Froxlor\UI\Collection(\Froxlor\Api\Commands\Customers::class, $userinfo)) ->has('admin', \Froxlor\Api\Commands\Admins::class, 'adminid', 'adminid') - ->withPagination($customer_list_data['customer_list']['columns']) - ->getList(); + ->withPagination($customer_list_data['customer_list']['columns']); } catch (Exception $e) { \Froxlor\UI\Response::dynamic_error($e->getMessage()); } UI::twigBuffer('user/table.html.twig', [ - 'listing' => \Froxlor\UI\Listing::format($list, $customer_list_data['customer_list']), + 'listing' => \Froxlor\UI\Listing::format($collection, $customer_list_data['customer_list']), ]); UI::twigOutputBuffer(); } elseif ($action == 'su' && $id != 0) { diff --git a/admin_domains.php b/admin_domains.php index e8d32190..3170667f 100644 --- a/admin_domains.php +++ b/admin_domains.php @@ -35,16 +35,15 @@ if ($page == 'domains' || $page == 'overview') { try { $domain_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/admin/tablelisting.domains.php'; - $list = (new \Froxlor\UI\Collection(\Froxlor\Api\Commands\Domains::class, $userinfo)) + $collection = (new \Froxlor\UI\Collection(\Froxlor\Api\Commands\Domains::class, $userinfo)) ->has('customer', \Froxlor\Api\Commands\Customers::class, 'customerid', 'customerid') - ->withPagination($domain_list_data['domain_list']['columns']) - ->getList(); + ->withPagination($domain_list_data['domain_list']['columns']); } catch (Exception $e) { \Froxlor\UI\Response::dynamic_error($e->getMessage()); } UI::twigBuffer('user/table.html.twig', [ - 'listing' => \Froxlor\UI\Listing::format($list, $domain_list_data['domain_list']), + 'listing' => \Froxlor\UI\Listing::format($collection, $domain_list_data['domain_list']), ]); UI::twigOutputBuffer(); } elseif ($action == 'delete' && $id != 0) { diff --git a/admin_ipsandports.php b/admin_ipsandports.php index 172806c6..8f641d29 100644 --- a/admin_ipsandports.php +++ b/admin_ipsandports.php @@ -33,15 +33,14 @@ if ($page == 'ipsandports' || $page == 'overview') { try { $ipsandports_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/admin/tablelisting.ipsandports.php'; - $list = (new \Froxlor\UI\Collection(\Froxlor\Api\Commands\IpsAndPorts::class, $userinfo)) - ->withPagination($ipsandports_list_data['ipsandports_list']['columns']) - ->getList(); + $collection = (new \Froxlor\UI\Collection(\Froxlor\Api\Commands\IpsAndPorts::class, $userinfo)) + ->withPagination($ipsandports_list_data['ipsandports_list']['columns']); } catch (Exception $e) { \Froxlor\UI\Response::dynamic_error($e->getMessage()); } UI::twigBuffer('user/table.html.twig', [ - 'listing' => \Froxlor\UI\Listing::format($list, $ipsandports_list_data['ipsandports_list']), + 'listing' => \Froxlor\UI\Listing::format($collection, $ipsandports_list_data['ipsandports_list']), ]); UI::twigOutputBuffer(); } elseif ($action == 'delete' && $id != 0) { diff --git a/admin_plans.php b/admin_plans.php index c856766e..23132af0 100644 --- a/admin_plans.php +++ b/admin_plans.php @@ -34,15 +34,14 @@ if ($page == '' || $page == 'overview') { try { $plan_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/admin/tablelisting.plans.php'; - $list = (new \Froxlor\UI\Collection(\Froxlor\Api\Commands\HostingPlans::class, $userinfo)) - ->withPagination($plan_list_data['plan_list']['columns']) - ->getList(); + $collection = (new \Froxlor\UI\Collection(\Froxlor\Api\Commands\HostingPlans::class, $userinfo)) + ->withPagination($plan_list_data['plan_list']['columns']); } catch (Exception $e) { \Froxlor\UI\Response::dynamic_error($e->getMessage()); } UI::twigBuffer('user/table.html.twig', [ - 'listing' => \Froxlor\UI\Listing::format($list, $plan_list_data['plan_list']), + 'listing' => \Froxlor\UI\Listing::format($collection, $plan_list_data['plan_list']), ]); UI::twigOutputBuffer(); } elseif ($action == 'delete' && $id != 0) { diff --git a/lib/Froxlor/UI/Collection.php b/lib/Froxlor/UI/Collection.php index 82f216c8..5c175a95 100644 --- a/lib/Froxlor/UI/Collection.php +++ b/lib/Froxlor/UI/Collection.php @@ -23,6 +23,7 @@ class Collection private array $has = []; private array $params; private array $userinfo; + private ?Pagination $pagination; public function __construct(string $class, array $userInfo, array $params = []) { @@ -60,6 +61,11 @@ class Collection } } + // attach pagination if available + if ($this->pagination) { + $result = array_merge($result, $this->pagination->getApiResponseParams()); + } + return $result; } @@ -93,10 +99,17 @@ class Collection public function withPagination(array $columns): Collection { - // TODO: handle 'sortable' => true in $columns + // Get only searchable columns + $sortableColumns = []; + foreach ($columns as $key => $column) { + if (isset($column['sortable']) && $column['sortable']) { + $sortableColumns[$key] = $column; + } + } - $pagination = new Pagination($this->userinfo, $columns, $this->count()); - $this->params = array_merge($this->params, $pagination->getApiCommandParams()); + // Prepare pagination + $this->pagination = new Pagination($this->userinfo, $sortableColumns, $this->count()); + $this->params = array_merge($this->params, $this->pagination->getApiCommandParams()); return $this; } diff --git a/lib/Froxlor/UI/Listing.php b/lib/Froxlor/UI/Listing.php index a6d3b200..6b1ee8fa 100644 --- a/lib/Froxlor/UI/Listing.php +++ b/lib/Froxlor/UI/Listing.php @@ -20,16 +20,18 @@ use Froxlor\UI\Panel\UI; */ class Listing { - public static function format(array $list, array $tabellisting): array + public static function format(Collection $collection, array $tabellisting): array { + $collection = $collection->get(); + return [ 'title' => $tabellisting['title'], 'icon' => $tabellisting['icon'], 'table' => [ 'th' => self::generateTableHeadings($tabellisting), - 'tr' => self::generateTableRows($list, $tabellisting), + 'tr' => self::generateTableRows($collection['data']['list'], $tabellisting), ], - 'pagination' => null, // TODO: write some logic + 'pagination' => $collection['pagination'], ]; } @@ -43,7 +45,7 @@ class Listing continue; } - $heading[] = [ + $heading[$visible_column] = [ 'text' => $tabellisting['columns'][$visible_column]['label'], 'class' => $tabellisting['columns'][$visible_column]['class'] ?? null, ]; @@ -51,7 +53,7 @@ class Listing // Table headings for actions if (isset($tabellisting['actions'])) { - $heading[] = [ + $heading['actions'] = [ 'text' => UI::getLng('panel.options'), 'class' => 'text-end', ]; diff --git a/lib/Froxlor/UI/Pagination.php b/lib/Froxlor/UI/Pagination.php index 91a1699d..65eae496 100644 --- a/lib/Froxlor/UI/Pagination.php +++ b/lib/Froxlor/UI/Pagination.php @@ -1,6 +1,8 @@ fields = $fields; $this->entries = $total_entries; + $this->perPage = $perPage; $this->pageno = 1; // add default limitation by settings - $this->addLimit(\Froxlor\Settings::Get('panel.paging')); + $this->addLimit(Settings::Get('panel.paging')); // check search request $this->searchtext = ''; if (count($fields) > 0) { @@ -91,23 +96,21 @@ class Pagination if (isset($_REQUEST['pageno']) && intval($_REQUEST['pageno']) != 0) { $this->pageno = intval($_REQUEST['pageno']); } - if (($this->pageno - 1) * \Froxlor\Settings::Get('panel.paging') > $this->entries) { + if (($this->pageno - 1) * Settings::Get('panel.paging') > $this->entries) { $this->pageno = 1; } - $this->addOffset(($this->pageno - 1) * \Froxlor\Settings::Get('panel.paging')); + $this->addOffset(($this->pageno - 1) * Settings::Get('panel.paging')); } /** * add a field for ordering * * @param string $field - * @param string $order - * optional, default 'ASC' - * - * @return \Froxlor\UI\Pagination + * @param string $order optional, default 'ASC' + * @return Pagination */ - public function addOrderBy($field = null, $order = 'ASC') - { + public function addOrderBy($field = null, $order = 'ASC'): Pagination + { if (! isset($this->data['sql_orderby'])) { $this->data['sql_orderby'] = array(); } @@ -118,42 +121,40 @@ class Pagination /** * add a limit * - * @param number $limit + * @param int $limit * optional, default 0 * - * @return \Froxlor\UI\Pagination + * @return Pagination */ - public function addLimit($limit = 0) - { - $this->data['sql_limit'] = (int) $limit; + public function addLimit(int $limit = 0): Pagination + { + $this->data['sql_limit'] = $limit; return $this; } /** * add an offset * - * @param number $offset - * optional, default 0 - * - * @return \Froxlor\UI\Pagination + * @param int $offset optional, default 0 + * @return Pagination */ - public function addOffset($offset = 0) - { - $this->data['sql_offset'] = (int) $offset; + public function addOffset(int $offset = 0): Pagination + { + $this->data['sql_offset'] = $offset; return $this; } - /** - * add a search operation - * - * @param string $searchtext - * @param string $field - * @param string $operator - * - * @return \Froxlor\UI\Pagination - */ - public function addSearch($searchtext = null, $field = null, $operator = null) - { + /** + * add a search operation + * + * @param string|null $searchtext + * @param string|null $field + * @param string|null $operator + * + * @return Pagination + */ + public function addSearch(string $searchtext = null, string $field = null, string $operator = null): Pagination + { if (! isset($this->data['sql_search'])) { $this->data['sql_search'] = array(); } @@ -175,150 +176,28 @@ class Pagination * * @return number */ - public function getEntries() - { + public function getEntries(): int + { return $this->entries; } - /** - * Returns html code for sorting field - * - * @param array $lng - * Language array - * @return string the html sortcode - */ - public function getHtmlSortCode($lng, $break = false) - { - $sortcode = ''; - $fieldoptions = ''; - $orderoptions = ''; - - foreach ($this->fields as $fieldname => $fieldcaption) { - $fieldoptions .= HTML::makeoption($fieldcaption, $fieldname, $this->sortfield, true, true); - } - - $breakorws = ($break ? '
' : ' '); - foreach (array( - 'asc' => $lng['panel']['ascending'], - 'desc' => $lng['panel']['descending'] - ) as $sortordertype => $sortorderdescription) { - $orderoptions .= HTML::makeoption($sortorderdescription, $sortordertype, $this->sortorder, true, true); - } - - eval("\$sortcode =\"" . Template::getTemplate("misc/htmlsortcode", '1') . "\";"); - return $sortcode; - } - - /** - * Returns html code for sorting arrows - * - * @param string $baseurl - * URL to use as base for links - * @param string $field - * If set, only this field will be returned - * - * @return mixed An array or a string (if field is set) of html code of arrows - */ - public function getHtmlArrowCode($baseurl, $field = '') - { - global $theme; - if ($field != '' && isset($this->fields[$field])) { - $baseurl = htmlspecialchars($baseurl); - $fieldname = htmlspecialchars($field); - eval("\$arrowcode =\"" . Template::getTemplate("misc/htmlarrowcode", '1') . "\";"); - } else { - $baseurl = htmlspecialchars($baseurl); - $arrowcode = array(); - foreach ($this->fields as $fieldname => $fieldcaption) { - $fieldname = htmlspecialchars($fieldname); - eval("\$arrowcode[\$fieldname] =\"" . Template::getTemplate("misc/htmlarrowcode", '1') . "\";"); - } - } - return $arrowcode; - } - - /** - * Returns html code for searching field - * - * @param array $lng - * Language array - * - * @return string the html searchcode - */ - public function getHtmlSearchCode($lng) - { - $searchcode = ''; - $fieldoptions = ''; - $searchtext = htmlspecialchars($this->searchtext); - foreach ($this->fields as $fieldname => $fieldcaption) { - $fieldoptions .= HTML::makeoption($fieldcaption, $fieldname, $this->searchfield, true, true); - } - eval("\$searchcode =\"" . Template::getTemplate("misc/htmlsearchcode", '1') . "\";"); - return $searchcode; - } - - /** - * Returns html code for paging - * - * @param string $baseurl - * URL to use as base for links - * - * @return string the html pagingcode - */ - public function getHtmlPagingCode($baseurl) - { - if (\Froxlor\Settings::Get('panel.paging') == 0 || $this->is_search) { - return ''; - } else { - $pages = intval($this->getEntries() / \Froxlor\Settings::Get('panel.paging')); - } - - if ($this->getEntries() % \Froxlor\Settings::Get('panel.paging') != 0) { - $pages ++; - } - - if ($pages > 1) { - - $start = $this->pageno - 4; - if ($start < 1) { - $start = 1; - } - - $stop = $this->pageno + 4; - if ($stop > $pages) { - $stop = $pages; - } - - // check for possible sorting values and keep it - $orderstr = ''; - if (!empty($this->sortfield)) { - $fieldname = htmlspecialchars($this->sortfield); - $orderstr .= '&sortfield=' . $fieldname . '&sortorder=' . $this->sortorder; - } - - $pagingcode = '« < '; - for ($i = $start; $i <= $stop; $i ++) { - if ($i != $this->pageno) { - $pagingcode .= ' ' . $i . ' '; - } else { - $pagingcode .= ' ' . $i . ' '; - } - } - $pagingcode .= ' > »'; - } else { - $pagingcode = ''; - } - - return $pagingcode; - } - - /** - * return parameter array for API command parameters $sql_search, $sql_limit, $sql_offset and $sql_orderby - * - * @return array - */ - public function getApiCommandParams() + public function getApiCommandParams(): array { return $this->data; } + + public function getApiResponseParams(): array + { + return [ + 'pagination' => [ + "total" => $this->entries, + "per_page" => $this->perPage, + "current_page" => $this->pageno, + "last_page" => ceil($this->entries / $this->perPage), + "from" => $this->data['sql_offset'] ?? null, + "to" => min($this->data['sql_offset'] + $this->perPage, $this->entries), + 'sortfields' => array_keys($this->fields), + ] + ]; + } } diff --git a/lib/Froxlor/UI/Paging.php b/lib/Froxlor/UI/Paging.php index 6b7e5ee7..0ea498c9 100644 --- a/lib/Froxlor/UI/Paging.php +++ b/lib/Froxlor/UI/Paging.php @@ -22,6 +22,7 @@ namespace Froxlor\UI; * Class to manage paging system * * @package Functions + * @deprecated */ class Paging { diff --git a/lib/tablelisting/admin/tablelisting.domains.php b/lib/tablelisting/admin/tablelisting.domains.php index fb54fdcc..602df146 100644 --- a/lib/tablelisting/admin/tablelisting.domains.php +++ b/lib/tablelisting/admin/tablelisting.domains.php @@ -27,6 +27,7 @@ return [ 'd.domain_ace' => [ 'label' => $lng['domains']['domainname'], 'column' => 'domain_ace', + 'sortable' => true, ], 'c.name' => [ 'label' => $lng['customer']['name'], @@ -44,6 +45,7 @@ return [ 'label' => $lng['login']['username'], 'column' => 'customer.loginname', 'format_callback' => [Impersonate::class, 'customer'], + 'sortable' => true, ], 'd.aliasdomain' => [ 'label' => $lng['domains']['aliasdomain'], diff --git a/ssl_certificates.php b/ssl_certificates.php index 62864a9f..aa97141d 100644 --- a/ssl_certificates.php +++ b/ssl_certificates.php @@ -51,14 +51,13 @@ $log->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, "viewed domains::ssl_cert try { $certificates_list_data = include_once dirname(__FILE__) . '/lib/tablelisting/admin/tablelisting.sslcertificates.php'; - $list = (new Collection(Certificates::class, $userinfo)) - ->withPagination($certificates_list_data['sslcertificates_list']['columns']) - ->getList(); + $collection = (new Collection(Certificates::class, $userinfo)) + ->withPagination($certificates_list_data['sslcertificates_list']['columns']); } catch (Exception $e) { Response::dynamic_error($e->getMessage()); } UI::twigBuffer('user/table.html.twig', [ - 'listing' => Listing::format($list, $certificates_list_data['sslcertificates_list']), + 'listing' => Listing::format($collection, $certificates_list_data['sslcertificates_list']), ]); UI::twigOutputBuffer(); diff --git a/templates/Froxlor/table/pagination.html.twig b/templates/Froxlor/table/pagination.html.twig new file mode 100644 index 00000000..c3a9b570 --- /dev/null +++ b/templates/Froxlor/table/pagination.html.twig @@ -0,0 +1,28 @@ +{% macro paging(pagination) %} + +{% endmacro %} + +{% macro titlesorting(pagination, key, th) %} + {% if pagination is defined and key in pagination.sortfields %} + + {{ th.text }} + + + + {% else %} + {{ th.text }} + {% endif %} +{% endmacro %} \ No newline at end of file diff --git a/templates/Froxlor/table/table.html.twig b/templates/Froxlor/table/table.html.twig index 157ee887..8a79212b 100644 --- a/templates/Froxlor/table/table.html.twig +++ b/templates/Froxlor/table/table.html.twig @@ -1,6 +1,7 @@ {% macro table(listing) %} {% import "Froxlor/table/callbacks.html.twig" as callbacks %} + {% import "Froxlor/table/pagination.html.twig" as pagination %}
{% if listing.title is not empty %} @@ -16,8 +17,8 @@ - {% for th in listing.table.th %} - + {% for key,th in listing.table.th %} + {{ pagination.titlesorting(listing.pagination, key, th) }} {% endfor %} @@ -50,9 +51,7 @@
{{ th.text }}
{% if listing.pagination is not empty %} - + {{ pagination.paging(listing.pagination) }} {% endif %}