Refactor the GenericTables class #51
| @ -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); | ||||
| 			} | ||||
| 		]; | ||||
| 
 | ||||
|  | ||||
| @ -38,40 +38,33 @@ class ManageAssets extends HTMLController | ||||
| 				'checkbox' => [ | ||||
| 					'header' => '<input type="checkbox" id="selectall">', | ||||
| 					'is_sortable' => false, | ||||
| 					'parse' => [ | ||||
| 						'type' => 'function', | ||||
| 						'data' => function($row) { | ||||
| 							return '<input type="checkbox" class="asset_select" name="delete[]" value="' . $row['id_asset'] . '">'; | ||||
| 						}, | ||||
| 					], | ||||
| 					'format' => fn($row) => | ||||
| 						'<input type="checkbox" class="asset_select" name="delete[]" value="' . $row['id_asset'] . '">', | ||||
| 				], | ||||
| 				'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('<img src="%s" style="width: %s; height: %s;">', $thumb, $width, $height); | ||||
| 						}, | ||||
| 					], | ||||
| 						$width = isset($width) ? $width . 'px' : 'auto'; | ||||
| 						$height = isset($height) ? $height . 'px' : 'auto'; | ||||
| 
 | ||||
| 						return sprintf('<img src="%s" style="width: %s; height: %s;">', $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('<a href="%s/edituser/?id=%d">%s</a>', BASEURL, $row['id_user'], | ||||
| 									$row['first_name'] . ' ' . $row['surname']); | ||||
| 							else | ||||
| 								return 'n/a'; | ||||
| 						}, | ||||
| 					], | ||||
| 					'format' => function($row) { | ||||
| 						if (!empty($row['id_user'])) | ||||
| 							return sprintf('<a href="%s/edituser/?id=%d">%s</a>', 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', | ||||
| 		]; | ||||
| 
 | ||||
|  | ||||
| @ -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'] . '<br>' . | ||||
| 								'<div><a onclick="this.parentNode.childNodes[1].style.display=\'block\';this.style.display=\'none\';">Show debug info</a>' . | ||||
| 								'<pre style="display: none">' . htmlspecialchars($row['debug_info']) . | ||||
| 								'</pre></div>' . | ||||
| 								'<small><a href="' . BASEURL . | ||||
| 									htmlspecialchars($row['request_uri']) . '">' . | ||||
| 									htmlspecialchars($row['request_uri']) . '</a></small>'; | ||||
| 						} | ||||
| 					], | ||||
| 					'header' => 'Message / URL', | ||||
| 					'is_sortable' => false, | ||||
| 					'format' => function($row) { | ||||
| 						return $row['message'] . '<br>' . | ||||
| 							'<div><a onclick="this.parentNode.childNodes[1].style.display=\'block\';this.style.display=\'none\';">Show debug info</a>' . | ||||
| 							'<pre style="display: none">' . htmlspecialchars($row['debug_info']) . | ||||
| 							'</pre></div>' . | ||||
| 							'<small><a href="' . BASEURL . | ||||
| 								htmlspecialchars($row['request_uri']) . '">' . | ||||
| 								htmlspecialchars($row['request_uri']) . '</a></small>'; | ||||
| 					}, | ||||
| 				], | ||||
| 				'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); | ||||
|  | ||||
| @ -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('<a href="%s/edituser/?id=%d">%s</a>', BASEURL, $row['id_user'], | ||||
| 									$row['first_name'] . ' ' . $row['surname']); | ||||
| 							else | ||||
| 								return 'n/a'; | ||||
| 						}, | ||||
| 					], | ||||
| 					'format' => function($row) { | ||||
| 						if (!empty($row['id_user'])) | ||||
| 							return sprintf('<a href="%s/edituser/?id=%d">%s</a>', 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); | ||||
| 			} | ||||
| 		]; | ||||
| 
 | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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)) | ||||
|  | ||||
| @ -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, | ||||
| 			]); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -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 '<a href="' . $href . '">' . $value . '</a>'; | ||||
| 	} | ||||
| 
 | ||||
| 			$value = '<a href="' . str_replace($keys, $values, $options['link']) . '">' . $value . '</a>'; | ||||
| 		} | ||||
| 	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); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -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...
 | ||||
|  | ||||
| @ -1,76 +0,0 @@ | ||||
| <?php | ||||
| /***************************************************************************** | ||||
|  * PhotoAlbum.php | ||||
|  * Contains key class PhotoAlbum. | ||||
|  * | ||||
|  * Kabuki CMS (C) 2013-2015, Aaron van Geffen | ||||
|  *****************************************************************************/ | ||||
| 
 | ||||
| class PhotoAlbum extends Tag | ||||
| { | ||||
| 	public static function getHierarchy($order, $direction) | ||||
| 	{ | ||||
| 		$db = Registry::get('db'); | ||||
| 		$res = $db->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; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										100
									
								
								models/Tag.php
									
									
									
									
									
								
							
							
						
						
									
										100
									
								
								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; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -33,7 +33,7 @@ class MainNavBar extends NavBar | ||||
| 					<span class="navbar-toggler-icon"></span> | ||||
| 				</button>'; | ||||
| 
 | ||||
| 		if (Registry::get('user')->isLoggedIn()) | ||||
| 		if (Registry::has('user') && Registry::get('user')->isLoggedIn()) | ||||
| 		{ | ||||
| 			echo ' | ||||
| 				<div class="collapse navbar-collapse justify-content-end" id="', $this->innerMenuId, '"> | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user