forked from Public/pics
Initial commit.
This is to be the new HashRU website based on the Aaronweb.net/Kabuki CMS.
This commit is contained in:
159
controllers/EditAsset.php
Normal file
159
controllers/EditAsset.php
Normal file
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
/*****************************************************************************
|
||||
* EditAsset.php
|
||||
* Contains the asset management controller
|
||||
*
|
||||
* Kabuki CMS (C) 2013-2015, Aaron van Geffen
|
||||
*****************************************************************************/
|
||||
|
||||
class EditAsset extends HTMLController
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
// Ensure it's just admins at this point.
|
||||
if (!Registry::get('user')->isAdmin())
|
||||
throw new NotAllowedException();
|
||||
|
||||
if (empty($_GET['id']))
|
||||
throw new Exception('Invalid request.');
|
||||
|
||||
$asset = Asset::fromId($_GET['id']);
|
||||
if (empty($asset))
|
||||
throw new NotFoundException('Asset not found');
|
||||
|
||||
if (isset($_REQUEST['delete']))
|
||||
throw new Exception('Not implemented.');
|
||||
|
||||
if (!empty($_POST))
|
||||
{
|
||||
if (isset($_GET['updatethumb']))
|
||||
{
|
||||
$image = $asset->getImage();
|
||||
return $this->updateThumb($image);
|
||||
}
|
||||
|
||||
// Key info
|
||||
if (isset($_POST['title'], $_POST['date_captured'], $_POST['priority']))
|
||||
{
|
||||
$date_captured = !empty($_POST['date_captured']) ? new DateTime($_POST['date_captured']) : null;
|
||||
$asset->setKeyData(htmlentities($_POST['title']), $date_captured, intval($_POST['priority']));
|
||||
}
|
||||
|
||||
// Handle tags
|
||||
$new_tags = [];
|
||||
if (isset($_POST['tag']) && is_array($_POST['tag']))
|
||||
foreach ($_POST['tag'] as $id_tag => $bool)
|
||||
if (is_numeric($id_tag))
|
||||
$new_tags[] = $id_tag;
|
||||
|
||||
$current_tags = array_keys($asset->getTags());
|
||||
|
||||
$tags_to_unlink = array_diff($current_tags, $new_tags);
|
||||
$asset->unlinkTags($tags_to_unlink);
|
||||
|
||||
$tags_to_link = array_diff($new_tags, $current_tags);
|
||||
$asset->linkTags($tags_to_link);
|
||||
|
||||
// Meta data
|
||||
if (isset($_POST['meta_key'], $_POST['meta_value']))
|
||||
{
|
||||
$new_meta = array_filter(array_combine($_POST['meta_key'], $_POST['meta_value']), function($e) {
|
||||
return !empty($e);
|
||||
});
|
||||
|
||||
$asset->setMetaData($new_meta);
|
||||
}
|
||||
|
||||
// A replacement file?
|
||||
if (isset($_FILES['replacement'], $_POST['replacement_target']) && !empty($_FILES['replacement']['tmp_name']))
|
||||
{
|
||||
if ($_POST['replacement_target'] === 'full')
|
||||
{
|
||||
$asset->replaceFile($_FILES['replacement']['tmp_name']);
|
||||
if ($asset->isImage())
|
||||
{
|
||||
$image = $asset->getImage();
|
||||
$image->removeAllThumbnails();
|
||||
}
|
||||
}
|
||||
elseif (preg_match('~^thumb_(\d+)x(\d+)(_c[best]?)?$~', $_POST['replacement_target']))
|
||||
{
|
||||
$image = $asset->getImage();
|
||||
if (($replace_result = $image->replaceThumbnail($_POST['replacement_target'], $_FILES['replacement']['tmp_name'])) !== 0)
|
||||
throw new Exception('Could not replace thumbnail \'' . $_POST['replacement_target'] . '\' with the uploaded file. Error code: ' . $replace_result);
|
||||
}
|
||||
}
|
||||
|
||||
header('Location: ' . BASEURL . '/editasset/?id=' . $asset->getId());
|
||||
}
|
||||
|
||||
// Get list of thumbnails
|
||||
$thumbs = $this->getThumbs($asset);
|
||||
|
||||
$page = new EditAssetForm($asset, $thumbs);
|
||||
parent::__construct('Edit asset \'' . $asset->getTitle() . '\' (' . $asset->getFilename() . ') - ' . SITE_TITLE);
|
||||
$this->page->adopt($page);
|
||||
}
|
||||
|
||||
private function getThumbs(Asset $asset)
|
||||
{
|
||||
$path = $asset->getPath();
|
||||
$thumbs = [];
|
||||
$metadata = $asset->getMeta();
|
||||
foreach ($metadata as $key => $meta)
|
||||
{
|
||||
if (!preg_match('~^thumb_(?<width>\d+)x(?<height>\d+)(?<suffix>_c(?<method>[best]?))?$~', $key, $thumb))
|
||||
continue;
|
||||
|
||||
$has_crop_boundary = isset($metadata['crop_' . $thumb['width'] . 'x' . $thumb['height']]);
|
||||
$has_custom_image = isset($metadata['custom_' . $thumb['width'] . 'x' . $thumb['height']]);
|
||||
$thumbs[] = [
|
||||
'dimensions' => [(int) $thumb['width'], (int) $thumb['height']],
|
||||
'cropped' => !$has_custom_image && (!empty($thumb['suffix']) || $has_crop_boundary),
|
||||
'crop_method' => !$has_custom_image && !empty($thumb['method']) ? $thumb['method'] : (!empty($thumb['suffix']) ? 'c' : null),
|
||||
'crop_region' => $has_crop_boundary ? $metadata['crop_' . $thumb['width'] . 'x' . $thumb['height']] : null,
|
||||
'custom_image' => $has_custom_image,
|
||||
'filename' => $meta,
|
||||
'full_path' => THUMBSDIR . '/' . $path . '/' . $meta,
|
||||
'url' => THUMBSURL . '/' . $path . '/' . $meta,
|
||||
'status' => file_exists(THUMBSDIR . '/' . $path . '/' . $meta),
|
||||
];
|
||||
}
|
||||
|
||||
return $thumbs;
|
||||
}
|
||||
|
||||
private function updateThumb(Image $image)
|
||||
{
|
||||
$data = json_decode($_POST['data']);
|
||||
$meta = $image->getMeta();
|
||||
|
||||
// Set new crop boundary.
|
||||
$crop_key = 'crop_' . $data->thumb_width . 'x' . $data->thumb_height;
|
||||
$crop_value = $data->crop_width . ',' . $data->crop_height . ',' . $data->source_x . ',' . $data->source_y;
|
||||
$meta[$crop_key] = $crop_value;
|
||||
|
||||
// If we uploaded a custom thumbnail, stop considering it such.
|
||||
$custom_key = 'custom_' . $data->thumb_width . 'x' . $data->thumb_height;
|
||||
if (isset($meta[$custom_key]))
|
||||
unset($meta[$custom_key]);
|
||||
|
||||
// Force a rebuild of related thumbnails.
|
||||
$thumb_key = 'thumb_' . $data->thumb_width . 'x' . $data->thumb_height;
|
||||
foreach ($meta as $meta_key => $meta_value)
|
||||
if ($meta_key === $thumb_key || strpos($meta_key, $thumb_key . '_') !== false)
|
||||
unset($meta[$meta_key]);
|
||||
|
||||
$image->setMetaData($meta);
|
||||
|
||||
$payload = [
|
||||
'key' => $crop_key,
|
||||
'value' => $crop_value,
|
||||
'url' => $image->getThumbnailUrl($data->thumb_width, $data->thumb_height, 'exact'),
|
||||
];
|
||||
|
||||
header('Content-Type: text/json; charset=utf-8');
|
||||
echo json_encode($payload);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
195
controllers/EditUser.php
Normal file
195
controllers/EditUser.php
Normal file
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
/*****************************************************************************
|
||||
* EditUser.php
|
||||
* Contains the edit user controller.
|
||||
*
|
||||
* Kabuki CMS (C) 2013-2015, Aaron van Geffen
|
||||
*****************************************************************************/
|
||||
|
||||
class EditUser extends HTMLController
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
// Ensure it's just admins at this point.
|
||||
if (!Registry::get('user')->isAdmin())
|
||||
throw new NotAllowedException();
|
||||
|
||||
// Who are we, again?
|
||||
$current_user = Registry::get('user');
|
||||
|
||||
$id_user = isset($_GET['id']) ? (int) $_GET['id'] : 0;
|
||||
if (empty($id_user) && !isset($_GET['add']))
|
||||
throw new UnexpectedValueException('Requested user not found or not requesting a new user.');
|
||||
|
||||
// Adding a user?
|
||||
if (isset($_GET['add']))
|
||||
{
|
||||
parent::__construct('Add a new user');
|
||||
$view = new DummyBox('Add a new user');
|
||||
$this->page->adopt($view);
|
||||
$this->page->addClass('edituser');
|
||||
}
|
||||
// Deleting one?
|
||||
elseif (isset($_GET['delete']))
|
||||
{
|
||||
// Don't be stupid.
|
||||
if ($current_user->getUserId() == $id_user)
|
||||
trigger_error('Sorry, I cannot allow you to delete yourself.', E_USER_ERROR);
|
||||
|
||||
// So far so good?
|
||||
$user = Member::fromId($id_user);
|
||||
if (Session::validateSession('get') && $user->delete())
|
||||
{
|
||||
header('Location: ' . BASEURL . '/manageusers/');
|
||||
exit;
|
||||
}
|
||||
else
|
||||
trigger_error('Cannot delete user: an error occured while processing the request.', E_USER_ERROR);
|
||||
}
|
||||
// Editing one, then, surely.
|
||||
else
|
||||
{
|
||||
$user = Member::fromId($id_user);
|
||||
parent::__construct('Edit user \'' . $user->getFullName() . '\'');
|
||||
$view = new DummyBox('Edit user \'' . $user->getFullName() . '\'');
|
||||
$this->page->adopt($view);
|
||||
$this->page->addClass('edituser');
|
||||
}
|
||||
|
||||
// Session checking!
|
||||
if (empty($_POST))
|
||||
Session::resetSessionToken();
|
||||
else
|
||||
Session::validateSession();
|
||||
|
||||
if ($id_user && !($current_user->isAdmin() && $current_user->getUserId() == $id_user))
|
||||
$after_form = '<a href="' . BASEURL . '/edituser/?id=' . $id_user . '&delete&' . Session::getSessionTokenKey() . '=' . Session::getSessionToken() . '" class="btn btn-danger" onclick="return confirm(\'Are you sure you want to delete this user? You cannot undo this!\');">Delete user</a>';
|
||||
elseif (!$id_user)
|
||||
$after_form = '<button name="submit_and_new" class="btn">Save and add another</button>';
|
||||
else
|
||||
$after_form = '';
|
||||
|
||||
$form = new Form([
|
||||
'request_url' => BASEURL . '/edituser/?' . ($id_user ? 'id=' . $id_user : 'add'),
|
||||
'content_below' => $after_form,
|
||||
'fields' => [
|
||||
'first_name' => [
|
||||
'type' => 'text',
|
||||
'label' => 'First name',
|
||||
'size' => 50,
|
||||
'maxlength' => 255,
|
||||
],
|
||||
'surname' => [
|
||||
'type' => 'text',
|
||||
'label' => 'Surname',
|
||||
'size' => 50,
|
||||
'maxlength' => 255,
|
||||
],
|
||||
'slug' => [
|
||||
'type' => 'text',
|
||||
'label' => 'URL slug',
|
||||
'size' => 50,
|
||||
'maxlength' => 255,
|
||||
],
|
||||
'emailaddress' => [
|
||||
'type' => 'text',
|
||||
'label' => 'Email address',
|
||||
'size' => 50,
|
||||
'maxlength' => 255,
|
||||
],
|
||||
'password1' => [
|
||||
'type' => 'password',
|
||||
'label' => 'Password',
|
||||
'size' => 50,
|
||||
'maxlength' => 255,
|
||||
'is_optional' => true,
|
||||
],
|
||||
'password2' => [
|
||||
'type' => 'password',
|
||||
'label' => 'Password (repeat)',
|
||||
'size' => 50,
|
||||
'maxlength' => 255,
|
||||
'is_optional' => true,
|
||||
],
|
||||
'is_admin' => [
|
||||
'header' => 'Privileges',
|
||||
'type' => 'checkbox',
|
||||
'label' => 'This user ' . ($id_user ? 'has' : 'should have') . ' administrative privileges.',
|
||||
'is_optional' => true,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
// Create the form, add in default values.
|
||||
$form->setData($id_user ? $user->getProps() : $_POST);
|
||||
$formview = new FormView($form);
|
||||
$view->adopt($formview);
|
||||
|
||||
if (!empty($_POST))
|
||||
{
|
||||
$form->verify($_POST);
|
||||
|
||||
// Anything missing?
|
||||
if (!empty($form->getMissing()))
|
||||
return $formview->adopt(new DummyBox('Some data missing', 'Please fill out the following fields: ' . implode(', ', $form->getMissing())));
|
||||
|
||||
$data = $form->getData();
|
||||
|
||||
// Just to be on the safe side.
|
||||
$data['first_name'] = htmlentities(trim($data['first_name']));
|
||||
$data['surname'] = htmlentities(trim($data['surname']));
|
||||
$data['emailaddress'] = trim($data['emailaddress']);
|
||||
|
||||
// Make sure there's a slug.
|
||||
if (empty($data['slug']))
|
||||
$data['slug'] = $data['first_name'];
|
||||
|
||||
// Quick stripping.
|
||||
$data['slug'] = strtr(strtolower($data['slug']), [' ' => '-', '--' => '-', '&' => 'and', '=>' => '', "'" => "", ":"=> "", '/' => '-', '\\' => '-']);
|
||||
|
||||
// Checkboxes, fun!
|
||||
$data['is_admin'] = empty($data['is_admin']) ? 0 : 1;
|
||||
|
||||
// If it looks like an e-mail address...
|
||||
if (!empty($data['emailaddress']) && !preg_match('~^[^ ]+@[^ ]+\.[a-z]+$~', $data['emailaddress']))
|
||||
return $formview->adopt(new DummyBox('Email addresses invalid', 'The email address you entered is not a valid email address.'));
|
||||
// Check whether email address is already linked to an account in the database -- just not to the account we happen to be editing, of course.
|
||||
elseif (!empty($data['emailaddress']) && Member::exists($data['emailaddress']) && !($id_user && $user->getEmailAddress() == $data['emailaddress']))
|
||||
return $formview->adopt(new DummyBox('Email address already in use', 'Another account is already using the e-mail address you entered.'));
|
||||
|
||||
// Setting passwords? We'll need two!
|
||||
if (!$id_user || !empty($data['password1']) && !empty($data['password2']))
|
||||
{
|
||||
if (strlen($data['password1']) < 6 || !preg_match('~[^A-z]~', $data['password1']))
|
||||
return $formview->adopt(new DummyBox('Password not acceptable', 'Please fill in a password that is at least six characters long and contains at least one non-alphabetic character (e.g. a number or symbol).'));
|
||||
elseif ($data['password1'] !== $data['password2'])
|
||||
return $formview->adopt(new DummyBox('Passwords do not match', 'The passwords you entered do not match. Please try again.'));
|
||||
else
|
||||
$data['password'] = $data['password1'];
|
||||
|
||||
unset($data['password1'], $data['password2']);
|
||||
}
|
||||
|
||||
// Creating a new user?
|
||||
if (!$id_user)
|
||||
{
|
||||
$return = Member::createNew($data);
|
||||
if ($return === false)
|
||||
return $formview->adopt(new DummyBox('Cannot create this user', 'Something went wrong while creating the user...'));
|
||||
|
||||
if (isset($_POST['submit_and_new']))
|
||||
{
|
||||
header('Location: ' . BASEURL . '/edituser/?add');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
// Just updating?
|
||||
else
|
||||
$user->update($data);
|
||||
|
||||
// Redirect to the user management page.
|
||||
header('Location: ' . BASEURL . '/manageusers/');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
34
controllers/HTMLController.php
Normal file
34
controllers/HTMLController.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
/*****************************************************************************
|
||||
* HTMLController.php
|
||||
* Contains the key HTML controller
|
||||
*
|
||||
* Kabuki CMS (C) 2013-2015, Aaron van Geffen
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* The abstract class that allows easy creation of html pages.
|
||||
*/
|
||||
abstract class HTMLController
|
||||
{
|
||||
protected $page;
|
||||
protected $admin_bar;
|
||||
|
||||
public function __construct($title)
|
||||
{
|
||||
header('Content-Type: text/html; charset=utf-8');
|
||||
$this->page = new MainTemplate($title);
|
||||
|
||||
if (Registry::get('user')->isAdmin())
|
||||
{
|
||||
$this->page->appendStylesheet(BASEURL . '/css/admin.css');
|
||||
$this->admin_bar = new AdminBar();
|
||||
$this->page->adopt($this->admin_bar);
|
||||
}
|
||||
}
|
||||
|
||||
public function showContent()
|
||||
{
|
||||
$this->page->html_main();
|
||||
}
|
||||
}
|
||||
21
controllers/JSONController.php
Normal file
21
controllers/JSONController.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/*****************************************************************************
|
||||
* JSONController.php
|
||||
* Contains the key JSON controller
|
||||
*
|
||||
* Kabuki CMS (C) 2013-2015, Aaron van Geffen
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* The abstract class that allows easy creation of json replies.
|
||||
*/
|
||||
class JSONController
|
||||
{
|
||||
protected $payload;
|
||||
|
||||
public function showContent()
|
||||
{
|
||||
header('Content-Type: text/json; charset=utf-8');
|
||||
echo json_encode($this->payload);
|
||||
}
|
||||
}
|
||||
56
controllers/Login.php
Normal file
56
controllers/Login.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/*****************************************************************************
|
||||
* Login.php
|
||||
* Contains the controller for logging the user in.
|
||||
*
|
||||
* Kabuki CMS (C) 2013-2015, Aaron van Geffen
|
||||
*****************************************************************************/
|
||||
|
||||
class Login extends HTMLController
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
// No need to log in twice, dear heart!
|
||||
if (Registry::get('user')->isLoggedIn())
|
||||
{
|
||||
if (Registry::get('user')->isAdmin())
|
||||
header('Location: ' . BASEURL . '/admin/');
|
||||
else
|
||||
header('Location: ' . BASEURL . '/');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Sanity check
|
||||
$login_error = false;
|
||||
if (isset($_POST['emailaddress'], $_POST['password']))
|
||||
{
|
||||
if (Authentication::checkPassword($_POST['emailaddress'], $_POST['password']))
|
||||
{
|
||||
parent::__construct('Login');
|
||||
$_SESSION['user_id'] = Authentication::getUserId($_POST['emailaddress']);
|
||||
|
||||
if (isset($_POST['redirect_url']))
|
||||
header('Location: ' . base64_decode($_POST['redirect_url']));
|
||||
elseif (isset($_SESSION['login_url']))
|
||||
header('Location: ' . $_SESSION['redirect_url']);
|
||||
else
|
||||
header('Location: ' . BASEURL . '/admin/');
|
||||
exit;
|
||||
}
|
||||
else
|
||||
$login_error = true;
|
||||
}
|
||||
|
||||
parent::__construct('Log in');
|
||||
$this->page->appendStylesheet(BASEURL . '/css/admin.css');
|
||||
$form = new LogInForm('Log in');
|
||||
if ($login_error)
|
||||
$form->setErrorMessage('Invalid email address or password.');
|
||||
|
||||
// Tried anything? Be helpful, at least.
|
||||
if (isset($_POST['emailaddress']))
|
||||
$form->setEmail($_POST['emailaddress']);
|
||||
|
||||
$this->page->adopt($form);
|
||||
}
|
||||
}
|
||||
20
controllers/Logout.php
Normal file
20
controllers/Logout.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
/*****************************************************************************
|
||||
* Logout.php
|
||||
* Contains the controller for logging the user out.
|
||||
*
|
||||
* Kabuki CMS (C) 2013-2015, Aaron van Geffen
|
||||
*****************************************************************************/
|
||||
|
||||
class Logout extends HTMLController
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
// Clear the entire sesssion.
|
||||
$_SESSION = [];
|
||||
|
||||
// Back to the frontpage you go.
|
||||
header('Location: ' . BASEURL);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
122
controllers/ManageErrors.php
Normal file
122
controllers/ManageErrors.php
Normal file
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
/*****************************************************************************
|
||||
* ManageErrors.php
|
||||
* Contains the controller for managing errors.
|
||||
*
|
||||
* Kabuki CMS (C) 2013-2015, Aaron van Geffen
|
||||
*****************************************************************************/
|
||||
|
||||
class ManageErrors extends HTMLController
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
// Ensure it's just admins at this point.
|
||||
if (!Registry::get('user')->isAdmin())
|
||||
throw new NotAllowedException();
|
||||
|
||||
// Flushing, are we?
|
||||
if (isset($_POST['flush']))
|
||||
ErrorLog::flush();
|
||||
|
||||
$options = [
|
||||
'title' => 'Error log',
|
||||
'form' => [
|
||||
'action' => BASEURL . '/manageerrors/',
|
||||
'method' => 'post',
|
||||
'class' => 'floatright',
|
||||
'buttons' => [
|
||||
'flush' => [
|
||||
'type' => 'submit',
|
||||
'caption' => 'Delete all',
|
||||
],
|
||||
],
|
||||
],
|
||||
'columns' => [
|
||||
'id' => [
|
||||
'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">' . $row['debug_info'] . '</pre></div>' .
|
||||
'<small><a href="' . BASEURL . $row['request_uri'] . '">' . $row['request_uri'] . '</a></small>';
|
||||
}
|
||||
],
|
||||
'header' => 'Message / URL',
|
||||
'is_sortable' => false,
|
||||
],
|
||||
'file' => [
|
||||
'value' => 'file',
|
||||
'header' => 'File',
|
||||
'is_sortable' => true,
|
||||
],
|
||||
'line' => [
|
||||
'value' => 'line',
|
||||
'header' => 'Line',
|
||||
'is_sortable' => true,
|
||||
],
|
||||
'time' => [
|
||||
'parse' => [
|
||||
'type' => 'timestamp',
|
||||
'data' => [
|
||||
'timestamp' => 'time',
|
||||
'pattern' => 'long',
|
||||
],
|
||||
],
|
||||
'header' => 'Time',
|
||||
'is_sortable' => true,
|
||||
],
|
||||
'ip' => [
|
||||
'value' => 'ip_address',
|
||||
'header' => 'IP',
|
||||
'is_sortable' => true,
|
||||
],
|
||||
'uid' => [
|
||||
'header' => 'UID',
|
||||
'is_sortable' => true,
|
||||
'parse' => [
|
||||
'link' => BASEURL . '/member/?id={ID_USER}',
|
||||
'data' => 'id_user',
|
||||
],
|
||||
],
|
||||
],
|
||||
'start' => !empty($_GET['start']) ? (int) $_GET['start'] : 0,
|
||||
'sort_order' => !empty($_GET['order']) ? $_GET['order'] : '',
|
||||
'sort_direction' => !empty($_GET['dir']) ? $_GET['dir'] : '',
|
||||
'no_items_label' => "No errors to display -- we're all good!",
|
||||
'items_per_page' => 20,
|
||||
'index_class' => 'floatleft',
|
||||
'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,
|
||||
];
|
||||
},
|
||||
];
|
||||
|
||||
$error_log = new GenericTable($options);
|
||||
parent::__construct('Error log - Page ' . $error_log->getCurrentPage() .' - ' . SITE_TITLE);
|
||||
$this->page->adopt(new TabularData($error_log));
|
||||
}
|
||||
}
|
||||
122
controllers/ManageTags.php
Normal file
122
controllers/ManageTags.php
Normal file
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
/*****************************************************************************
|
||||
* ManageTags.php
|
||||
* Contains the controller with the admin's list of tags.
|
||||
*
|
||||
* Kabuki CMS (C) 2013-2015, Aaron van Geffen
|
||||
*****************************************************************************/
|
||||
|
||||
class ManageTags extends HTMLController
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
// Ensure it's just admins at this point.
|
||||
if (!Registry::get('user')->isAdmin())
|
||||
throw new NotAllowedException();
|
||||
|
||||
if (isset($_REQUEST['create']) && isset($_POST['tag']))
|
||||
$this->handleTagCreation();
|
||||
|
||||
$options = [
|
||||
'columns' => [
|
||||
'id_post' => [
|
||||
'value' => 'id_tag',
|
||||
'header' => 'ID',
|
||||
'is_sortable' => true,
|
||||
],
|
||||
'tag' => [
|
||||
'header' => 'Tag',
|
||||
'is_sortable' => true,
|
||||
'parse' => [
|
||||
'link' => BASEURL . '/managetag/?id={ID_TAG}',
|
||||
'data' => 'tag',
|
||||
],
|
||||
],
|
||||
'slug' => [
|
||||
'header' => 'Slug',
|
||||
'is_sortable' => true,
|
||||
'parse' => [
|
||||
'link' => BASEURL . '/managetag/?id={ID_TAG}',
|
||||
'data' => 'slug',
|
||||
],
|
||||
],
|
||||
'count' => [
|
||||
'header' => 'Cardinality',
|
||||
'is_sortable' => true,
|
||||
'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,
|
||||
'title' => 'Manage tags',
|
||||
'no_items_label' => 'No tags meet the requirements of the current filter.',
|
||||
'items_per_page' => 25,
|
||||
'base_url' => BASEURL . '/managetags/',
|
||||
'get_data' => function($offset = 0, $limit = 15, $order = '', $direction = 'up') {
|
||||
if (!in_array($order, ['id_post', 'tag', 'slug', 'count']))
|
||||
$order = 'tag';
|
||||
if (!in_array($direction, ['up', 'down']))
|
||||
$direction = 'up';
|
||||
|
||||
$data = Registry::get('db')->queryAssocs('
|
||||
SELECT *
|
||||
FROM tags
|
||||
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 == 'up' ? 'up' : 'down'),
|
||||
];
|
||||
},
|
||||
'get_count' => function() {
|
||||
return Registry::get('db')->queryValue('
|
||||
SELECT COUNT(*)
|
||||
FROM tags');
|
||||
}
|
||||
];
|
||||
|
||||
$table = new GenericTable($options);
|
||||
parent::__construct('Tag management - Page ' . $table->getCurrentPage() .' - ' . SITE_TITLE);
|
||||
$this->page->adopt(new TabularData($table));
|
||||
}
|
||||
|
||||
private function handleTagCreation()
|
||||
{
|
||||
header('Content-Type: text/json; charset=utf-8');
|
||||
|
||||
// It better not already exist!
|
||||
if (Tag::exactMatch($_POST['tag']))
|
||||
{
|
||||
echo '{"error":"Tag already exists!"}';
|
||||
exit;
|
||||
}
|
||||
|
||||
$label = htmlentities(trim($_POST['tag']));
|
||||
$slug = strtr(strtolower($label), [' ' => '-']);
|
||||
$tag = Tag::createNew([
|
||||
'tag' => $label,
|
||||
'slug' => $slug,
|
||||
]);
|
||||
|
||||
// Did we succeed?
|
||||
if (!$tag)
|
||||
{
|
||||
echo '{"error":"Could not create tag."}';
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'label' => $tag->tag,
|
||||
'id_tag' => $tag->id_tag,
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
131
controllers/ManageUsers.php
Normal file
131
controllers/ManageUsers.php
Normal file
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
/*****************************************************************************
|
||||
* ManageUsers.php
|
||||
* Contains the controller with the list of users.
|
||||
*
|
||||
* Kabuki CMS (C) 2013-2015, Aaron van Geffen
|
||||
*****************************************************************************/
|
||||
|
||||
class ManageUsers extends HTMLController
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
// Ensure it's just admins at this point.
|
||||
if (!Registry::get('user')->isAdmin())
|
||||
throw new NotAllowedException();
|
||||
|
||||
$options = [
|
||||
'form' => [
|
||||
'action' => BASEURL . '/edituser/',
|
||||
'method' => 'get',
|
||||
'class' => 'floatright',
|
||||
'buttons' => [
|
||||
'add' => [
|
||||
'type' => 'submit',
|
||||
'caption' => 'Add new user',
|
||||
],
|
||||
],
|
||||
],
|
||||
'columns' => [
|
||||
'id_user' => [
|
||||
'value' => 'id_user',
|
||||
'header' => 'ID',
|
||||
'is_sortable' => true,
|
||||
],
|
||||
'surname' => [
|
||||
'header' => 'Last name',
|
||||
'is_sortable' => true,
|
||||
'parse' => [
|
||||
'link' => BASEURL . '/edituser/?id={ID_USER}',
|
||||
'data' => 'surname',
|
||||
],
|
||||
],
|
||||
'first_name' => [
|
||||
'header' => 'First name',
|
||||
'is_sortable' => true,
|
||||
'parse' => [
|
||||
'link' => BASEURL . '/edituser/?id={ID_USER}',
|
||||
'data' => 'first_name',
|
||||
],
|
||||
],
|
||||
'slug' => [
|
||||
'header' => 'Slug',
|
||||
'is_sortable' => true,
|
||||
'parse' => [
|
||||
'link' => BASEURL . '/edituser/?id={ID_USER}',
|
||||
'data' => 'slug',
|
||||
],
|
||||
],
|
||||
'emailaddress' => [
|
||||
'value' => 'emailaddress',
|
||||
'header' => 'Email address',
|
||||
'is_sortable' => true,
|
||||
],
|
||||
'last_action_time' => [
|
||||
'parse' => [
|
||||
'type' => 'timestamp',
|
||||
'data' => [
|
||||
'timestamp' => 'last_action_time',
|
||||
'pattern' => 'long',
|
||||
],
|
||||
],
|
||||
'header' => 'Last activity',
|
||||
'is_sortable' => true,
|
||||
],
|
||||
'ip_address' => [
|
||||
'is_sortable' => true,
|
||||
'value' => 'ip_address',
|
||||
'header' => 'IP address',
|
||||
],
|
||||
'is_admin' => [
|
||||
'is_sortable' => true,
|
||||
'header' => 'Admin?',
|
||||
'parse' => [
|
||||
'type' => 'function',
|
||||
'data' => function($row) {
|
||||
return $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'] : '',
|
||||
'title' => 'Manage users',
|
||||
'no_items_label' => 'No users meet the requirements of the current filter.',
|
||||
'items_per_page' => 15,
|
||||
'index_class' => 'floatleft',
|
||||
'base_url' => BASEURL . '/manageusers/',
|
||||
'get_data' => function($offset = 0, $limit = 15, $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');
|
||||
}
|
||||
];
|
||||
|
||||
$table = new GenericTable($options);
|
||||
parent::__construct('User management - Page ' . $table->getCurrentPage() .' - ' . SITE_TITLE);
|
||||
$this->page->adopt(new TabularData($table));
|
||||
}
|
||||
}
|
||||
41
controllers/ProvideAutoSuggest.php
Normal file
41
controllers/ProvideAutoSuggest.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/*****************************************************************************
|
||||
* ProvideAutoSuggest.php
|
||||
* Contains the autosuggest provider.
|
||||
*
|
||||
* Kabuki CMS (C) 2013-2015, Aaron van Geffen
|
||||
*****************************************************************************/
|
||||
|
||||
class ProvideAutoSuggest extends JSONController
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
// Ensure it's just admins at this point.
|
||||
if (!Registry::get('user')->isAdmin())
|
||||
throw new NotAllowedException();
|
||||
|
||||
if (!isset($_GET['type']))
|
||||
throw new UnexpectedValueException('Unsupported autosuggest request.');
|
||||
|
||||
if ($_GET['type'] === 'tags' && isset($_GET['data']))
|
||||
{
|
||||
$data = array_unique(explode(' ', urldecode($_GET['data'])));
|
||||
$data = array_filter($data, function($item) {
|
||||
return strlen($item) >= 3;
|
||||
});
|
||||
|
||||
$this->payload = ['items' => []];
|
||||
|
||||
// Nothing to look for?
|
||||
if (count($data) === 0)
|
||||
return;
|
||||
|
||||
$results = Tag::match($data);
|
||||
foreach ($results as $id_tag => $tag)
|
||||
$this->payload['items'][] = [
|
||||
'label' => $tag,
|
||||
'id_tag' => $id_tag,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
58
controllers/UploadMedia.php
Normal file
58
controllers/UploadMedia.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/*****************************************************************************
|
||||
* UploadMedia.php
|
||||
* Contains the media uploading controller
|
||||
*
|
||||
* Kabuki CMS (C) 2013-2015, Aaron van Geffen
|
||||
*****************************************************************************/
|
||||
|
||||
class UploadMedia extends HTMLController
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
// Ensure it's just admins at this point.
|
||||
if (!Registry::get('user')->isAdmin())
|
||||
throw new NotAllowedException();
|
||||
|
||||
$page = new MediaUploader();
|
||||
parent::__construct('Upload new media - ' . SITE_TITLE);
|
||||
$this->page->adopt($page);
|
||||
|
||||
// Are we saving something?
|
||||
if (isset($_POST['save']))
|
||||
{
|
||||
if (empty($_FILES) || empty($_FILES['new_asset']))
|
||||
return;
|
||||
|
||||
// Any tags?
|
||||
$new_tags = [];
|
||||
if (isset($_POST['tag']) && is_array($_POST['tag']))
|
||||
{
|
||||
foreach ($_POST['tag'] as $id_tag => $bool)
|
||||
if (is_numeric($id_tag))
|
||||
$new_tags[] = $id_tag;
|
||||
}
|
||||
|
||||
var_dump($_FILES);
|
||||
var_dump($_POST);
|
||||
|
||||
foreach ($_FILES['new_asset']['tmp_name'] as $num => $uploaded_file)
|
||||
{
|
||||
if (empty($uploaded_file))
|
||||
continue;
|
||||
|
||||
$asset = Asset::createNew([
|
||||
'filename_to_copy' => $uploaded_file,
|
||||
'preferred_filename' => $_FILES['new_asset']['name'][$num],
|
||||
'title' => !empty($_POST['title'][$num]) ? $_POST['title'][$num] : null,
|
||||
]);
|
||||
|
||||
$asset->linkTags($new_tags);
|
||||
}
|
||||
|
||||
// Prevent uploading twice.
|
||||
header('Location: ' . BASEURL . '/uploadmedia/');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
45
controllers/ViewPeople.php
Normal file
45
controllers/ViewPeople.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/*****************************************************************************
|
||||
* ViewPeople.php
|
||||
* Contains the people index controller
|
||||
*
|
||||
* Kabuki CMS (C) 2013-2015, Aaron van Geffen
|
||||
*****************************************************************************/
|
||||
|
||||
class ViewPeople extends HTMLController
|
||||
{
|
||||
const PER_PAGE = 24;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
// Fetch subalbums.
|
||||
// !!! TODO: pagination.
|
||||
$subalbums = Tag::getPeople();
|
||||
|
||||
// What assets are we using?
|
||||
$id_assets = array_map(function($album) {
|
||||
return (int) $album['id_asset_thumb'];
|
||||
}, $subalbums);
|
||||
|
||||
// Fetch assets for thumbnails.
|
||||
$assets = Asset::fromIds($id_assets, 'object');
|
||||
|
||||
// Build album list.
|
||||
$albums = [];
|
||||
foreach ($subalbums as $album)
|
||||
{
|
||||
$albums[$album['id_tag']] = [
|
||||
'id_tag' => $album['id_tag'],
|
||||
'caption' => $album['tag'],
|
||||
'link' => BASEURL . '/' . $album['slug'] . '/',
|
||||
'thumbnail' => !empty($album['id_asset_thumb']) ? $assets[$album['id_asset_thumb']]->getImage() : null,
|
||||
];
|
||||
}
|
||||
|
||||
$index = new AlbumIndex($albums);
|
||||
|
||||
parent::__construct('People - ' . SITE_TITLE);
|
||||
$this->page->adopt($index);
|
||||
$this->page->setCanonicalUrl(BASEURL . '/people/');
|
||||
}
|
||||
}
|
||||
130
controllers/ViewPhotoAlbum.php
Normal file
130
controllers/ViewPhotoAlbum.php
Normal file
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
/*****************************************************************************
|
||||
* ViewPhotoAlbum.php
|
||||
* Contains the photo album index controller
|
||||
*
|
||||
* Kabuki CMS (C) 2013-2015, Aaron van Geffen
|
||||
*****************************************************************************/
|
||||
|
||||
class ViewPhotoAlbum extends HTMLController
|
||||
{
|
||||
protected $iterator;
|
||||
protected $total_count;
|
||||
protected $base_url;
|
||||
|
||||
const PER_PAGE = 24;
|
||||
|
||||
public function __construct($title = 'Photos - ' . SITE_TITLE)
|
||||
{
|
||||
// Viewing an album?
|
||||
if (isset($_GET['tag']))
|
||||
{
|
||||
$tag = Tag::fromSlug($_GET['tag']);
|
||||
$id_tag = $tag->id_tag;
|
||||
$title = $tag->tag;
|
||||
$description = !empty($tag->description) ? '<p>' . $tag->description . '</p>' : '';
|
||||
|
||||
// Can we go up a level?
|
||||
if ($tag->id_parent != 0)
|
||||
{
|
||||
$ptag = Tag::fromId($tag->id_parent);
|
||||
$description .= '<p><a href="' . BASEURL . '/' . (!empty($ptag->slug) ? $ptag->slug . '/' : '') . '">« Go back to "' . $ptag->tag . '"</a></p>';
|
||||
}
|
||||
elseif ($tag->kind === 'Person')
|
||||
$description .= '<p><a href="' . BASEURL . '/people/">« Go back to "People"</a></p>';
|
||||
}
|
||||
// View the album root.
|
||||
else
|
||||
{
|
||||
$id_tag = 1;
|
||||
$title = 'Albums';
|
||||
$description = '';
|
||||
}
|
||||
|
||||
// What page are we at?
|
||||
$page = isset($_GET['page']) ? (int) $_GET['page'] : 1;
|
||||
|
||||
parent::__construct($title . ' - Page ' . $page . ' - ' . SITE_TITLE);
|
||||
|
||||
if ($id_tag !== 1)
|
||||
$this->page->adopt(new DummyBox($title, $description, 'page_title_box'));
|
||||
|
||||
// Fetch subalbums, but only if we're on the first page.
|
||||
if ($page === 1)
|
||||
{
|
||||
$albums = $this->getAlbums($id_tag);
|
||||
$index = new AlbumIndex($albums);
|
||||
$this->page->adopt($index);
|
||||
}
|
||||
|
||||
// Load a photo mosaic for the current tag.
|
||||
list($mosaic, $total_count) = $this->getPhotoMosaic($id_tag, $page);
|
||||
if (isset($mosaic))
|
||||
$this->page->adopt(new PhotosIndex($mosaic, Registry::get('user')->isAdmin()));
|
||||
|
||||
// Make a page index as needed, while we're at it.
|
||||
if ($total_count > self::PER_PAGE)
|
||||
{
|
||||
$index = new PageIndex([
|
||||
'recordCount' => $total_count,
|
||||
'items_per_page' => self::PER_PAGE,
|
||||
'start' => (isset($_GET['page']) ? $_GET['page'] - 1 : 0) * self::PER_PAGE,
|
||||
'base_url' => BASEURL . '/' . (isset($_GET['tag']) ? $_GET['tag'] . '/' : ''),
|
||||
'page_slug' => 'page/%PAGE%/',
|
||||
]);
|
||||
$this->page->adopt(new Pagination($index));
|
||||
}
|
||||
|
||||
// Set the canonical url.
|
||||
$this->page->setCanonicalUrl(BASEURL . '/' . (isset($_GET['tag']) ? $_GET['tag'] . '/' : ''));
|
||||
}
|
||||
|
||||
public function getPhotoMosaic($id_tag, $page)
|
||||
{
|
||||
// Create an iterator.
|
||||
list($this->iterator, $total_count) = AssetIterator::getByOptions([
|
||||
'id_tag' => $id_tag,
|
||||
'order' => 'date_captured',
|
||||
'direction' => 'desc',
|
||||
'limit' => self::PER_PAGE,
|
||||
'page' => $page,
|
||||
], true);
|
||||
|
||||
$mosaic = $total_count > 0 ? new PhotoMosaic($this->iterator) : null;
|
||||
return [$mosaic, $total_count];
|
||||
}
|
||||
|
||||
private function getAlbums($id_tag)
|
||||
{
|
||||
// Fetch subalbums.
|
||||
$subalbums = Tag::getAlbums($id_tag);
|
||||
|
||||
// What assets are we using?
|
||||
$id_assets = array_map(function($album) {
|
||||
return (int) $album['id_asset_thumb'];
|
||||
}, $subalbums);
|
||||
|
||||
// Fetch assets for thumbnails.
|
||||
$assets = Asset::fromIds($id_assets, 'object');
|
||||
|
||||
// Build album list.
|
||||
$albums = [];
|
||||
foreach ($subalbums as $album)
|
||||
{
|
||||
$albums[$album['id_tag']] = [
|
||||
'id_tag' => $album['id_tag'],
|
||||
'caption' => $album['tag'],
|
||||
'link' => BASEURL . '/' . $album['slug'] . '/',
|
||||
'thumbnail' => !empty($album['id_asset_thumb']) ? $assets[$album['id_asset_thumb']]->getImage() : null,
|
||||
];
|
||||
}
|
||||
|
||||
return $albums;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if (isset($this->iterator))
|
||||
$this->iterator->clean();
|
||||
}
|
||||
}
|
||||
58
controllers/ViewTimeline.php
Normal file
58
controllers/ViewTimeline.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/*****************************************************************************
|
||||
* ViewTimeline.php
|
||||
* Contains the photo timeline index controller
|
||||
*
|
||||
* Kabuki CMS (C) 2013-2015, Aaron van Geffen
|
||||
*****************************************************************************/
|
||||
|
||||
class ViewTimeline extends HTMLController
|
||||
{
|
||||
protected $iterator;
|
||||
protected $total_count;
|
||||
protected $base_url;
|
||||
|
||||
const PER_PAGE = 24;
|
||||
|
||||
public function __construct($title = 'Photos - ' . SITE_TITLE)
|
||||
{
|
||||
// What page are we at?
|
||||
$page = isset($_GET['page']) ? (int) $_GET['page'] : 1;
|
||||
|
||||
parent::__construct('Timeline - Page ' . $page . ' - ' . SITE_TITLE);
|
||||
|
||||
// Load a photo mosaic.
|
||||
list($this->iterator, $total_count) = AssetIterator::getByOptions([
|
||||
'order' => 'date_captured',
|
||||
'direction' => 'desc',
|
||||
'limit' => self::PER_PAGE,
|
||||
'page' => $page,
|
||||
], true);
|
||||
|
||||
$mosaic = $total_count > 0 ? new PhotoMosaic($this->iterator) : null;
|
||||
if (isset($mosaic))
|
||||
$this->page->adopt(new PhotosIndex($mosaic, Registry::get('user')->isAdmin()));
|
||||
|
||||
// Make a page index as needed, while we're at it.
|
||||
if ($total_count > self::PER_PAGE)
|
||||
{
|
||||
$index = new PageIndex([
|
||||
'recordCount' => $total_count,
|
||||
'items_per_page' => self::PER_PAGE,
|
||||
'start' => (isset($_GET['page']) ? $_GET['page'] - 1 : 0) * self::PER_PAGE,
|
||||
'base_url' => BASEURL . '/timeline/',
|
||||
'page_slug' => 'page/%PAGE%/',
|
||||
]);
|
||||
$this->page->adopt(new Pagination($index));
|
||||
}
|
||||
|
||||
// Set the canonical url.
|
||||
$this->page->setCanonicalUrl(BASEURL . '/timeline/');
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if (isset($this->iterator))
|
||||
$this->iterator->clean();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user