New bootstrap-based layout #30
@ -18,7 +18,7 @@ class ManageAlbums extends HTMLController
|
||||
'form' => [
|
||||
'action' => BASEURL . '/editalbum/',
|
||||
'method' => 'get',
|
||||
'class' => 'floatright',
|
||||
'class' => 'float-end',
|
||||
'buttons' => [
|
||||
'add' => [
|
||||
'type' => 'submit',
|
||||
@ -60,7 +60,7 @@ class ManageAlbums extends HTMLController
|
||||
'title' => 'Manage albums',
|
||||
'no_items_label' => 'No albums meet the requirements of the current filter.',
|
||||
'items_per_page' => 9999,
|
||||
'index_class' => 'floatleft',
|
||||
'index_class' => 'float-start',
|
||||
'base_url' => BASEURL . '/managealbums/',
|
||||
'get_data' => function($offset = 0, $limit = 9999, $order = '', $direction = 'up') {
|
||||
if (!in_array($order, ['id_tag', 'tag', 'slug', 'count']))
|
||||
|
@ -29,7 +29,7 @@ class ManageErrors extends HTMLController
|
||||
'form' => [
|
||||
'action' => BASEURL . '/manageerrors/?' . Session::getSessionTokenKey() . '=' . Session::getSessionToken(),
|
||||
'method' => 'post',
|
||||
'class' => 'floatright',
|
||||
'class' => 'float-end',
|
||||
'buttons' => [
|
||||
'flush' => [
|
||||
'type' => 'submit',
|
||||
@ -99,7 +99,7 @@ class ManageErrors extends HTMLController
|
||||
'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',
|
||||
'index_class' => 'float-start',
|
||||
'base_url' => BASEURL . '/manageerrors/',
|
||||
'get_count' => 'ErrorLog::getCount',
|
||||
'get_data' => function($offset = 0, $limit = 20, $order = '', $direction = 'down') {
|
||||
|
@ -18,7 +18,7 @@ class ManageTags extends HTMLController
|
||||
'form' => [
|
||||
'action' => BASEURL . '/edittag/',
|
||||
'method' => 'get',
|
||||
'class' => 'floatright',
|
||||
'class' => 'float-end',
|
||||
'buttons' => [
|
||||
'add' => [
|
||||
'type' => 'submit',
|
||||
@ -65,7 +65,7 @@ class ManageTags extends HTMLController
|
||||
'title' => 'Manage tags',
|
||||
'no_items_label' => 'No tags meet the requirements of the current filter.',
|
||||
'items_per_page' => 30,
|
||||
'index_class' => 'floatleft',
|
||||
'index_class' => 'float-start',
|
||||
'base_url' => BASEURL . '/managetags/',
|
||||
'get_data' => function($offset = 0, $limit = 30, $order = '', $direction = 'up') {
|
||||
if (!in_array($order, ['id_tag', 'tag', 'slug', 'kind', 'count']))
|
||||
|
@ -18,7 +18,7 @@ class ManageUsers extends HTMLController
|
||||
'form' => [
|
||||
'action' => BASEURL . '/edituser/',
|
||||
'method' => 'get',
|
||||
'class' => 'floatright',
|
||||
'class' => 'float-end',
|
||||
'buttons' => [
|
||||
'add' => [
|
||||
'type' => 'submit',
|
||||
@ -94,7 +94,7 @@ class ManageUsers extends HTMLController
|
||||
'title' => 'Manage users',
|
||||
'no_items_label' => 'No users meet the requirements of the current filter.',
|
||||
'items_per_page' => 30,
|
||||
'index_class' => 'floatleft',
|
||||
'index_class' => 'float-start',
|
||||
'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']))
|
||||
|
@ -53,7 +53,7 @@ class ViewPeople extends HTMLController
|
||||
'base_url' => BASEURL . '/people/',
|
||||
'page_slug' => 'page/%PAGE%/',
|
||||
]);
|
||||
$this->page->adopt(new Pagination($pagination));
|
||||
$this->page->adopt(new PageIndexWidget($pagination));
|
||||
|
||||
$this->page->setCanonicalUrl(BASEURL . '/people/' . ($page > 1 ? 'page/' . $page . '/' : ''));
|
||||
}
|
||||
|
@ -112,7 +112,7 @@ class ViewPhotoAlbum extends HTMLController
|
||||
'base_url' => BASEURL . '/' . (isset($_GET['tag']) ? $_GET['tag'] . '/' : ''),
|
||||
'page_slug' => 'page/%PAGE%/',
|
||||
]);
|
||||
$this->page->adopt(new Pagination($index));
|
||||
$this->page->adopt(new PageIndexWidget($index));
|
||||
}
|
||||
|
||||
// Set the canonical url.
|
||||
|
@ -47,7 +47,7 @@ class ViewTimeline extends HTMLController
|
||||
'base_url' => BASEURL . '/timeline/',
|
||||
'page_slug' => 'page/%PAGE%/',
|
||||
]);
|
||||
$this->page->adopt(new Pagination($index));
|
||||
$this->page->adopt(new PageIndexWidget($index));
|
||||
}
|
||||
|
||||
// Set the canonical url.
|
||||
|
341
models/Form.php
341
models/Form.php
@ -3,7 +3,8 @@
|
||||
* Form.php
|
||||
* Contains key class Form.
|
||||
*
|
||||
* Kabuki CMS (C) 2013-2015, Aaron van Geffen
|
||||
* Global Data Lab code (C) Radboud University Nijmegen
|
||||
* Programming (C) Aaron van Geffen, 2015-2022
|
||||
*****************************************************************************/
|
||||
|
||||
class Form
|
||||
@ -12,9 +13,11 @@ class Form
|
||||
public $request_url;
|
||||
public $content_above;
|
||||
public $content_below;
|
||||
private $fields;
|
||||
private $data;
|
||||
private $missing;
|
||||
private $fields = [];
|
||||
private $data = [];
|
||||
private $missing = [];
|
||||
private $submit_caption;
|
||||
private $trim_inputs;
|
||||
|
||||
// NOTE: this class does not verify the completeness of form options.
|
||||
public function __construct($options)
|
||||
@ -24,9 +27,42 @@ class Form
|
||||
$this->fields = !empty($options['fields']) ? $options['fields'] : [];
|
||||
$this->content_below = !empty($options['content_below']) ? $options['content_below'] : null;
|
||||
$this->content_above = !empty($options['content_above']) ? $options['content_above'] : null;
|
||||
$this->submit_caption = !empty($options['submit_caption']) ? $options['submit_caption'] : 'Save information';
|
||||
$this->trim_inputs = !empty($options['trim_inputs']);
|
||||
}
|
||||
|
||||
public function verify($post)
|
||||
public function getFields()
|
||||
{
|
||||
return $this->fields;
|
||||
}
|
||||
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function getSubmitButtonCaption()
|
||||
{
|
||||
return $this->submit_caption;
|
||||
}
|
||||
|
||||
public function getMissing()
|
||||
{
|
||||
return $this->missing;
|
||||
}
|
||||
|
||||
public function setData($data)
|
||||
{
|
||||
$this->verify($data, true);
|
||||
$this->missing = [];
|
||||
}
|
||||
|
||||
public function setFieldAsMissing($field)
|
||||
{
|
||||
$this->missing[] = $field;
|
||||
}
|
||||
|
||||
public function verify($post, $initalisation = false)
|
||||
{
|
||||
$this->data = [];
|
||||
$this->missing = [];
|
||||
@ -41,30 +77,43 @@ class Form
|
||||
}
|
||||
|
||||
// No data present at all for this field?
|
||||
if ((!isset($post[$field_id]) || $post[$field_id] == '') && empty($field['is_optional']))
|
||||
if ((!isset($post[$field_id]) || $post[$field_id] == '') &&
|
||||
$field['type'] !== 'captcha')
|
||||
{
|
||||
$this->missing[] = $field_id;
|
||||
$this->data[$field_id] = '';
|
||||
if (empty($field['is_optional']))
|
||||
$this->missing[] = $field_id;
|
||||
|
||||
if ($field['type'] === 'select' && !empty($field['multiple']))
|
||||
$this->data[$field_id] = [];
|
||||
else
|
||||
$this->data[$field_id] = '';
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Verify data for all fields
|
||||
// Should we trim this?
|
||||
if ($this->trim_inputs && $field['type'] !== 'captcha' && empty($field['multiple']))
|
||||
$post[$field_id] = trim($post[$field_id]);
|
||||
|
||||
// Using a custom validation function?
|
||||
if (isset($field['validate']) && is_callable($field['validate']))
|
||||
{
|
||||
// Validation functions can clean up the data if passed by reference
|
||||
$this->data[$field_id] = $post[$field_id];
|
||||
|
||||
// Evaluate validation functions as boolean to see if data is missing
|
||||
if (!$field['validate']($post[$field_id]))
|
||||
$this->missing[] = $field_id;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Verify data by field type
|
||||
switch ($field['type'])
|
||||
{
|
||||
case 'select':
|
||||
case 'radio':
|
||||
// Skip validation? Dangerous territory!
|
||||
if (isset($field['verify_options']) && $field['verify_options'] === false)
|
||||
$this->data[$field_id] = $post[$field_id];
|
||||
// Check whether selected option is valid.
|
||||
elseif (isset($post[$field_id]) && !isset($field['options'][$post[$field_id]]))
|
||||
{
|
||||
$this->missing[] = $field_id;
|
||||
$this->data[$field_id] = '';
|
||||
continue 2;
|
||||
}
|
||||
else
|
||||
$this->data[$field_id] = $post[$field_id];
|
||||
$this->validateSelect($field_id, $field, $post);
|
||||
break;
|
||||
|
||||
case 'checkbox':
|
||||
@ -73,61 +122,22 @@ class Form
|
||||
break;
|
||||
|
||||
case 'color':
|
||||
// Colors are stored as a string of length 3 or 6 (hex)
|
||||
if (!isset($post[$field_id]) || (strlen($post[$field_id]) != 3 && strlen($post[$field_id]) != 6))
|
||||
{
|
||||
$this->missing[] = $field_id;
|
||||
$this->data[$field_id] = '';
|
||||
continue 2;
|
||||
}
|
||||
else
|
||||
$this->data[$field_id] = $post[$field_id];
|
||||
$this->validateColor($field_id, $field, $post);
|
||||
break;
|
||||
|
||||
case 'file':
|
||||
// Needs to be verified elsewhere!
|
||||
// Asset needs to be processed out of POST! This is just a filename.
|
||||
$this->data[$field_id] = isset($post[$field_id]) ? $post[$field_id] : '';
|
||||
break;
|
||||
|
||||
case 'numeric':
|
||||
$data = isset($post[$field_id]) ? $post[$field_id] : '';
|
||||
// Do we need to check bounds?
|
||||
if (isset($field['min_value']) && is_numeric($data))
|
||||
{
|
||||
if (is_float($field['min_value']) && (float) $data < $field['min_value'])
|
||||
{
|
||||
$this->missing[] = $field_id;
|
||||
$this->data[$field_id] = 0.0;
|
||||
}
|
||||
elseif (is_int($field['min_value']) && (int) $data < $field['min_value'])
|
||||
{
|
||||
$this->missing[] = $field_id;
|
||||
$this->data[$field_id] = 0;
|
||||
}
|
||||
else
|
||||
$this->data[$field_id] = $data;
|
||||
}
|
||||
elseif (isset($field['max_value']) && is_numeric($data))
|
||||
{
|
||||
if (is_float($field['max_value']) && (float) $data > $field['max_value'])
|
||||
{
|
||||
$this->missing[] = $field_id;
|
||||
$this->data[$field_id] = 0.0;
|
||||
}
|
||||
elseif (is_int($field['max_value']) && (int) $data > $field['max_value'])
|
||||
{
|
||||
$this->missing[] = $field_id;
|
||||
$this->data[$field_id] = 0;
|
||||
}
|
||||
else
|
||||
$this->data[$field_id] = $data;
|
||||
}
|
||||
// Does it look numeric?
|
||||
elseif (is_numeric($data))
|
||||
{
|
||||
$this->data[$field_id] = $data;
|
||||
}
|
||||
// Let's consider it missing, then.
|
||||
else
|
||||
$this->validateNumeric($field_id, $field, $post);
|
||||
break;
|
||||
|
||||
case 'captcha':
|
||||
if (isset($_POST['g-recaptcha-response']) && !$initalisation)
|
||||
$this->validateCaptcha($field_id);
|
||||
elseif (!$initalisation)
|
||||
{
|
||||
$this->missing[] = $field_id;
|
||||
$this->data[$field_id] = 0;
|
||||
@ -137,29 +147,200 @@ class Form
|
||||
case 'text':
|
||||
case 'textarea':
|
||||
default:
|
||||
$this->data[$field_id] = isset($post[$field_id]) ? $post[$field_id] : '';
|
||||
$this->validateText($field_id, $field, $post);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function setData($data)
|
||||
private function validateCaptcha($field_id)
|
||||
{
|
||||
$this->verify($data);
|
||||
$this->missing = [];
|
||||
$postdata = http_build_query([
|
||||
'secret' => RECAPTCHA_API_SECRET,
|
||||
'response' => $_POST['g-recaptcha-response'],
|
||||
]);
|
||||
|
||||
$opts = [
|
||||
'http' => [
|
||||
'method' => 'POST',
|
||||
'header' => 'Content-type: application/x-www-form-urlencoded',
|
||||
'content' => $postdata,
|
||||
]
|
||||
];
|
||||
|
||||
$context = stream_context_create($opts);
|
||||
$result = file_get_contents('https://www.google.com/recaptcha/api/siteverify', false, $context);
|
||||
$check = json_decode($result);
|
||||
|
||||
if ($check->success)
|
||||
{
|
||||
$this->data[$field_id] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->data[$field_id] = 0;
|
||||
$this->missing[] = $field_id;
|
||||
}
|
||||
}
|
||||
|
||||
public function getFields()
|
||||
private function validateColor($field_id, array $field, array $post)
|
||||
{
|
||||
return $this->fields;
|
||||
// Colors are stored as a string of length 3 or 6 (hex)
|
||||
if (!isset($post[$field_id]) || (strlen($post[$field_id]) != 3 && strlen($post[$field_id]) != 6))
|
||||
{
|
||||
$this->missing[] = $field_id;
|
||||
$this->data[$field_id] = '';
|
||||
}
|
||||
else
|
||||
$this->data[$field_id] = $post[$field_id];
|
||||
}
|
||||
|
||||
public function getData()
|
||||
private function validateNumeric($field_id, array $field, array $post)
|
||||
{
|
||||
return $this->data;
|
||||
$data = isset($post[$field_id]) ? $post[$field_id] : '';
|
||||
|
||||
// Sanity check: does this even look numeric?
|
||||
if (!is_numeric($data))
|
||||
{
|
||||
$this->missing[] = $field_id;
|
||||
$this->data[$field_id] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Do we need to a minimum bound?
|
||||
if (isset($field['min_value']))
|
||||
{
|
||||
if (is_float($field['min_value']) && (float) $data < $field['min_value'])
|
||||
{
|
||||
$this->missing[] = $field_id;
|
||||
$this->data[$field_id] = 0.0;
|
||||
}
|
||||
elseif (is_int($field['min_value']) && (int) $data < $field['min_value'])
|
||||
{
|
||||
$this->missing[] = $field_id;
|
||||
$this->data[$field_id] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// What about a maximum bound?
|
||||
if (isset($field['max_value']))
|
||||
{
|
||||
if (is_float($field['max_value']) && (float) $data > $field['max_value'])
|
||||
{
|
||||
$this->missing[] = $field_id;
|
||||
$this->data[$field_id] = 0.0;
|
||||
}
|
||||
elseif (is_int($field['max_value']) && (int) $data > $field['max_value'])
|
||||
{
|
||||
$this->missing[] = $field_id;
|
||||
$this->data[$field_id] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
$this->data[$field_id] = $data;
|
||||
}
|
||||
|
||||
public function getMissing()
|
||||
private function validateSelect($field_id, array $field, array $post)
|
||||
{
|
||||
return $this->missing;
|
||||
// Skip validation? Dangerous territory!
|
||||
if (isset($field['verify_options']) && $field['verify_options'] === false)
|
||||
{
|
||||
$this->data[$field_id] = $post[$field_id];
|
||||
return;
|
||||
}
|
||||
|
||||
// Check whether selected option is valid.
|
||||
if (($field['type'] !== 'select' || empty($field['multiple'])) && empty($field['has_groups']))
|
||||
{
|
||||
if (isset($post[$field_id]) && !isset($field['options'][$post[$field_id]]))
|
||||
{
|
||||
$this->missing[] = $field_id;
|
||||
$this->data[$field_id] = '';
|
||||
return;
|
||||
}
|
||||
else
|
||||
$this->data[$field_id] = $post[$field_id];
|
||||
}
|
||||
// Multiple selections involve a bit more work.
|
||||
elseif (!empty($field['multiple']) && empty($field['has_groups']))
|
||||
{
|
||||
$this->data[$field_id] = [];
|
||||
if (!is_array($post[$field_id]))
|
||||
{
|
||||
if (isset($field['options'][$post[$field_id]]))
|
||||
$this->data[$field_id][] = $post[$field_id];
|
||||
else
|
||||
$this->missing[] = $field_id;
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($post[$field_id] as $option)
|
||||
{
|
||||
if (isset($field['options'][$option]))
|
||||
$this->data[$field_id][] = $option;
|
||||
}
|
||||
|
||||
if (empty($this->data[$field_id]))
|
||||
$this->missing[] = $field_id;
|
||||
}
|
||||
// Any optgroups involved?
|
||||
elseif (!empty($field['has_groups']))
|
||||
{
|
||||
if (!isset($post[$field_id]))
|
||||
{
|
||||
$this->missing[] = $field_id;
|
||||
$this->data[$field_id] = '';
|
||||
return;
|
||||
}
|
||||
|
||||
// Expensive: iterate over all groups until the value selected has been found.
|
||||
foreach ($field['options'] as $label => $options)
|
||||
{
|
||||
if (is_array($options))
|
||||
{
|
||||
// Consider each of the options as a valid a value.
|
||||
foreach ($options as $value => $label)
|
||||
{
|
||||
if ($post[$field_id] === $value)
|
||||
{
|
||||
$this->data[$field_id] = $options;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is an ungrouped value in disguise! Treat it as such.
|
||||
if ($post[$field_id] === $options)
|
||||
{
|
||||
$this->data[$field_id] = $options;
|
||||
return;
|
||||
}
|
||||
else
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// If we've reached this point, we'll consider the data invalid.
|
||||
$this->missing[] = $field_id;
|
||||
$this->data[$field_id] = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new UnexpectedValueException('Unexpected field configuration in validateSelect!');
|
||||
}
|
||||
}
|
||||
|
||||
private function validateText($field_id, array $field, array $post)
|
||||
{
|
||||
$this->data[$field_id] = isset($post[$field_id]) ? $post[$field_id] : '';
|
||||
|
||||
// Trim leading and trailing whitespace?
|
||||
if (!empty($field['trim']))
|
||||
$this->data[$field_id] = trim($this->data[$field_id]);
|
||||
|
||||
// Is there a length limit to enforce?
|
||||
if (isset($field['maxlength']) && strlen($post[$field_id]) > $field['maxlength']) {
|
||||
$post[$field_id] = substr($post[$field_id], 0, $field['maxlength']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,8 @@
|
||||
* GenericTable.php
|
||||
* Contains key class GenericTable.
|
||||
*
|
||||
* Kabuki CMS (C) 2013-2015, Aaron van Geffen
|
||||
* Global Data Lab code (C) Radboud University Nijmegen
|
||||
* Programming (C) Aaron van Geffen, 2015-2021
|
||||
*****************************************************************************/
|
||||
|
||||
class GenericTable
|
||||
@ -19,7 +20,7 @@ class GenericTable
|
||||
|
||||
public $form_above;
|
||||
public $form_below;
|
||||
|
||||
private $table_class;
|
||||
private $sort_direction;
|
||||
private $sort_order;
|
||||
private $base_url;
|
||||
@ -84,6 +85,8 @@ class GenericTable
|
||||
else
|
||||
$this->body = $options['no_items_label'] ?? '';
|
||||
|
||||
$this->table_class = $options['table_class'] ?? '';
|
||||
|
||||
// Got a title?
|
||||
$this->title = $options['title'] ?? '';
|
||||
$this->title_class = $options['title_class'] ?? '';
|
||||
@ -105,6 +108,7 @@ class GenericTable
|
||||
|
||||
$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,
|
||||
'label' => $column['header'],
|
||||
@ -168,6 +172,11 @@ class GenericTable
|
||||
return $this->pageIndex;
|
||||
}
|
||||
|
||||
public function getTableClass()
|
||||
{
|
||||
return $this->table_class;
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
@ -196,6 +205,7 @@ class GenericTable
|
||||
|
||||
// Append the cell to the row.
|
||||
$newRow['cells'][] = [
|
||||
'class' => $column['cell_class'] ?? '',
|
||||
'value' => $value,
|
||||
];
|
||||
}
|
||||
|
@ -11,13 +11,6 @@
|
||||
margin: 0 0 0.2em;
|
||||
}
|
||||
|
||||
.floatleft {
|
||||
float: left;
|
||||
}
|
||||
.floatright {
|
||||
float: right;
|
||||
}
|
||||
|
||||
/* Admin bar styles
|
||||
---------------------*/
|
||||
body {
|
||||
@ -81,22 +74,6 @@ body {
|
||||
}
|
||||
|
||||
|
||||
/* Edit user screen
|
||||
---------------------*/
|
||||
.edituser dt {
|
||||
clear: left;
|
||||
float: left;
|
||||
width: 150px;
|
||||
}
|
||||
.edituser dd {
|
||||
float: left;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.edituser form div:last-child {
|
||||
padding: 1em 0 0;
|
||||
}
|
||||
|
||||
|
||||
/* Admin widgets
|
||||
------------------*/
|
||||
.widget {
|
||||
@ -195,119 +172,3 @@ body {
|
||||
top: 400px;
|
||||
left: 300px;
|
||||
}
|
||||
|
||||
|
||||
/* The pagination styles below are based on Bootstrap 2.3.2
|
||||
-------------------------------------------------------------*/
|
||||
|
||||
.table_pagination, .table_form {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.table_pagination ul {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.table_pagination ul > li {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.table_pagination ul > li > a,
|
||||
.table_pagination ul > li > span {
|
||||
float: left;
|
||||
padding: 4px 12px;
|
||||
line-height: 20px;
|
||||
text-decoration: none;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #dddddd;
|
||||
border-left-width: 0;
|
||||
}
|
||||
|
||||
.table_pagination ul > li > a:hover,
|
||||
.table_pagination ul > li > a:focus,
|
||||
.table_pagination ul > .active > a,
|
||||
.table_pagination ul > .active > span {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.table_pagination ul > .active > a,
|
||||
.table_pagination ul > .active > span {
|
||||
color: #999999;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.table_pagination ul > .disabled > span,
|
||||
.table_pagination ul > .disabled > a,
|
||||
.table_pagination ul > .disabled > a:hover,
|
||||
.table_pagination ul > .disabled > a:focus {
|
||||
color: #999999;
|
||||
cursor: default;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.table_pagination ul > li:first-child > a,
|
||||
.table_pagination ul > li:first-child > span {
|
||||
border-left-width: 1px;
|
||||
}
|
||||
|
||||
|
||||
/* The table styles below were taken from Bootstrap 2.3.2
|
||||
-----------------------------------------------------------*/
|
||||
table {
|
||||
max-width: 100%;
|
||||
background-color: transparent;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
.table {
|
||||
width: 100%;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.table th,
|
||||
.table td {
|
||||
border-top: 1px solid #dddddd;
|
||||
line-height: 20px;
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.table th {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.table thead th {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.table caption + thead tr:first-child th,
|
||||
.table caption + thead tr:first-child td,
|
||||
.table colgroup + thead tr:first-child th,
|
||||
.table colgroup + thead tr:first-child td,
|
||||
.table thead:first-child tr:first-child th,
|
||||
.table thead:first-child tr:first-child td {
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
.table tbody + tbody {
|
||||
border-top: 2px solid #dddddd;
|
||||
}
|
||||
|
||||
.table .table {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.table-striped tbody > tr:nth-child(odd) > td,
|
||||
.table-striped tbody > tr:nth-child(odd) > th {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.table-hover tbody tr:hover > td,
|
||||
.table-hover tbody tr:hover > th {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
}
|
||||
|
||||
body {
|
||||
font: 13px/1.7 "Open Sans", sans-serif;
|
||||
font-family: "Open Sans", sans-serif;
|
||||
padding: 0 0 3em;
|
||||
margin: 0;
|
||||
background: #aaa 0 -50% fixed;
|
||||
@ -94,51 +94,6 @@ ul#nav li a:hover {
|
||||
}
|
||||
|
||||
|
||||
/* Pagination
|
||||
---------------*/
|
||||
.pagination {
|
||||
clear: both;
|
||||
text-align: center;
|
||||
}
|
||||
.pagination ul {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.pagination ul > li {
|
||||
display: inline;
|
||||
}
|
||||
.pagination ul > li > a, .pagination ul > li > span {
|
||||
float: left;
|
||||
font: 300 18px/2.2 "Open Sans", sans-serif;
|
||||
padding: 6px 22px;
|
||||
text-decoration: none;
|
||||
background-color: #fff;
|
||||
border-right: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.pagination ul > li > a:hover, .pagination ul > li > a:focus,
|
||||
.pagination ul > .active > a, .pagination ul > .active > span {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.pagination ul > .active > a, .pagination ul > .active > span {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.pagination ul > .disabled > span, .pagination ul > .disabled > a,
|
||||
.pagination ul > .disabled > a:hover, .pagination ul > .disabled > a:focus {
|
||||
color: #999;
|
||||
cursor: default;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.pagination .page-padding {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
/* Tiled grid
|
||||
---------------*/
|
||||
.tiled_header {
|
||||
@ -372,51 +327,6 @@ footer a {
|
||||
|
||||
/* Input
|
||||
----------*/
|
||||
|
||||
input, select, .btn {
|
||||
background: #fff;
|
||||
border: 1px solid #dbdbdb;
|
||||
border-radius: 4px;
|
||||
color: #000;
|
||||
font: 13px/1.7 "Open Sans", "Helvetica", sans-serif;
|
||||
padding: 3px;
|
||||
}
|
||||
textarea {
|
||||
border: 1px solid #dbdbdb;
|
||||
border-radius: 4px;
|
||||
font: 14px/1.4 'Inconsolata', 'DejaVu Sans Mono', monospace;
|
||||
padding: 0.75%;
|
||||
}
|
||||
|
||||
input[type=submit], button, .btn {
|
||||
background-color: #eee;
|
||||
border-color: #dbdbdb;
|
||||
border-width: 1px;
|
||||
border-radius: 4px;
|
||||
color: #363636;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
justify-content: center;
|
||||
padding-bottom: calc(0.4em - 1px);
|
||||
padding-left: 0.8em;
|
||||
padding-right: 0.8em;
|
||||
padding-top: calc(0.4em - 1px);
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
input:hover, select:hover, button:hover, .btn:hover {
|
||||
border-color: #b5b5b5;
|
||||
}
|
||||
input:focus, select:focus, button:focus, .btn:focus {
|
||||
border-color: #3273dc;
|
||||
}
|
||||
input:focus:not(:active), select:focus:not(:active), button:focus:not(:active), .btn:focus:not(:active) {
|
||||
box-shadow: 0px 0px 0px 2px rgba(50, 115, 220, 0.25);
|
||||
}
|
||||
input:active, select:active, button:active, .btn:active {
|
||||
border-color: #4a4a4a;
|
||||
}
|
||||
|
||||
.btn-red {
|
||||
background: #eebbaa;
|
||||
border-color: #cc9988;
|
||||
@ -474,70 +384,6 @@ input:active, select:active, button:active, .btn:active {
|
||||
}
|
||||
|
||||
|
||||
/* Alert boxes -- styling borrowed from Bootstrap 2
|
||||
-----------------------------------------------------*/
|
||||
.alert {
|
||||
padding: 8px 35px 8px 14px;
|
||||
margin-bottom: 20px;
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
|
||||
background-color: #fcf8e3;
|
||||
border: 1px solid #fbeed5;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.alert,
|
||||
.alert h4 {
|
||||
color: #c09853;
|
||||
}
|
||||
.alert h4 {
|
||||
margin: 0;
|
||||
}
|
||||
.alert .close {
|
||||
position: relative;
|
||||
top: -2px;
|
||||
right: -21px;
|
||||
line-height: 20px;
|
||||
}
|
||||
.alert-success {
|
||||
background-color: #dff0d8;
|
||||
border-color: #d6e9c6;
|
||||
color: #468847;
|
||||
}
|
||||
.alert-success h4 {
|
||||
color: #468847;
|
||||
}
|
||||
.alert-danger,
|
||||
.alert-error {
|
||||
background-color: #f2dede;
|
||||
border-color: #eed3d7;
|
||||
color: #b94a48;
|
||||
}
|
||||
.alert-danger h4,
|
||||
.alert-error h4 {
|
||||
color: #b94a48;
|
||||
}
|
||||
.alert-info {
|
||||
background-color: #d9edf7;
|
||||
border-color: #bce8f1;
|
||||
color: #3a87ad;
|
||||
}
|
||||
.alert-info h4 {
|
||||
color: #3a87ad;
|
||||
}
|
||||
.alert-block {
|
||||
padding-top: 14px;
|
||||
padding-bottom: 14px;
|
||||
}
|
||||
.alert-block > p,
|
||||
.alert-block > ul {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.alert-block p + p {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
|
||||
/* Styling for the photo pages
|
||||
--------------------------------*/
|
||||
#photo_frame {
|
||||
|
@ -26,7 +26,7 @@ class AlbumIndex extends SubTemplate
|
||||
protected function html_content()
|
||||
{
|
||||
echo '
|
||||
<div class="tiled_grid">';
|
||||
<div class="tiled_grid clearfix">';
|
||||
|
||||
foreach (array_chunk($this->albums, 3) as $photos)
|
||||
{
|
||||
|
@ -6,7 +6,7 @@
|
||||
* Kabuki CMS (C) 2013-2015, Aaron van Geffen
|
||||
*****************************************************************************/
|
||||
|
||||
class Alert extends SubTemplate
|
||||
class Alert extends Template
|
||||
{
|
||||
private $_type;
|
||||
private $_message;
|
||||
@ -16,20 +16,20 @@ class Alert extends SubTemplate
|
||||
{
|
||||
$this->_title = $title;
|
||||
$this->_message = $message;
|
||||
$this->_type = in_array($type, ['alert', 'error', 'success', 'info']) ? $type : 'alert';
|
||||
$this->_type = in_array($type, ['success', 'info', 'warning', 'danger']) ? $type : 'info';
|
||||
}
|
||||
|
||||
protected function html_content()
|
||||
public function html_main()
|
||||
{
|
||||
echo '
|
||||
<div class="alert', $this->_type != 'alert' ? ' alert-' . $this->_type : '', '">', (!empty($this->_title) ? '
|
||||
<strong>' . $this->_title . '</strong><br>' : ''), '<p>', $this->_message, '</p>';
|
||||
|
||||
$this->additional_alert_content();
|
||||
|
||||
echo '</div>';
|
||||
<div class="alert', $this->_type !== 'alert' ? ' alert-' . $this->_type : '', '">'
|
||||
, !empty($this->_title) ? '<strong>' . $this->_title . '</strong><br>' : '', '
|
||||
', $this->_message,
|
||||
$this->additional_alert_content(), '
|
||||
</div>';
|
||||
}
|
||||
|
||||
protected function additional_alert_content()
|
||||
{}
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -3,52 +3,42 @@
|
||||
* FormView.php
|
||||
* Contains the form template.
|
||||
*
|
||||
* Kabuki CMS (C) 2013-2015, Aaron van Geffen
|
||||
* Global Data Lab code (C) Radboud University Nijmegen
|
||||
* Programming (C) Aaron van Geffen, 2015-2022
|
||||
*****************************************************************************/
|
||||
|
||||
class FormView extends SubTemplate
|
||||
{
|
||||
private $content_below;
|
||||
private $content_above;
|
||||
private $data;
|
||||
private $missing;
|
||||
private $fields;
|
||||
private $request_method;
|
||||
private $request_url;
|
||||
private $form;
|
||||
private array $data;
|
||||
private array $missing;
|
||||
private $title;
|
||||
|
||||
public function __construct(Form $form, $title = '')
|
||||
{
|
||||
$this->form = $form;
|
||||
$this->title = $title;
|
||||
$this->request_url = $form->request_url;
|
||||
$this->request_method = $form->request_method;
|
||||
$this->fields = $form->getFields();
|
||||
$this->missing = $form->getMissing();
|
||||
$this->data = $form->getData();
|
||||
$this->content_above = $form->content_above;
|
||||
$this->content_below = $form->content_below;
|
||||
}
|
||||
|
||||
protected function html_content($exclude = [], $include = [])
|
||||
{
|
||||
if (!empty($this->title))
|
||||
echo '
|
||||
<div class="admin_box">
|
||||
<h2>', htmlspecialchars($this->title), '</h2>';
|
||||
<h1>', $this->title, '</h1>';
|
||||
|
||||
foreach ($this->_subtemplates as $template)
|
||||
$template->html_main();
|
||||
|
||||
echo '
|
||||
<form action="', $this->request_url, '" method="', $this->request_method, '" enctype="multipart/form-data">';
|
||||
<form action="', $this->form->request_url, '" method="', $this->form->request_method, '" enctype="multipart/form-data">';
|
||||
|
||||
if (isset($this->content_above))
|
||||
echo $this->content_above;
|
||||
if (isset($this->form->content_above))
|
||||
echo $this->form->content_above;
|
||||
|
||||
echo '
|
||||
<dl>';
|
||||
$this->missing = $this->form->getMissing();
|
||||
$this->data = $this->form->getData();
|
||||
|
||||
foreach ($this->fields as $field_id => $field)
|
||||
foreach ($this->form->getFields() as $field_id => $field)
|
||||
{
|
||||
// Either we have a blacklist
|
||||
if (!empty($exclude) && in_array($field_id, $exclude))
|
||||
@ -62,107 +52,230 @@ class FormView extends SubTemplate
|
||||
}
|
||||
|
||||
echo '
|
||||
</dl>
|
||||
<input type="hidden" name="', Session::getSessionTokenKey(), '" value="', Session::getSessionToken(), '">
|
||||
<div style="clear: both">
|
||||
<button type="submit" class="btn btn-primary">Save information</button>';
|
||||
<div class="form-group">
|
||||
<div class="offset-sm-2 col-sm-10">
|
||||
<button type="submit" name="submit" class="btn btn-primary">', $this->form->getSubmitButtonCaption(), '</button>';
|
||||
|
||||
if (isset($this->content_below))
|
||||
if (isset($this->form->content_below))
|
||||
echo '
|
||||
', $this->content_below;
|
||||
', $this->form->content_below;
|
||||
|
||||
echo '
|
||||
</div>
|
||||
</div>
|
||||
</form>';
|
||||
|
||||
if (!empty($this->title))
|
||||
echo '
|
||||
</div>';
|
||||
}
|
||||
|
||||
protected function renderField($field_id, $field)
|
||||
protected function renderField($field_id, array $field)
|
||||
{
|
||||
if (isset($field['before_html']))
|
||||
echo '</dl>
|
||||
', $field['before_html'], '
|
||||
<dl>';
|
||||
|
||||
if ($field['type'] != 'checkbox' && isset($field['label']))
|
||||
echo '
|
||||
<dt class="cont_', $field_id, isset($field['tab_class']) ? ' target target-' . $field['tab_class'] : '', '"', in_array($field_id, $this->missing) ? ' style="color: red"' : '', '>', $field['label'], '</dt>';
|
||||
elseif ($field['type'] === 'checkbox' && isset($field['header']))
|
||||
echo '
|
||||
<dt class="cont_', $field_id, isset($field['tab_class']) ? ' target target-' . $field['tab_class'] : '', '"', in_array($field_id, $this->missing) ? ' style="color: red"' : '', '>', $field['header'], '</dt>';
|
||||
', $field['before_html'];
|
||||
|
||||
echo '
|
||||
<dd class="cont_', $field_id, isset($field['dd_class']) ? ' ' . $field['dd_class'] : '', isset($field['tab_class']) ? ' target target-' . $field['tab_class'] : '', '">';
|
||||
<div class="row mb-2">';
|
||||
|
||||
if (isset($field['before']))
|
||||
echo $field['before'];
|
||||
|
||||
if ($field['type'] !== 'checkbox')
|
||||
if (isset($field['label']))
|
||||
echo '
|
||||
<label class="col-sm-2 col-form-label" for="', $field_id, '"', in_array($field_id, $this->missing) ? ' style="color: red"' : '', '>', $field['label'], ':</label>
|
||||
<div class="', isset($field['class']) ? $field['class'] : 'col-sm-6', '">';
|
||||
else
|
||||
echo '
|
||||
<div class="offset-sm-2 ', isset($field['class']) ? $field['class'] : 'col-sm-6', '">';
|
||||
|
||||
switch ($field['type'])
|
||||
{
|
||||
case 'select':
|
||||
echo '
|
||||
<select name="', $field_id, '" id="', $field_id, '"', !empty($field['disabled']) ? ' disabled' : '', '>';
|
||||
|
||||
if (isset($field['placeholder']))
|
||||
echo '
|
||||
<option value="">', $field['placeholder'], '</option>';
|
||||
|
||||
foreach ($field['options'] as $value => $option)
|
||||
echo '
|
||||
<option value="', $value, '"', $this->data[$field_id] == $value ? ' selected' : '', '>', htmlentities($option), '</option>';
|
||||
|
||||
echo '
|
||||
</select>';
|
||||
$this->renderSelect($field_id, $field);
|
||||
break;
|
||||
|
||||
case 'radio':
|
||||
foreach ($field['options'] as $value => $option)
|
||||
echo '
|
||||
<input type="radio" name="', $field_id, '" value="', $value, '"', $this->data[$field_id] == $value ? ' checked' : '', !empty($field['disabled']) ? ' disabled' : '', '> ', htmlentities($option);
|
||||
$this->renderRadio($field_id, $field);
|
||||
break;
|
||||
|
||||
case 'checkbox':
|
||||
echo '
|
||||
<label><input type="checkbox"', $this->data[$field_id] ? ' checked' : '', !empty($field['disabled']) ? ' disabled' : '', ' name="', $field_id, '"> ', htmlentities($field['label']), '</label>';
|
||||
$this->renderCheckbox($field_id, $field);
|
||||
break;
|
||||
|
||||
case 'textarea':
|
||||
echo '
|
||||
<textarea name="', $field_id, '" id="', $field_id, '" cols="', isset($field['columns']) ? $field['columns'] : 40, '" rows="', isset($field['rows']) ? $field['rows'] : 4, '"', !empty($field['disabled']) ? ' disabled' : '', '>', $this->data[$field_id], '</textarea>';
|
||||
$this->renderTextArea($field_id, $field);
|
||||
break;
|
||||
|
||||
case 'color':
|
||||
echo '
|
||||
<input type="color" name="', $field_id, '" id="', $field_id, '" value="', htmlentities($this->data[$field_id]), '"', !empty($field['disabled']) ? ' disabled' : '', '>';
|
||||
$this->renderColor($field_id, $field);
|
||||
break;
|
||||
|
||||
case 'numeric':
|
||||
echo '
|
||||
<input type="number"', isset($field['step']) ? ' step="' . $field['step'] . '"' : '', ' min="', isset($field['min_value']) ? $field['min_value'] : '0', '" max="', isset($field['max_value']) ? $field['max_value'] : '9999', '" name="', $field_id, '" id="', $field_id, '"', isset($field['size']) ? ' size="' . $field['size'] . '"' : '', isset($field['maxlength']) ? ' maxlength="' . $field['maxlength'] . '"' : '', ' value="', htmlentities($this->data[$field_id]), '"', !empty($field['disabled']) ? ' disabled' : '', '>';
|
||||
$this->renderNumeric($field_id, $field);
|
||||
break;
|
||||
|
||||
case 'file':
|
||||
if (!empty($this->data[$field_id]))
|
||||
echo '<img src="', $this->data[$field_id], '" alt=""><br>';
|
||||
$this->renderFile($field_id, $field);
|
||||
break;
|
||||
|
||||
echo '
|
||||
<input type="file" name="', $field_id, '" id="', $field_id, '"', !empty($field['disabled']) ? ' disabled' : '', '>';
|
||||
case 'captcha':
|
||||
$this->renderCaptcha($field_id, $field);
|
||||
break;
|
||||
|
||||
case 'text':
|
||||
case 'password':
|
||||
default:
|
||||
echo '
|
||||
<input type="', $field['type'], '" name="', $field_id, '" id="', $field_id, '"', isset($field['size']) ? ' size="' . $field['size'] . '"' : '', isset($field['maxlength']) ? ' maxlength="' . $field['maxlength'] . '"' : '', ' value="', htmlentities($this->data[$field_id]), '"', !empty($field['disabled']) ? ' disabled' : '', isset($field['trigger']) ? ' class="trigger-' . $field['trigger'] . '"' : '', '>';
|
||||
$this->renderText($field_id, $field);
|
||||
}
|
||||
|
||||
if (isset($field['after']))
|
||||
echo ' ', $field['after'];
|
||||
|
||||
if ($field['type'] !== 'checkbox')
|
||||
echo '
|
||||
</div>';
|
||||
|
||||
echo '
|
||||
</dd>';
|
||||
</div>';
|
||||
}
|
||||
|
||||
private function renderCaptcha($field_id, array $field)
|
||||
{
|
||||
echo '
|
||||
<div class="g-recaptcha" data-sitekey="', RECAPTCHA_API_KEY, '"></div>
|
||||
<script src="https://www.google.com/recaptcha/api.js"></script>';
|
||||
}
|
||||
|
||||
private function renderCheckbox($field_id, array $field)
|
||||
{
|
||||
echo '
|
||||
<div class="offset-sm-2 col-sm-10">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox"', $this->data[$field_id] ? ' checked' : '', !empty($field['disabled']) ? ' disabled' : '', ' name="', $field_id, '" id="check-', $field_id, '">
|
||||
<label class="form-check-label" for="check-', $field_id, '">
|
||||
', $field['label'], '
|
||||
</label>
|
||||
</div>
|
||||
</div>';
|
||||
}
|
||||
|
||||
private function renderColor($field_id, array $field)
|
||||
{
|
||||
echo '
|
||||
<input class="form-control" type="color" name="', $field_id, '" id="', $field_id, '" value="', htmlspecialchars($this->data[$field_id]), '"', !empty($field['disabled']) ? ' disabled' : '', '>';
|
||||
}
|
||||
|
||||
private function renderFile($field_id, array $field)
|
||||
{
|
||||
if (!empty($this->data[$field_id]))
|
||||
echo 'Currently using asset <tt>', $this->data[$field_id], '</tt>. Upload to overwrite.<br>';
|
||||
|
||||
echo '
|
||||
<input class="form-control" type="file" name="', $field_id, '" id="', $field_id, '"', !empty($field['disabled']) ? ' disabled' : '', '>';
|
||||
}
|
||||
|
||||
private function renderNumeric($field_id, array $field)
|
||||
{
|
||||
echo '
|
||||
<input class="form-control" type="number"',
|
||||
isset($field['step']) ? ' step="' . $field['step'] . '"' : '',
|
||||
' min="', isset($field['min_value']) ? $field['min_value'] : '0', '"',
|
||||
' max="', isset($field['max_value']) ? $field['max_value'] : '9999', '"',
|
||||
' name="', $field_id, '" id="', $field_id, '"',
|
||||
isset($field['size']) ? ' size="' . $field['size'] . '"' : '',
|
||||
isset($field['maxlength']) ? ' maxlength="' . $field['maxlength'] . '"' : '',
|
||||
' value="', htmlspecialchars($this->data[$field_id]), '"',
|
||||
!empty($field['disabled']) ? ' disabled' : '', '>';
|
||||
}
|
||||
|
||||
private function renderRadio($field_id, array $field)
|
||||
{
|
||||
foreach ($field['options'] as $value => $option)
|
||||
echo '
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="', $field_id, '" id="radio-', $field_id, '-', $value, '" value="', $value, '"', $this->data[$field_id] == $value ? ' checked' : '', !empty($field['disabled']) ? ' disabled' : '', '>
|
||||
<label class="form-check-label" for="radio-', $field_id, '-', $value, '">
|
||||
', htmlspecialchars($option), '
|
||||
</label>
|
||||
</div>';
|
||||
}
|
||||
|
||||
private function renderSelect($field_id, array $field)
|
||||
{
|
||||
echo '
|
||||
<select class="form-select" name="', $field_id, !empty($field['multiple']) ? '[]' : '',
|
||||
'" id="', $field_id, '"',
|
||||
!empty($field['disabled']) ? ' disabled' : '',
|
||||
!empty($field['multiple']) ? ' multiple' : '',
|
||||
!empty($field['size']) ? ' size="' . $field['size'] . '"' : '',
|
||||
'>';
|
||||
|
||||
if (isset($field['placeholder']))
|
||||
echo '
|
||||
<option value="">', $field['placeholder'], '</option>';
|
||||
|
||||
foreach ($field['options'] as $key => $value)
|
||||
{
|
||||
if (is_array($value))
|
||||
{
|
||||
assert(empty($field['multiple']));
|
||||
$this->renderSelectOptionGroup($field_id, $key, $value);
|
||||
}
|
||||
else
|
||||
$this->renderSelectOption($field_id, $value, $key, !empty($field['multiple']));
|
||||
}
|
||||
|
||||
echo '
|
||||
</select>';
|
||||
}
|
||||
|
||||
private function renderSelectOption($field_id, $label, $value, $multiple = false)
|
||||
{
|
||||
echo '
|
||||
<option value="', $value, '"',
|
||||
!$multiple && $this->data[$field_id] == $value ? ' selected' : '',
|
||||
$multiple && in_array($value, $this->data[$field_id]) ? ' selected' : '',
|
||||
'>', htmlspecialchars($label), '</option>';
|
||||
}
|
||||
|
||||
private function renderSelectOptionGroup($field_id, $label, $options)
|
||||
{
|
||||
echo '
|
||||
<optgroup label="', $label, '">';
|
||||
|
||||
foreach ($options as $value => $option)
|
||||
$this->renderSelectOption($field_id, $option, $value);
|
||||
|
||||
echo '
|
||||
</optgroup>';
|
||||
}
|
||||
|
||||
private function renderText($field_id, array $field)
|
||||
{
|
||||
echo '
|
||||
<input class="form-control" ',
|
||||
'type="', $field['type'], '" ',
|
||||
'name="', $field_id, '" ',
|
||||
'id="', $field_id, '"',
|
||||
isset($field['size']) ? ' size="' . $field['size'] . '"' : '',
|
||||
isset($field['maxlength']) ? ' maxlength="' . $field['maxlength'] . '"' : '',
|
||||
isset($this->data[$field_id]) ? ' value="' . htmlspecialchars($this->data[$field_id]) . '"' : '',
|
||||
isset($field['placeholder']) ? ' placeholder="' . $field['placeholder'] . '"' : '',
|
||||
!empty($field['disabled']) ? ' disabled' : '',
|
||||
isset($field['trigger']) ? ' class="trigger-' . $field['trigger'] . '"' : '',
|
||||
'>';
|
||||
}
|
||||
|
||||
private function renderTextArea($field_id, array $field)
|
||||
{
|
||||
echo '
|
||||
<textarea class="form-control' .
|
||||
'" name="', $field_id,
|
||||
'" id="', $field_id,
|
||||
'" cols="', isset($field['columns']) ? $field['columns'] : 40,
|
||||
'" rows="', isset($field['rows']) ? $field['rows'] : 4, '"',
|
||||
isset($field['placeholder']) ? ' placeholder="' . $field['placeholder'] . '"' : '',
|
||||
'"', !empty($field['disabled']) ? ' disabled' : '',
|
||||
'>', $this->data[$field_id], '</textarea>';
|
||||
}
|
||||
}
|
||||
|
61
templates/PageIndexWidget.php
Normal file
61
templates/PageIndexWidget.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/*****************************************************************************
|
||||
* PageIndexWidget.php
|
||||
* Contains the template that displays a page index.
|
||||
*
|
||||
* Global Data Lab code (C) Radboud University Nijmegen
|
||||
* Programming (C) Aaron van Geffen, 2015-2022
|
||||
*****************************************************************************/
|
||||
|
||||
class PageIndexWidget extends Template
|
||||
{
|
||||
private $index;
|
||||
private string $class;
|
||||
|
||||
public function __construct(PageIndex $index)
|
||||
{
|
||||
$this->index = $index;
|
||||
$this->class = $index->getPageIndexClass();
|
||||
}
|
||||
|
||||
public function html_main()
|
||||
{
|
||||
self::paginate($this->index, $this->class);
|
||||
}
|
||||
|
||||
public static function paginate(PageIndex $index, $class = null)
|
||||
{
|
||||
$page_index = $index->getPageIndex();
|
||||
if (empty($page_index) || count($page_index) == 1)
|
||||
return;
|
||||
|
||||
if (!isset($class))
|
||||
$class = $index->getPageIndexClass();
|
||||
|
||||
echo '
|
||||
<ul class="pagination', $class ? ' ' . $class : '', '">
|
||||
<li class="page-item', empty($page_index['previous']) ? ' disabled' : '', '">',
|
||||
'<a class="page-link"', !empty($page_index['previous']) ? ' href="' . $page_index['previous']['href'] . '"' : '', '>',
|
||||
'« previous</a></li>';
|
||||
|
||||
foreach ($page_index as $key => $page)
|
||||
{
|
||||
if (!is_numeric($key))
|
||||
continue;
|
||||
|
||||
if (!is_array($page))
|
||||
echo '
|
||||
<li class="page-item disabled"><a class="page-link">...</a></li>';
|
||||
else
|
||||
echo '
|
||||
<li class="page-item', $page['is_selected'] ? ' active" aria-current="page' : '', '">',
|
||||
'<a class="page-link" href="', $page['href'], '">', $page['index'], '</a></li>';
|
||||
}
|
||||
|
||||
echo '
|
||||
<li class="page-item', empty($page_index['next']) ? ' disabled' : '', '">',
|
||||
'<a class="page-link"', !empty($page_index['next']) ? ' href="' . $page_index['next']['href'] . '"' : '', '>',
|
||||
'next »</a></li>
|
||||
</ul>';
|
||||
}
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
<?php
|
||||
/*****************************************************************************
|
||||
* Pagination.php
|
||||
* Contains the pagination template.
|
||||
*
|
||||
* Kabuki CMS (C) 2013-2016, Aaron van Geffen
|
||||
*****************************************************************************/
|
||||
|
||||
class Pagination extends SubTemplate
|
||||
{
|
||||
private $index;
|
||||
private static $unique_index_count = 0;
|
||||
private string $class;
|
||||
|
||||
public function __construct(PageIndex $index)
|
||||
{
|
||||
$this->index = $index;
|
||||
$this->class = $index->getPageIndexClass();
|
||||
}
|
||||
|
||||
protected function html_content()
|
||||
{
|
||||
$index = $this->index->getPageIndex();
|
||||
|
||||
echo '
|
||||
<div class="table_pagination', !empty($this->class) ? ' ' . $this->class : '', '">
|
||||
<ul>
|
||||
<li class="first"><', !empty($index['previous']) ? 'a href="' . $index['previous']['href'] . '"' : 'span', '>« previous</', !empty($index['previous']) ? 'a' : 'span', '></li>';
|
||||
|
||||
$num_wildcards = 0;
|
||||
foreach ($index as $key => $page)
|
||||
{
|
||||
if (!is_numeric($key))
|
||||
continue;
|
||||
|
||||
if (!is_array($page))
|
||||
{
|
||||
$num_wildcards++;
|
||||
echo '
|
||||
<li class="page-padding" onclick="javascript:promptGoToPage(', self::$unique_index_count, ')"><span>...</span></li>';
|
||||
}
|
||||
else
|
||||
echo '
|
||||
<li class="page-number', $page['is_selected'] ? ' active' : '', '"><a href="', $page['href'], '">', $page['index'], '</a></li>';
|
||||
}
|
||||
|
||||
echo '
|
||||
<li class="last"><', !empty($index['next']) ? 'a href="' . $index['next']['href'] . '"' : 'span', '>next »</', !empty($index['next']) ? 'a' : 'span', '></li>
|
||||
</ul>
|
||||
</div>';
|
||||
|
||||
if ($num_wildcards)
|
||||
{
|
||||
echo '
|
||||
<script type="text/javascript">
|
||||
var page_index_', self::$unique_index_count++, ' = {
|
||||
wildcard_url: "', $this->index->getLink("%d"), '",
|
||||
num_pages: ', $this->index->getNumberOfPages(), ',
|
||||
per_page: ', $this->index->getItemsPerPage(), '
|
||||
};
|
||||
</script>';
|
||||
}
|
||||
}
|
||||
}
|
@ -45,7 +45,7 @@ class PhotosIndex extends SubTemplate
|
||||
protected function html_content()
|
||||
{
|
||||
echo '
|
||||
<div class="tiled_grid">';
|
||||
<div class="tiled_grid clearfix">';
|
||||
|
||||
for ($i = $this->row_limit; $i > 0 && $row = $this->mosaic->getRow(); $i--)
|
||||
{
|
||||
|
@ -3,56 +3,73 @@
|
||||
* TabularData.php
|
||||
* Contains the template that displays tabular data.
|
||||
*
|
||||
* Kabuki CMS (C) 2013-2015, Aaron van Geffen
|
||||
* Global Data Lab code (C) Radboud University Nijmegen
|
||||
* Programming (C) Aaron van Geffen, 2015-2022
|
||||
*****************************************************************************/
|
||||
|
||||
class TabularData extends SubTemplate
|
||||
{
|
||||
private Pagination $pager;
|
||||
private GenericTable $_t;
|
||||
|
||||
public function __construct(GenericTable $table)
|
||||
{
|
||||
$this->_t = $table;
|
||||
|
||||
$pageIndex = $table->getPageIndex();
|
||||
if ($pageIndex)
|
||||
$this->pager = new Pagination($pageIndex);
|
||||
}
|
||||
|
||||
protected function html_content()
|
||||
{
|
||||
echo '
|
||||
<div class="admin_box">';
|
||||
|
||||
$title = $this->_t->getTitle();
|
||||
if (!empty($title))
|
||||
{
|
||||
$titleclass = $this->_t->getTitleClass();
|
||||
echo '
|
||||
<h2>', $title, '</h2>';
|
||||
<div class="generic-table', !empty($titleclass) ? ' ' . $titleclass : '', '">
|
||||
<h1>', htmlspecialchars($title), '</h1>';
|
||||
}
|
||||
|
||||
// Showing a page index?
|
||||
if (isset($this->pager))
|
||||
$this->pager->html_content();
|
||||
foreach ($this->_subtemplates as $template)
|
||||
$template->html_main();
|
||||
|
||||
// Maybe even a small form?
|
||||
if (isset($this->_t->form_above))
|
||||
$this->showForm($this->_t->form_above);
|
||||
// Showing an inline form?
|
||||
$pager = $this->_t->getPageIndex();
|
||||
if (!empty($pager) || isset($this->_t->form_above))
|
||||
{
|
||||
echo '
|
||||
<div class="row clearfix justify-content-end">';
|
||||
|
||||
// Page index?
|
||||
if (!empty($pager))
|
||||
PageIndexWidget::paginate($pager);
|
||||
|
||||
// Form controls?
|
||||
if (isset($this->_t->form_above))
|
||||
$this->showForm($this->_t->form_above);
|
||||
|
||||
echo '
|
||||
</div>';
|
||||
}
|
||||
|
||||
$tableClass = $this->_t->getTableClass();
|
||||
if ($tableClass)
|
||||
echo '
|
||||
<div class="', $tableClass, '">';
|
||||
|
||||
// Build the table!
|
||||
echo '
|
||||
<table class="table table-striped">
|
||||
<table class="table table-striped table-condensed">
|
||||
<thead>
|
||||
<tr>';
|
||||
|
||||
// Show the table's headers.
|
||||
foreach ($this->_t->getHeader() as $th)
|
||||
// Show all headers in their full glory!
|
||||
$header = $this->_t->getHeader();
|
||||
foreach ($header as $th)
|
||||
{
|
||||
echo '
|
||||
<th', (!empty($th['width']) ? ' width="' . $th['width'] . '"' : ''), (!empty($th['class']) ? ' class="' . $th['class'] . '"' : ''), ($th['colspan'] > 1 ? ' colspan="' . $th['colspan'] . '"' : ''), ' scope="', $th['scope'], '">',
|
||||
$th['href'] ? '<a href="' . $th['href'] . '">' . $th['label'] . '</a>' : $th['label'];
|
||||
|
||||
if ($th['sort_mode'] )
|
||||
echo ' ', $th['sort_mode'] === 'up' ? '↑' : '↓';
|
||||
if ($th['sort_mode'])
|
||||
echo ' <i class="bi bi-caret-' . ($th['sort_mode'] === 'down' ? 'down' : 'up') . '-fill"></i>';
|
||||
|
||||
echo '</th>';
|
||||
}
|
||||
@ -62,7 +79,7 @@ class TabularData extends SubTemplate
|
||||
</thead>
|
||||
<tbody>';
|
||||
|
||||
// Show the table's body.
|
||||
// The body is what we came to see!
|
||||
$body = $this->_t->getBody();
|
||||
if (is_array($body))
|
||||
{
|
||||
@ -72,51 +89,134 @@ class TabularData extends SubTemplate
|
||||
<tr', (!empty($tr['class']) ? ' class="' . $tr['class'] . '"' : ''), '>';
|
||||
|
||||
foreach ($tr['cells'] as $td)
|
||||
{
|
||||
echo '
|
||||
<td', (!empty($td['width']) ? ' width="' . $td['width'] . '"' : ''), '>', $td['value'], '</td>';
|
||||
<td', (!empty($td['width']) ? ' width="' . $td['width'] . '"' : ''), '>';
|
||||
|
||||
if (!empty($td['class']))
|
||||
echo '<span class="', $td['class'], '">', $td['value'], '</span>';
|
||||
else
|
||||
echo $td['value'];
|
||||
|
||||
echo '</td>';
|
||||
}
|
||||
|
||||
echo '
|
||||
</tr>';
|
||||
}
|
||||
}
|
||||
// !!! Sum colspan!
|
||||
else
|
||||
echo '
|
||||
<tr>
|
||||
<td colspan="', count($this->_t->getHeader()), '">', $body, '</td>
|
||||
<td colspan="', count($header), '" class="fullwidth">', $body, '</td>
|
||||
</tr>';
|
||||
|
||||
echo '
|
||||
</tbody>
|
||||
</table>';
|
||||
|
||||
// Maybe another small form?
|
||||
if (isset($this->_t->form_below))
|
||||
$this->showForm($this->_t->form_below);
|
||||
if ($tableClass)
|
||||
echo '
|
||||
</div>';
|
||||
|
||||
// Showing a page index?
|
||||
if (isset($this->pager))
|
||||
$this->pager->html_content();
|
||||
// Showing an inline form?
|
||||
if (!empty($pager) || isset($this->_t->form_below))
|
||||
{
|
||||
echo '
|
||||
<div class="row clearfix justify-content-end">';
|
||||
|
||||
echo '
|
||||
// Page index?
|
||||
if (!empty($pager))
|
||||
PageIndexWidget::paginate($pager);
|
||||
|
||||
// Form controls?
|
||||
if (isset($this->_t->form_below))
|
||||
$this->showForm($this->_t->form_below);
|
||||
|
||||
echo '
|
||||
</div>';
|
||||
}
|
||||
|
||||
if (!empty($title))
|
||||
echo '
|
||||
</div>';
|
||||
}
|
||||
|
||||
protected function showForm($form)
|
||||
{
|
||||
echo '
|
||||
<form action="', $form['action'], '" method="', $form['method'], '" class="table_form ', $form['class'], '">';
|
||||
<form action="', $form['action'], '" method="', $form['method'], '" class="', $form['class'], '">';
|
||||
|
||||
if (!empty($form['is_group']))
|
||||
echo '
|
||||
<div class="input-group">';
|
||||
|
||||
if (!empty($form['fields']))
|
||||
{
|
||||
foreach ($form['fields'] as $name => $field)
|
||||
echo '
|
||||
<input name="', $name, '" type="', $field['type'], '" placeholder="', $field['placeholder'], '"', isset($field['class']) ? ' class="' . $field['class'] . '"' : '', isset($field['value']) ? ' value="' . $field['value'] . '"' : '', '>';
|
||||
{
|
||||
if ($field['type'] === 'select')
|
||||
{
|
||||
echo '
|
||||
<select class="form-select" name="', $name, '"', (isset($field['onchange']) ? ' onchange="' . $field['onchange'] . '"' : ''), '>';
|
||||
|
||||
foreach ($field['values'] as $value => $caption)
|
||||
{
|
||||
if (!is_array($caption))
|
||||
{
|
||||
echo '
|
||||
<option value="', $value, '"', $value === $field['selected'] ? ' selected' : '', '>', $caption, '</option>';
|
||||
}
|
||||
else
|
||||
{
|
||||
$label = $value;
|
||||
$options = $caption;
|
||||
|
||||
echo '
|
||||
<optgroup label="', $label, '">';
|
||||
|
||||
foreach ($options as $value => $caption)
|
||||
{
|
||||
echo '
|
||||
<option value="', $value, '"', $value === $field['selected'] ? ' selected' : '', '>', $caption, '</option>';
|
||||
}
|
||||
|
||||
echo '
|
||||
</optgroup>';
|
||||
}
|
||||
}
|
||||
|
||||
echo '
|
||||
</select>';
|
||||
}
|
||||
else
|
||||
echo '
|
||||
<input name="', $name, '" id="field_', $name, '" type="', $field['type'], '" placeholder="', $field['placeholder'], '" class="form-control', isset($field['class']) ? ' ' . $field['class'] : '', '"', isset($field['value']) ? ' value="' . htmlspecialchars($field['value']) . '"' : '', '>';
|
||||
|
||||
if (isset($field['html_after']))
|
||||
echo $field['html_after'];
|
||||
}
|
||||
}
|
||||
|
||||
echo '
|
||||
<input type="hidden" name="', Session::getSessionTokenKey(), '" value="', Session::getSessionToken(), '">';
|
||||
|
||||
if (!empty($form['buttons']))
|
||||
foreach ($form['buttons'] as $name => $button)
|
||||
{
|
||||
echo '
|
||||
<input name="', $name, '" type="', $button['type'], '" value="', $button['caption'], '" class="btn', isset($button['class']) ? ' ' . $button['class'] . '' : '', '">';
|
||||
<button class="btn ', isset($button['class']) ? $button['class'] : 'btn-primary', '" type="', $button['type'], '" name="', $name, '">', $button['caption'], '</button>';
|
||||
|
||||
if (isset($button['html_after']))
|
||||
echo $button['html_after'];
|
||||
}
|
||||
|
||||
if (!empty($form['is_group']))
|
||||
echo '
|
||||
</div>';
|
||||
|
||||
echo '
|
||||
</form>';
|
||||
</form>';
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user