diff --git a/controllers/ManageAlbums.php b/controllers/ManageAlbums.php
index 662714b..248e369 100644
--- a/controllers/ManageAlbums.php
+++ b/controllers/ManageAlbums.php
@@ -35,18 +35,14 @@ class ManageAlbums extends HTMLController
'tag' => [
'header' => 'Album',
'is_sortable' => true,
- 'parse' => [
- 'link' => BASEURL . '/editalbum/?id={ID_TAG}',
- 'data' => 'tag',
- ],
+ 'link' => BASEURL . '/editalbum/?id={ID_TAG}',
+ 'value' => 'tag',
],
'slug' => [
'header' => 'Slug',
'is_sortable' => true,
- 'parse' => [
- 'link' => BASEURL . '/editalbum/?id={ID_TAG}',
- 'data' => 'slug',
- ],
+ 'link' => BASEURL . '/editalbum/?id={ID_TAG}',
+ 'value' => 'slug',
],
'count' => [
'header' => '# Photos',
@@ -54,30 +50,21 @@ class ManageAlbums extends HTMLController
'value' => 'count',
],
],
- 'start' => !empty($_GET['start']) ? (int) $_GET['start'] : 0,
- 'sort_order' => !empty($_GET['order']) ? $_GET['order'] : null,
- 'sort_direction' => !empty($_GET['dir']) ? $_GET['dir'] : null,
+ 'default_sort_order' => 'tag',
+ 'default_sort_direction' => 'up',
+ 'start' => $_GET['start'] ?? 0,
+ 'sort_order' => $_GET['order'] ?? '',
+ 'sort_direction' => $_GET['dir'] ?? '',
'title' => 'Manage albums',
'no_items_label' => 'No albums meet the requirements of the current filter.',
'items_per_page' => 9999,
'index_class' => 'col-md-6',
'base_url' => BASEURL . '/managealbums/',
- 'get_data' => function($offset = 0, $limit = 9999, $order = '', $direction = 'up') {
- if (!in_array($order, ['id_tag', 'tag', 'slug', 'count']))
- $order = 'tag';
- if (!in_array($direction, ['up', 'down']))
- $direction = 'up';
-
- $rows = PhotoAlbum::getHierarchy($order, $direction);
-
- return [
- 'rows' => $rows,
- 'order' => $order,
- 'direction' => ($direction == 'up' ? 'up' : 'down'),
- ];
+ 'get_data' => function($offset, $limit, $order, $direction) {
+ return Tag::getOffset($offset, $limit, $order, $direction, true);
},
'get_count' => function() {
- return 9999;
+ return Tag::getCount(false, 'Album', true);
}
];
diff --git a/controllers/ManageAssets.php b/controllers/ManageAssets.php
index 5ae3597..59895ff 100644
--- a/controllers/ManageAssets.php
+++ b/controllers/ManageAssets.php
@@ -38,40 +38,33 @@ class ManageAssets extends HTMLController
'checkbox' => [
'header' => '',
'is_sortable' => false,
- 'parse' => [
- 'type' => 'function',
- 'data' => function($row) {
- return '';
- },
- ],
+ 'format' => fn($row) =>
+ '',
],
'thumbnail' => [
'header' => ' ',
'is_sortable' => false,
'cell_class' => 'text-center',
- 'parse' => [
- 'type' => 'function',
- 'data' => function($row) {
- $asset = Image::byRow($row);
- $width = $height = 65;
- if ($asset->isImage())
- {
- if ($asset->isPortrait())
- $width = null;
- else
- $height = null;
-
- $thumb = $asset->getThumbnailUrl($width, $height);
- }
+ 'format' => function($row) {
+ $asset = Image::byRow($row);
+ $width = $height = 65;
+ if ($asset->isImage())
+ {
+ if ($asset->isPortrait())
+ $width = null;
else
- $thumb = BASEURL . '/images/nothumb.svg';
+ $height = null;
- $width = isset($width) ? $width . 'px' : 'auto';
- $height = isset($height) ? $height . 'px' : 'auto';
+ $thumb = $asset->getThumbnailUrl($width, $height);
+ }
+ else
+ $thumb = BASEURL . '/images/nothumb.svg';
- return sprintf('', $thumb, $width, $height);
- },
- ],
+ $width = isset($width) ? $width . 'px' : 'auto';
+ $height = isset($height) ? $height . 'px' : 'auto';
+
+ return sprintf('', $thumb, $width, $height);
+ },
],
'id_asset' => [
'value' => 'id_asset',
@@ -87,72 +80,42 @@ class ManageAssets extends HTMLController
'value' => 'filename',
'header' => 'Filename',
'is_sortable' => true,
- 'parse' => [
- 'type' => 'value',
- 'link' => BASEURL . '/editasset/?id={ID_ASSET}',
- 'data' => 'filename',
- ],
+ 'link' => BASEURL . '/editasset/?id={ID_ASSET}',
+ 'value' => 'filename',
],
'id_user_uploaded' => [
'header' => 'User uploaded',
'is_sortable' => true,
- 'parse' => [
- 'type' => 'function',
- 'data' => function($row) {
- if (!empty($row['id_user']))
- return sprintf('%s', BASEURL, $row['id_user'],
- $row['first_name'] . ' ' . $row['surname']);
- else
- return 'n/a';
- },
- ],
+ 'format' => function($row) {
+ if (!empty($row['id_user']))
+ return sprintf('%s', BASEURL, $row['id_user'],
+ $row['first_name'] . ' ' . $row['surname']);
+ else
+ return 'n/a';
+ },
],
'dimensions' => [
'header' => 'Dimensions',
'is_sortable' => false,
- 'parse' => [
- 'type' => 'function',
- 'data' => function($row) {
- if (!empty($row['image_width']))
- return $row['image_width'] . ' x ' . $row['image_height'];
- else
- return 'n/a';
- },
- ],
+ 'format' => function($row) {
+ if (!empty($row['image_width']))
+ return $row['image_width'] . ' x ' . $row['image_height'];
+ else
+ return 'n/a';
+ },
],
],
- 'start' => !empty($_GET['start']) ? (int) $_GET['start'] : 0,
- 'sort_order' => !empty($_GET['order']) ? $_GET['order'] : '',
- 'sort_direction' => !empty($_GET['dir']) ? $_GET['dir'] : '',
+ 'default_sort_order' => 'id_asset',
+ 'default_sort_direction' => 'down',
+ 'start' => $_GET['start'] ?? 0,
+ 'sort_order' => $_GET['order'] ?? '',
+ 'sort_direction' => $_GET['dir'] ?? '',
'title' => 'Manage assets',
'no_items_label' => 'No assets meet the requirements of the current filter.',
'items_per_page' => 30,
'index_class' => 'col-md-6',
'base_url' => BASEURL . '/manageassets/',
- 'get_data' => function($offset = 0, $limit = 30, $order = '', $direction = 'down') {
- if (!in_array($order, ['id_asset', 'id_user_uploaded', 'title', 'subdir', 'filename']))
- $order = 'id_asset';
-
- $data = Registry::get('db')->queryAssocs('
- SELECT a.id_asset, a.subdir, a.filename,
- a.image_width, a.image_height, a.mimetype,
- u.id_user, u.first_name, u.surname
- FROM assets AS a
- LEFT JOIN users AS u ON a.id_user_uploaded = u.id_user
- ORDER BY {raw:order}
- LIMIT {int:offset}, {int:limit}',
- [
- 'order' => $order . ($direction == 'up' ? ' ASC' : ' DESC'),
- 'offset' => $offset,
- 'limit' => $limit,
- ]);
-
- return [
- 'rows' => $data,
- 'order' => $order,
- 'direction' => $direction,
- ];
- },
+ 'get_data' => 'Asset::getOffset',
'get_count' => 'Asset::getCount',
];
diff --git a/controllers/ManageErrors.php b/controllers/ManageErrors.php
index f510c7b..75186aa 100644
--- a/controllers/ManageErrors.php
+++ b/controllers/ManageErrors.php
@@ -14,8 +14,8 @@ class ManageErrors extends HTMLController
if (!Registry::get('user')->isAdmin())
throw new NotAllowedException();
- // Flushing, are we?
- if (isset($_POST['flush']) && Session::validateSession('get'))
+ // Clearing, are we?
+ if (isset($_POST['clear']) && Session::validateSession('get'))
{
ErrorLog::flush();
header('Location: ' . BASEURL . '/manageerrors/');
@@ -31,7 +31,7 @@ class ManageErrors extends HTMLController
'method' => 'post',
'class' => 'col-md-6 text-end',
'buttons' => [
- 'flush' => [
+ 'clear' => [
'type' => 'submit',
'caption' => 'Delete all',
'class' => 'btn-danger',
@@ -39,26 +39,23 @@ class ManageErrors extends HTMLController
],
],
'columns' => [
- 'id' => [
+ 'id_entry' => [
'value' => 'id_entry',
'header' => '#',
'is_sortable' => true,
],
'message' => [
- 'parse' => [
- 'type' => 'function',
- 'data' => function($row) {
- return $row['message'] . '
' .
- '
' .
- '' .
- htmlspecialchars($row['request_uri']) . '';
- }
- ],
'header' => 'Message / URL',
'is_sortable' => false,
+ 'format' => function($row) {
+ return $row['message'] . '
' .
+ '' .
+ '' .
+ htmlspecialchars($row['request_uri']) . '';
+ },
],
'file' => [
'value' => 'file',
@@ -71,12 +68,10 @@ class ManageErrors extends HTMLController
'is_sortable' => true,
],
'time' => [
- 'parse' => [
+ 'format' => [
'type' => 'timestamp',
- 'data' => [
- 'timestamp' => 'time',
- 'pattern' => 'long',
- ],
+ 'pattern' => 'long',
+ 'value' => 'time',
],
'header' => 'Time',
'is_sortable' => true,
@@ -89,41 +84,21 @@ class ManageErrors extends HTMLController
'uid' => [
'header' => 'UID',
'is_sortable' => true,
- 'parse' => [
- 'link' => BASEURL . '/edituser/?id={ID_USER}',
- 'data' => 'id_user',
- ],
+ 'link' => BASEURL . '/edituser/?id={ID_USER}',
+ 'value' => 'id_user',
],
],
- 'start' => !empty($_GET['start']) ? (int) $_GET['start'] : 0,
- 'sort_order' => !empty($_GET['order']) ? $_GET['order'] : '',
- 'sort_direction' => !empty($_GET['dir']) ? $_GET['dir'] : '',
+ 'default_sort_order' => 'id_entry',
+ 'default_sort_direction' => 'down',
+ 'start' => $_GET['start'] ?? 0,
+ 'sort_order' => $_GET['order'] ?? '',
+ 'sort_direction' => $_GET['dir'] ?? '',
'no_items_label' => "No errors to display -- we're all good!",
'items_per_page' => 20,
'index_class' => 'col-md-6',
'base_url' => BASEURL . '/manageerrors/',
'get_count' => 'ErrorLog::getCount',
- 'get_data' => function($offset = 0, $limit = 20, $order = '', $direction = 'down') {
- if (!in_array($order, ['id_entry', 'file', 'line', 'time', 'ipaddress', 'id_user']))
- $order = 'id_entry';
-
- $data = Registry::get('db')->queryAssocs('
- SELECT *
- FROM log_errors
- ORDER BY {raw:order}
- LIMIT {int:offset}, {int:limit}',
- [
- 'order' => $order . ($direction === 'up' ? ' ASC' : ' DESC'),
- 'offset' => $offset,
- 'limit' => $limit,
- ]);
-
- return [
- 'rows' => $data,
- 'order' => $order,
- 'direction' => $direction,
- ];
- },
+ 'get_data' => 'ErrorLog::getOffset',
];
$error_log = new GenericTable($options);
diff --git a/controllers/ManageTags.php b/controllers/ManageTags.php
index ec12de0..9dc653b 100644
--- a/controllers/ManageTags.php
+++ b/controllers/ManageTags.php
@@ -37,32 +37,25 @@ class ManageTags extends HTMLController
'tag' => [
'header' => 'Tag',
'is_sortable' => true,
- 'parse' => [
- 'link' => BASEURL . '/edittag/?id={ID_TAG}',
- 'data' => 'tag',
- ],
+ 'link' => BASEURL . '/edittag/?id={ID_TAG}',
+ 'value' => 'tag',
],
'slug' => [
'header' => 'Slug',
'is_sortable' => true,
- 'parse' => [
- 'link' => BASEURL . '/edittag/?id={ID_TAG}',
- 'data' => 'slug',
- ],
+ 'link' => BASEURL . '/edittag/?id={ID_TAG}',
+ 'value' => 'slug',
],
'id_user_owner' => [
'header' => 'Owning user',
'is_sortable' => true,
- 'parse' => [
- 'type' => 'function',
- 'data' => function($row) {
- if (!empty($row['id_user']))
- return sprintf('%s', BASEURL, $row['id_user'],
- $row['first_name'] . ' ' . $row['surname']);
- else
- return 'n/a';
- },
- ],
+ 'format' => function($row) {
+ if (!empty($row['id_user']))
+ return sprintf('%s', BASEURL, $row['id_user'],
+ $row['first_name'] . ' ' . $row['surname']);
+ else
+ return 'n/a';
+ },
],
'count' => [
'header' => 'Cardinality',
@@ -70,46 +63,21 @@ class ManageTags extends HTMLController
'value' => 'count',
],
],
- 'start' => !empty($_GET['start']) ? (int) $_GET['start'] : 0,
- 'sort_order' => !empty($_GET['order']) ? $_GET['order'] : null,
- 'sort_direction' => !empty($_GET['dir']) ? $_GET['dir'] : null,
+ 'default_sort_order' => 'tag',
+ 'default_sort_direction' => 'up',
+ 'start' => $_GET['start'] ?? 0,
+ 'sort_order' => $_GET['order'] ?? '',
+ 'sort_direction' => $_GET['dir'] ?? '',
'title' => 'Manage tags',
'no_items_label' => 'No tags meet the requirements of the current filter.',
'items_per_page' => 30,
'index_class' => 'col-md-6',
'base_url' => BASEURL . '/managetags/',
- 'get_data' => function($offset = 0, $limit = 30, $order = '', $direction = 'up') {
- if (!in_array($order, ['id_tag', 'tag', 'slug', 'kind', 'count']))
- $order = 'tag';
- if (!in_array($direction, ['up', 'down']))
- $direction = 'up';
-
- $data = Registry::get('db')->queryAssocs('
- SELECT t.*, u.id_user, u.first_name, u.surname
- FROM tags AS t
- LEFT JOIN users AS u ON t.id_user_owner = u.id_user
- WHERE kind != {string:album}
- ORDER BY {raw:order}
- LIMIT {int:offset}, {int:limit}',
- [
- 'order' => $order . ($direction == 'up' ? ' ASC' : ' DESC'),
- 'offset' => $offset,
- 'limit' => $limit,
- 'album' => 'Album',
- ]);
-
- return [
- 'rows' => $data,
- 'order' => $order,
- 'direction' => ($direction == 'up' ? 'up' : 'down'),
- ];
+ 'get_data' => function($offset, $limit, $order, $direction) {
+ return Tag::getOffset($offset, $limit, $order, $direction, false);
},
'get_count' => function() {
- return Registry::get('db')->queryValue('
- SELECT COUNT(*)
- FROM tags
- WHERE kind != {string:album}',
- ['album' => 'Album']);
+ return Tag::getCount(false, null, false);
}
];
diff --git a/controllers/ManageUsers.php b/controllers/ManageUsers.php
index 1be66a5..8473596 100644
--- a/controllers/ManageUsers.php
+++ b/controllers/ManageUsers.php
@@ -37,26 +37,20 @@ class ManageUsers extends HTMLController
'surname' => [
'header' => 'Last name',
'is_sortable' => true,
- 'parse' => [
- 'link' => BASEURL . '/edituser/?id={ID_USER}',
- 'data' => 'surname',
- ],
+ 'link' => BASEURL . '/edituser/?id={ID_USER}',
+ 'value' => 'surname',
],
'first_name' => [
'header' => 'First name',
'is_sortable' => true,
- 'parse' => [
- 'link' => BASEURL . '/edituser/?id={ID_USER}',
- 'data' => 'first_name',
- ],
+ 'link' => BASEURL . '/edituser/?id={ID_USER}',
+ 'value' => 'first_name',
],
'slug' => [
'header' => 'Slug',
'is_sortable' => true,
- 'parse' => [
- 'link' => BASEURL . '/edituser/?id={ID_USER}',
- 'data' => 'slug',
- ],
+ 'link' => BASEURL . '/edituser/?id={ID_USER}',
+ 'value' => 'slug',
],
'emailaddress' => [
'value' => 'emailaddress',
@@ -64,12 +58,10 @@ class ManageUsers extends HTMLController
'is_sortable' => true,
],
'last_action_time' => [
- 'parse' => [
+ 'format' => [
'type' => 'timestamp',
- 'data' => [
- 'timestamp' => 'last_action_time',
- 'pattern' => 'long',
- ],
+ 'pattern' => 'long',
+ 'value' => 'last_action_time',
],
'header' => 'Last activity',
'is_sortable' => true,
@@ -82,48 +74,21 @@ class ManageUsers extends HTMLController
'is_admin' => [
'is_sortable' => true,
'header' => 'Admin?',
- 'parse' => [
- 'type' => 'function',
- 'data' => function($row) {
- return $row['is_admin'] ? 'yes' : 'no';
- }
- ],
+ 'format' => fn($row) => $row['is_admin'] ? 'yes' : 'no',
],
],
- 'start' => !empty($_GET['start']) ? (int) $_GET['start'] : 0,
- 'sort_order' => !empty($_GET['order']) ? $_GET['order'] : '',
- 'sort_direction' => !empty($_GET['dir']) ? $_GET['dir'] : '',
+ 'default_sort_order' => 'id_user',
+ 'default_sort_direction' => 'down',
+ 'start' => $_GET['start'] ?? 0,
+ 'sort_order' => $_GET['order'] ?? '',
+ 'sort_direction' => $_GET['dir'] ?? '',
'title' => 'Manage users',
'no_items_label' => 'No users meet the requirements of the current filter.',
'items_per_page' => 30,
'index_class' => 'col-md-6',
'base_url' => BASEURL . '/manageusers/',
- 'get_data' => function($offset = 0, $limit = 30, $order = '', $direction = 'down') {
- if (!in_array($order, ['id_user', 'surname', 'first_name', 'slug', 'emailaddress', 'last_action_time', 'ip_address', 'is_admin']))
- $order = 'id_user';
-
- $data = Registry::get('db')->queryAssocs('
- SELECT *
- FROM users
- ORDER BY {raw:order}
- LIMIT {int:offset}, {int:limit}',
- [
- 'order' => $order . ($direction == 'up' ? ' ASC' : ' DESC'),
- 'offset' => $offset,
- 'limit' => $limit,
- ]);
-
- return [
- 'rows' => $data,
- 'order' => $order,
- 'direction' => $direction,
- ];
- },
- 'get_count' => function() {
- return Registry::get('db')->queryValue('
- SELECT COUNT(*)
- FROM users');
- }
+ 'get_data' => 'Member::getOffset',
+ 'get_count' => 'Member::getCount',
];
$table = new GenericTable($options);
diff --git a/models/Asset.php b/models/Asset.php
index edadd5f..b3803bf 100644
--- a/models/Asset.php
+++ b/models/Asset.php
@@ -680,6 +680,23 @@ class Asset
FROM assets');
}
+ public static function getOffset($offset, $limit, $order, $direction)
+ {
+ return Registry::get('db')->queryAssocs('
+ SELECT a.id_asset, a.subdir, a.filename,
+ a.image_width, a.image_height, a.mimetype,
+ u.id_user, u.first_name, u.surname
+ FROM assets AS a
+ LEFT JOIN users AS u ON a.id_user_uploaded = u.id_user
+ ORDER BY {raw:order}
+ LIMIT {int:offset}, {int:limit}',
+ [
+ 'order' => $order . ($direction == 'up' ? ' ASC' : ' DESC'),
+ 'offset' => $offset,
+ 'limit' => $limit,
+ ]);
+ }
+
public function save()
{
if (empty($this->id_asset))
diff --git a/models/ErrorLog.php b/models/ErrorLog.php
index 0f13d5b..f406ae3 100644
--- a/models/ErrorLog.php
+++ b/models/ErrorLog.php
@@ -24,7 +24,7 @@ class ErrorLog
public static function flush()
{
- return Registry::get('db')->query('TRUNCATE log_errors');
+ return Registry::get('db')->query('DELETE FROM log_errors');
}
public static function getCount()
@@ -33,4 +33,20 @@ class ErrorLog
SELECT COUNT(*)
FROM log_errors');
}
+
+ public static function getOffset($offset, $limit, $order, $direction)
+ {
+ assert(in_array($order, ['id_entry', 'file', 'line', 'time', 'ipaddress', 'id_user']));
+
+ return Registry::get('db')->queryAssocs('
+ SELECT *
+ FROM log_errors
+ ORDER BY {raw:order}
+ LIMIT {int:offset}, {int:limit}',
+ [
+ 'order' => $order . ($direction === 'up' ? ' ASC' : ' DESC'),
+ 'offset' => $offset,
+ 'limit' => $limit,
+ ]);
+ }
}
diff --git a/models/GenericTable.php b/models/GenericTable.php
index 415ef53..ec76a1a 100644
--- a/models/GenericTable.php
+++ b/models/GenericTable.php
@@ -15,7 +15,6 @@ class GenericTable
private $title;
private $title_class;
- private $tableIsSortable = false;
public $form_above;
public $form_below;
@@ -29,58 +28,22 @@ class GenericTable
public function __construct($options)
{
- // Make sure we're actually sorting on something sortable.
- if (!isset($options['sort_order']) || (!empty($options['sort_order']) && empty($options['columns'][$options['sort_order']]['is_sortable'])))
- $options['sort_order'] = '';
+ $this->initOrder($options);
+ $this->initPagination($options);
- // Order in which direction?
- if (!empty($options['sort_direction']) && !in_array($options['sort_direction'], ['up', 'down']))
- $options['sort_direction'] = 'up';
-
- // Make sure we know whether we can actually sort on something.
- $this->tableIsSortable = !empty($options['base_url']);
-
- // How much data do we have?
- $this->recordCount = $options['get_count'](...(!empty($options['get_count_params']) ? $options['get_count_params'] : []));
-
- // How much data do we need to retrieve?
- $this->items_per_page = !empty($options['items_per_page']) ? $options['items_per_page'] : 30;
-
- // Figure out where to start.
- $this->start = empty($options['start']) || !is_numeric($options['start']) || $options['start'] < 0 || $options['start'] > $this->recordCount ? 0 : $options['start'];
-
- // Figure out where we are on the whole, too.
- $numPages = max(1, ceil($this->recordCount / $this->items_per_page));
- $this->currentPage = min(ceil($this->start / $this->items_per_page) + 1, $numPages);
-
- // Let's bear a few things in mind...
- $this->base_url = $options['base_url'];
-
- // Gather parameters for the data gather function first.
- $parameters = [$this->start, $this->items_per_page, $options['sort_order'], $options['sort_direction']];
- if (!empty($options['get_data_params']) && is_array($options['get_data_params']))
- $parameters = array_merge($parameters, $options['get_data_params']);
-
- // Okay, let's fetch the data!
- $data = $options['get_data'](...$parameters);
-
- // Extract data into local variables.
- $rawRowData = $data['rows'];
- $this->sort_order = $data['order'];
- $this->sort_direction = $data['direction'];
- unset($data);
+ $data = $options['get_data']($this->start, $this->items_per_page,
+ $this->sort_order, $this->sort_direction);
// Okay, now for the column headers...
$this->generateColumnHeaders($options);
// Should we create a page index?
- $needsPageIndex = !empty($this->items_per_page) && $this->recordCount > $this->items_per_page;
- if ($needsPageIndex)
+ if ($this->recordCount > $this->items_per_page)
$this->generatePageIndex($options);
// Process the data to be shown into rows.
- if (!empty($rawRowData))
- $this->processAllRows($rawRowData, $options);
+ if (!empty($data))
+ $this->processAllRows($data, $options);
else
$this->body = $options['no_items_label'] ?? '';
@@ -95,6 +58,38 @@ class GenericTable
$this->form_below = $options['form_below'] ?? $options['form'] ?? null;
}
+ private function initOrder($options)
+ {
+ assert(isset($options['default_sort_order']));
+ assert(isset($options['default_sort_direction']));
+
+ // Validate sort order (column)
+ $this->sort_order = $options['sort_order'];
+ if (empty($this->sort_order) || empty($options['columns'][$this->sort_order]['is_sortable']))
+ $this->sort_order = $options['default_sort_order'];
+
+ // Validate sort direction
+ $this->sort_direction = $options['sort_direction'];
+ if (empty($this->sort_direction) || !in_array($this->sort_direction, ['up', 'down']))
+ $this->sort_direction = $options['default_sort_direction'];
+ }
+
+ private function initPagination(array $options)
+ {
+ assert(isset($options['base_url']));
+ assert(isset($options['items_per_page']));
+
+ $this->base_url = $options['base_url'];
+
+ $this->recordCount = $options['get_count']();
+ $this->items_per_page = !empty($options['items_per_page']) ? $options['items_per_page'] : 30;
+
+ $this->start = empty($options['start']) || !is_numeric($options['start']) || $options['start'] < 0 || $options['start'] > $this->recordCount ? 0 : $options['start'];
+
+ $numPages = max(1, ceil($this->recordCount / $this->items_per_page));
+ $this->currentPage = min(ceil($this->start / $this->items_per_page) + 1, $numPages);
+ }
+
private function generateColumnHeaders($options)
{
foreach ($options['columns'] as $key => $column)
@@ -102,14 +97,14 @@ class GenericTable
if (empty($column['header']))
continue;
- $isSortable = $this->tableIsSortable && !empty($column['is_sortable']);
+ $isSortable = !empty($column['is_sortable']);
$sortDirection = $key == $this->sort_order && $this->sort_direction === 'up' ? 'down' : 'up';
$header = [
'class' => isset($column['class']) ? $column['class'] : '',
'cell_class' => isset($column['cell_class']) ? $column['cell_class'] : null,
'colspan' => !empty($column['header_colspan']) ? $column['header_colspan'] : 1,
- 'href' => $isSortable ? $this->getLink($this->start, $key, $sortDirection) : null,
+ 'href' => $isSortable ? $this->getHeaderLink($this->start, $key, $sortDirection) : null,
'label' => $column['header'],
'scope' => 'col',
'sort_mode' => $key == $this->sort_order ? $this->sort_direction : null,
@@ -126,7 +121,7 @@ class GenericTable
'base_url' => $this->base_url,
'index_class' => $options['index_class'] ?? '',
'items_per_page' => $this->items_per_page,
- 'linkBuilder' => [$this, 'getLink'],
+ 'linkBuilder' => [$this, 'getHeaderLink'],
'recordCount' => $this->recordCount,
'sort_direction' => $this->sort_direction,
'sort_order' => $this->sort_order,
@@ -134,7 +129,7 @@ class GenericTable
]);
}
- public function getLink($start = null, $order = null, $dir = null)
+ public function getHeaderLink($start = null, $order = null, $dir = null)
{
if ($start === null)
$start = $this->start;
@@ -196,12 +191,18 @@ class GenericTable
foreach ($options['columns'] as $column)
{
- // Process data for this particular cell.
- if (isset($column['parse']))
- $value = self::processCell($column['parse'], $row);
+ // Process formatting
+ if (isset($column['format']) && is_callable($column['format']))
+ $value = $column['format']($row);
+ elseif (isset($column['format']))
+ $value = self::processFormatting($column['format'], $row);
else
$value = $row[$column['value']];
+ // Turn value into a link?
+ if (!empty($column['link']))
+ $value = $this->processLink($column['link'], $value, $row);
+
// Append the cell to the row.
$newRow['cells'][] = [
'class' => $column['cell_class'] ?? '',
@@ -214,68 +215,47 @@ class GenericTable
}
}
- private function processCell($options, $rowData)
+ private function processFormatting($options, $rowData)
{
- if (!isset($options['type']))
- $options['type'] = 'value';
-
- // Parse the basic value first.
- switch ($options['type'])
+ if ($options['type'] === 'timestamp')
{
- // Basic option: simply take a use a particular data property.
- case 'value':
- $value = htmlspecialchars($rowData[$options['data']]);
- break;
+ if (empty($options['pattern']) || $options['pattern'] === 'long')
+ $pattern = 'Y-m-d H:i';
+ elseif ($options['pattern'] === 'short')
+ $pattern = 'Y-m-d';
+ else
+ $pattern = $options['pattern'];
- // Processing via a lambda function.
- case 'function':
- $value = $options['data']($rowData);
- break;
+ assert(isset($rowData[$options['value']]));
+ if (!is_numeric($rowData[$options['value']]))
+ $timestamp = strtotime($rowData[$options['value']]);
+ else
+ $timestamp = (int) $rowData[$options['value']];
- // Using sprintf to fill out a particular pattern.
- case 'sprintf':
- $parameters = [$options['data']['pattern']];
- foreach ($options['data']['arguments'] as $identifier)
- $parameters[] = $rowData[$identifier];
+ if (isset($options['if_null']) && $timestamp == 0)
+ $value = $options['if_null'];
+ else
+ $value = date($pattern, $timestamp);
- $value = sprintf(...$parameters);
- break;
-
- // Timestamps get custom treatment.
- case 'timestamp':
- if (empty($options['data']['pattern']) || $options['data']['pattern'] === 'long')
- $pattern = 'Y-m-d H:i';
- elseif ($options['data']['pattern'] === 'short')
- $pattern = 'Y-m-d';
- else
- $pattern = $options['data']['pattern'];
-
- if (!isset($rowData[$options['data']['timestamp']]))
- $timestamp = 0;
- elseif (!is_numeric($rowData[$options['data']['timestamp']]))
- $timestamp = strtotime($rowData[$options['data']['timestamp']]);
- else
- $timestamp = (int) $rowData[$options['data']['timestamp']];
-
- if (isset($options['data']['if_null']) && $timestamp == 0)
- $value = $options['data']['if_null'];
- else
- $value = date($pattern, $timestamp);
- break;
+ return $value;
}
+ else
+ throw ValueError('Unexpected formatter type: ' . $options['type']);
+ }
- // Generate a link, if requested.
- if (!empty($options['link']))
- {
- // First, generate the replacement variables.
- $keys = array_keys($rowData);
- $values = array_values($rowData);
- foreach ($keys as $keyKey => $keyValue)
- $keys[$keyKey] = '{' . strtoupper($keyValue) . '}';
+ private function processLink($template, $value, array $rowData)
+ {
+ $href = $this->rowReplacements($template, $rowData);
+ return '' . $value . '';
+ }
- $value = '' . $value . '';
- }
+ private function rowReplacements($template, array $rowData)
+ {
+ $keys = array_keys($rowData);
+ $values = array_values($rowData);
+ foreach ($keys as $keyKey => $keyValue)
+ $keys[$keyKey] = '{' . strtoupper($keyValue) . '}';
- return $value;
+ return str_replace($keys, $values, $template);
}
}
diff --git a/models/Member.php b/models/Member.php
index 84912a4..0bb2ef5 100644
--- a/models/Member.php
+++ b/models/Member.php
@@ -187,6 +187,22 @@ class Member extends User
FROM users');
}
+ public static function getOffset($offset, $limit, $order, $direction)
+ {
+ assert(in_array($order, ['id_user', 'surname', 'first_name', 'slug', 'emailaddress', 'last_action_time', 'ip_address', 'is_admin']));
+
+ return Registry::get('db')->queryAssocs('
+ SELECT *
+ FROM users
+ ORDER BY {raw:order}
+ LIMIT {int:offset}, {int:limit}',
+ [
+ 'order' => $order . ($direction === 'up' ? ' ASC' : ' DESC'),
+ 'offset' => $offset,
+ 'limit' => $limit,
+ ]);
+ }
+
public function getProps()
{
// We should probably phase out the use of this function, or refactor the access levels of member properties...
diff --git a/models/PhotoAlbum.php b/models/PhotoAlbum.php
deleted file mode 100644
index efdc41f..0000000
--- a/models/PhotoAlbum.php
+++ /dev/null
@@ -1,76 +0,0 @@
-query('
- SELECT *
- FROM tags
- WHERE kind = {string:album}
- ORDER BY id_parent, {raw:order}',
- [
- 'order' => $order . ($direction == 'up' ? ' ASC' : ' DESC'),
- 'album' => 'Album',
- ]);
-
- $albums_by_parent = [];
- while ($row = $db->fetch_assoc($res))
- {
- if (!isset($albums_by_parent[$row['id_parent']]))
- $albums_by_parent[$row['id_parent']] = [];
-
- $albums_by_parent[$row['id_parent']][] = $row + ['children' => []];
- }
-
- $albums = self::getChildrenRecursively(0, 0, $albums_by_parent);
- $rows = self::flattenChildrenRecursively($albums);
-
- return $rows;
- }
-
- private static function getChildrenRecursively($id_parent, $level, &$albums_by_parent)
- {
- $children = [];
- if (!isset($albums_by_parent[$id_parent]))
- return $children;
-
- foreach ($albums_by_parent[$id_parent] as $child)
- {
- if (isset($albums_by_parent[$child['id_tag']]))
- $child['children'] = self::getChildrenRecursively($child['id_tag'], $level + 1, $albums_by_parent);
-
- $child['tag'] = ($level ? str_repeat('—', $level * 2) . ' ' : '') . $child['tag'];
- $children[] = $child;
- }
-
- return $children;
- }
-
- private static function flattenChildrenRecursively($albums)
- {
- if (empty($albums))
- return [];
-
- $rows = [];
- foreach ($albums as $album)
- {
- $rows[] = array_intersect_key($album, array_flip(['id_tag', 'tag', 'slug', 'count']));
- if (!empty($album['children']))
- {
- $children = self::flattenChildrenRecursively($album['children']);
- foreach ($children as $child)
- $rows[] = array_intersect_key($child, array_flip(['id_tag', 'tag', 'slug', 'count']));
- }
- }
-
- return $rows;
- }
-}
diff --git a/models/Tag.php b/models/Tag.php
index 145db1e..d5f6db7 100644
--- a/models/Tag.php
+++ b/models/Tag.php
@@ -24,6 +24,11 @@ class Tag
$this->$attribute = $value;
}
+ public function __toString()
+ {
+ return $this->tag;
+ }
+
public static function fromId($id_tag, $return_format = 'object')
{
$db = Registry::get('db');
@@ -409,27 +414,98 @@ class Tag
['tags' => $tags]);
}
- public static function getCount($only_active = 1, $kind = '')
+ public static function getCount($only_used = true, $kind = '', $isAlbum = false)
{
$where = [];
- if ($only_active)
+ if ($only_used)
$where[] = 'count > 0';
- if (!empty($kind))
- $where[] = 'kind = {string:kind}';
+ if (empty($kind))
+ $kind = 'Album';
- if (!empty($where))
- $where = 'WHERE ' . implode(' AND ', $where);
- else
- $where = '';
+ $where[] = 'kind {raw:operator} {string:kind}';
+ $where = implode(' AND ', $where);
return Registry::get('db')->queryValue('
SELECT COUNT(*)
- FROM tags ' . $where,
- ['kind' => $kind]);
+ FROM tags
+ WHERE ' . $where,
+ [
+ 'kind' => $kind,
+ 'operator' => $isAlbum ? '=' : '!=',
+ ]);
}
- public function __toString()
+ public static function getOffset($offset, $limit, $order, $direction, $isAlbum = false)
{
- return $this->tag;
+ assert(in_array($order, ['id_tag', 'tag', 'slug', 'count']));
+
+ $db = Registry::get('db');
+ $res = $db->query('
+ SELECT t.*, u.id_user, u.first_name, u.surname
+ FROM tags AS t
+ LEFT JOIN users AS u ON t.id_user_owner = u.id_user
+ WHERE kind {raw:operator} {string:album}
+ ORDER BY id_parent, {raw:order}
+ LIMIT {int:offset}, {int:limit}',
+ [
+ 'order' => $order . ($direction === 'up' ? ' ASC' : ' DESC'),
+ 'offset' => $offset,
+ 'limit' => $limit,
+ 'album' => 'Album',
+ 'operator' => $isAlbum ? '=' : '!=',
+ ]);
+
+ $albums_by_parent = [];
+ while ($row = $db->fetch_assoc($res))
+ {
+ if (!isset($albums_by_parent[$row['id_parent']]))
+ $albums_by_parent[$row['id_parent']] = [];
+
+ $albums_by_parent[$row['id_parent']][] = $row + ['children' => []];
+ }
+
+ $albums = self::getChildrenRecursively(0, 0, $albums_by_parent);
+ $rows = self::flattenChildrenRecursively($albums);
+
+ return $rows;
+ }
+
+ private static function getChildrenRecursively($id_parent, $level, &$albums_by_parent)
+ {
+ $children = [];
+ if (!isset($albums_by_parent[$id_parent]))
+ return $children;
+
+ foreach ($albums_by_parent[$id_parent] as $child)
+ {
+ if (isset($albums_by_parent[$child['id_tag']]))
+ $child['children'] = self::getChildrenRecursively($child['id_tag'], $level + 1, $albums_by_parent);
+
+ $child['tag'] = ($level ? str_repeat('—', $level * 2) . ' ' : '') . $child['tag'];
+ $children[] = $child;
+ }
+
+ return $children;
+ }
+
+ private static function flattenChildrenRecursively($albums)
+ {
+ if (empty($albums))
+ return [];
+
+ $rows = [];
+ foreach ($albums as $album)
+ {
+ static $headers_to_keep = ['id_tag', 'tag', 'slug', 'count', 'id_user', 'first_name', 'surname'];
+ $rows[] = array_intersect_key($album, array_flip($headers_to_keep));
+ if (!empty($album['children']))
+ {
+ $children = self::flattenChildrenRecursively($album['children']);
+ foreach ($children as $child)
+ $rows[] = array_intersect_key($child, array_flip($headers_to_keep));
+ }
+ }
+
+ return $rows;
}
}
diff --git a/templates/MainNavBar.php b/templates/MainNavBar.php
index 51ae4c1..7b7b5e4 100644
--- a/templates/MainNavBar.php
+++ b/templates/MainNavBar.php
@@ -33,7 +33,7 @@ class MainNavBar extends NavBar
';
- if (Registry::get('user')->isLoggedIn())
+ if (Registry::has('user') && Registry::get('user')->isLoggedIn())
{
echo '