<?php namespace HashOver;
// Copyright (C) 2018-2021 Jacob Barkdull
// This file is part of HashOver.
//
// HashOver is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// HashOver is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with HashOver. If not, see <http://www.gnu.org/licenses/>.
// Generates an array of various settings information
function ui_array (Setup $setup, Locale $locale)
{
// Theme names
$themes = array ();
// Themes directory
$themes_directory = $setup->getAbsolutePath ('themes');
// Get each theme directory name
foreach (glob ($themes_directory . '/*', GLOB_ONLYDIR) as $directory) {
$theme = basename ($directory);
$themes[$theme] = $theme;
}
// Form fields options
$field_options = array (
'on' => $locale->text['enabled'],
'required' => $locale->text['required'],
'off' => $locale->text['disabled'],
);
// Return array of settings allowed to be changed
return array (
'general' => array (
'language' => array (
'type' => 'select',
'value' => $setup->language,
'options' => array (
'en-us' => 'English',
'pt-br' => 'Brazilian Portuguese',
'da-dk' => 'Danish',
'nl-nl' => 'Dutch',
'eo-eo' => 'Esperanto',
'fr-fr' => 'French',
'de-de' => 'German',
'el-el' => 'Greek',
'ja-jp' => 'Japanese',
'ko-kr' => 'Korean',
'lt-lt' => 'Lithuanian',
'fa-ir' => 'Persian',
'pl-pl' => 'Polish',
'ro-ro' => 'Romanian',
'ru-ru' => 'Russian',
'zh-cn' => 'Simplified Chinese',
'es-es' => 'Spanish',
'sv-se' => 'Swedish',
'tr-tr' => 'Turkish'
)
),
'theme' => array (
'type' => 'select',
'value' => $setup->theme,
'options' => $themes
),
'default-sorting' => array (
'type' => 'select',
'value' => $setup->defaultSorting,
'options' => array (
'ascending' => $locale->text['sort-ascending'],
'descending' => $locale->text['sort-descending'],
'by-date' => $locale->text['sort-by-date'],
'by-likes' => $locale->text['sort-by-likes'],
'by-replies' => $locale->text['sort-by-replies'],
'by-name' => $locale->text['sort-by-name'],
'threads' => array (
'threaded-descending' => $locale->text['sort-descending'],
'threaded-by-date' => $locale->text['sort-by-date'],
'threaded-by-likes' => $locale->text['sort-by-likes'],
'by-popularity' => $locale->text['sort-by-popularity'],
'by-discussion' => $locale->text['sort-by-discussion'],
'threaded-by-name' => $locale->text['sort-by-name']
)
)
),
'uses-markdown' => array (
'type' => 'checkbox',
'value' => $setup->usesMarkdown
),
'uses-ajax' => array (
'type' => 'checkbox',
'value' => $setup->usesAjax
),
'shows-reply-count' => array (
'type' => 'checkbox',
'value' => $setup->showsReplyCount
),
'allows-images' => array (
'type' => 'checkbox',
'value' => $setup->allowsImages
),
'allows-likes' => array (
'type' => 'checkbox',
'value' => $setup->allowsLikes
),
'allows-dislikes' => array (
'type' => 'checkbox',
'value' => $setup->allowsDislikes
)
),
'moderation' => array (
'uses-moderation' => array (
'type' => 'checkbox',
'value' => $setup->usesModeration
),
'pends-user-edits' => array (
'type' => 'checkbox',
'value' => $setup->pendsUserEdits
)
),
'e-mail' => array (
'sends-notifications' => array (
'type' => 'select',
'value' => $setup->sendsNotifications,
'options' => array (
'to-everyone' => 'Everyone, admin and users',
'to-users' => 'Users only, when they reply to each other',
'to-admin' => 'Admin only',
'to-nobody' => 'Nobody'
)
),
'mail-type' => array (
'type' => 'select',
'value' => $setup->mailType,
'options' => array (
'text' => 'Text',
'html' => 'HTML'
)
),
'mailer' => array (
'type' => 'select',
'value' => $setup->mailer,
'options' => array (
'sendmail' => 'Sendmail',
'smtp' => 'SMTP'
)
),
'subscribes-user' => array (
'type' => 'checkbox',
'value' => $setup->subscribesUser
),
'allows-user-replies' => array (
'type' => 'checkbox',
'value' => $setup->allowsUserReplies
)
),
'cookies' => array (
'sets-cookies' => array (
'type' => 'checkbox',
'value' => $setup->setsCookies
),
'cookie-expiration' => array (
'type' => 'text',
'value' => $setup->cookieExpiration
),
'secure-cookies' => array (
'type' => 'checkbox',
'value' => $setup->secureCookies
)
),
'comment-collapsing' => array (
'collapses-interface' => array (
'type' => 'checkbox',
'value' => $setup->collapsesInterface
),
'collapses-comments' => array (
'type' => 'checkbox',
'value' => $setup->collapsesComments
),
'collapse-limit' => array (
'type' => 'number',
'value' => $setup->collapseLimit
)
),
'popular-comments' => array (
'popularity-threshold' => array (
'type' => 'number',
'value' => $setup->popularityThreshold
),
'popularity-limit' => array (
'type' => 'number',
'value' => $setup->popularityLimit
)
),
'spam-protection' => array (
'spam-database' => array (
'type' => 'select',
'value' => $setup->spamDatabase,
'options' => array (
'remote' => 'StopForumSpam.com',
'local' => 'Local CSV file'
)
),
'spam-check-modes' => array (
'type' => 'select',
'value' => $setup->spamCheckModes,
'options' => array (
'both' => 'Both',
'json' => 'JavaScript',
'php' => 'PHP'
)
)
),
'avatars' => array (
'icon-mode' => array (
'type' => 'select',
'value' => $setup->iconMode,
'options' => array (
'image' => 'Image',
'count' => 'Count',
'none' => 'None'
)
),
'icon-size' => array (
'type' => 'number',
'value' => $setup->iconSize
),
'gravatar-default' => array (
'type' => 'select',
'value' => $setup->gravatarDefault,
'options' => array (
'custom' => 'Custom',
'identicon' => 'Identicon',
'monsterid' => 'Monsterid',
'wavatar' => 'Wavatar',
'retro' => 'Retro'
)
),
'gravatar-force' => array (
'type' => 'checkbox',
'value' => $setup->gravatarForce
)
),
'form-fields' => array (
'form-position' => array (
'type' => 'select',
'value' => $setup->formPosition,
'options' => array (
'top' => 'Top',
'bottom' => 'Bottom'
)
),
'name-field' => array (
'type' => 'select',
'value' => $setup->nameField,
'options' => $field_options
),
'password-field' => array (
'type' => 'select',
'value' => $setup->passwordField,
'options' => $field_options
),
'email-field' => array (
'type' => 'select',
'value' => $setup->emailField,
'options' => $field_options
),
'website-field' => array (
'type' => 'select',
'value' => $setup->websiteField,
'options' => $field_options
),
'displays-title' => array (
'type' => 'checkbox',
'value' => $setup->displaysTitle
),
'uses-cancel-buttons' => array (
'type' => 'checkbox',
'value' => $setup->usesCancelButtons
),
'uses-labels' => array (
'type' => 'checkbox',
'value' => $setup->usesLabels
)
),
'date-time' => array (
'date-pattern' => array (
'type' => 'text',
'value' => $setup->datePattern,
'show' => $setup->hasIntl
),
'time-pattern' => array (
'type' => 'text',
'value' => $setup->timePattern,
'show' => $setup->hasIntl
),
'date-format' => array (
'type' => 'text',
'value' => $setup->dateFormat,
'show' => !$setup->hasIntl
),
'time-format' => array (
'type' => 'text',
'value' => $setup->timeFormat,
'show' => !$setup->hasIntl
),
'server-timezone' => array (
'type' => 'text',
'value' => $setup->serverTimezone
),
'uses-user-timezone' => array (
'type' => 'checkbox',
'value' => $setup->usesUserTimezone
),
'uses-short-dates' => array (
'type' => 'checkbox',
'value' => $setup->usesShortDates
)
),
'login' => array (
'login-method' => array (
'type' => 'select',
'value' => $setup->loginMethod,
'options' => array (
'DefaultLogin' => 'Cookies Login',
'SessionLogin' => 'Session Login'
)
),
'allows-login' => array (
'type' => 'checkbox',
'value' => $setup->allowsLogin
),
'uses-auto-login' => array (
'type' => 'checkbox',
'value' => $setup->usesAutoLogin
)
),
'technical' => array (
'data-format' => array (
'type' => 'select',
'value' => $setup->dataFormat,
'options' => array (
'xml' => 'XML',
'json' => 'JSON',
'sql' => 'SQL'
)
),
'default-name' => array (
'type' => 'text',
'value' => $setup->defaultName
),
'reply-mode' => array (
'type' => 'select',
'value' => $setup->replyMode,
'options' => array (
'thread' => 'Threaded',
'stream' => 'Stream'
)
),
'stream-depth' => array (
'type' => 'number',
'value' => $setup->streamDepth
),
'image-format' => array (
'type' => 'select',
'value' => $setup->imageFormat,
'options' => array (
'png' => 'PNG',
'svg' => 'SVG'
)
),
'appends-css' => array (
'type' => 'checkbox',
'value' => $setup->appendsCss
),
'appends-rss' => array (
'type' => 'checkbox',
'value' => $setup->appendsRss
),
'counts-deletions' => array (
'type' => 'checkbox',
'value' => $setup->countsDeletions
),
'local-metadata' => array (
'type' => 'checkbox',
'value' => $setup->localMetadata
),
'stores-ip-address' => array (
'type' => 'checkbox',
'value' => $setup->storesIpAddress
)
),
'JavaScript' => array (
'minifies-javascript' => array (
'type' => 'checkbox',
'value' => $setup->minifiesJavascript
),
'minify-level' => array (
'type' => 'select',
'cast' => 'number',
'value' => $setup->minifyLevel,
'options' => array (
1 => 'Basic (removes code comments)',
2 => 'Low (removes whitespace + Basic)',
3 => 'Medium (removes newlines + Low)',
4 => 'High (removes extra bits + Medium)'
)
)
)
);
}
// Creates a select element with setting options
function create_select ($hashover, $name, array $setting)
{
// Create wrapper element for dropdown menu
$element = new HTMLTag ('span', array (
'class' => 'select-wrapper'
));
// Create dropdown menu
$select = new HTMLTag ('select', array (
'id' => $name,
'name' => $name,
'size' => 1
));
// Run through setting options
foreach ($setting['options'] as $value => $data) {
// Check if the current option is an array
if (is_array ($data)) {
// If so, add an option group spacer to menu
$select->appendChild (new HTMLTag ('optgroup', array (
'label' => ' '
)));
// Create an option group with localized label
$optgroup = new HTMLTag ('optgroup', array (
'label' => $hashover->locale->text[$value]
));
// Run through each optgroup option
foreach ($data as $opt_value => $opt_text) {
// Create setting option
$option = new HTMLTag ('option', array (
'value' => $opt_value,
'innerHTML' => $opt_text
), false);
// Select proper option
if ($opt_value === $setting['value']) {
$option->createAttribute ('selected', 'true');
}
// Append option to optgroup
$optgroup->appendChild ($option);
}
// And append optgroup to menu
$select->appendChild ($optgroup);
} else {
// If not, create setting option
$option = new HTMLTag ('option', array (
'value' => $value,
'innerHTML' => $data
), false);
// Select proper option
if ($value === $setting['value']) {
$option->createAttribute ('selected', 'true');
}
// Append option to menu
$select->appendChild ($option);
}
}
// Append dropdown menu to wrapper element
$element->appendChild ($select);
return $element;
}
// Creates a paragraph element for a setting value
function create_paragraph ($hashover, $name, array $setting)
{
// Create setting description and value paragraph
$paragraph = new HTMLTag ('p');
// Setting description locale string
$text = $hashover->locale->text['setting-' . $name];
// Create description label
$label = new HTMLTag ('label', array (
'for' => $name,
'innerHTML' => $text
), false);
// Get documentation locale string
$docs = $hashover->locale->text['documentation'];
// Create documentation link
$docs_link = new HTMLTag ('a', array (
'class' => 'docs-link',
'href' => '../docs/en-us/settings/#' . $name,
'target' => '_blank',
'title' => $docs,
'innerHTML' => '?'
), false);
// Handle specific setting types
switch ($setting['type']) {
// Create checkbox for enabling/disabling the setting
case 'checkbox': {
// Create checkbox type input element
$element = new HTMLTag ('input', array (
'id' => $name,
'type' => 'checkbox',
'name' => $name
), false, true);
// Set check based on current setting
if ($setting['value'] !== false) {
$element->createAttribute ('checked', 'true');
}
break;
}
// Create text/number box for entering the setting value
case 'number' : case 'text': {
// Add class to make label a block element
$label->createAttribute ('class', 'block');
// And create number or text type input element
$element = new HTMLTag ('input', array (
'id' => $name,
'type' => $setting['type'],
'name' => $name,
'value' => $setting['value'],
'size' => ($setting['type'] === 'text') ? '25' : '10'
), false, true);
break;
}
// Create dropdown menu for selecting the setting value
case 'select': {
// Add class to make label a block element
$label->createAttribute ('class', 'block');
// Create select element
$element = create_select ($hashover, $name, $setting);
break;
}
}
// Check if setting is a checkbox
if ($setting['type'] === 'checkbox') {
// If so, append setting value element to paragraph
$paragraph->appendChild ($element);
// Then append label element to paragraph
$paragraph->appendChild ($label);
// Append documentation link to description and value paragraph
$paragraph->appendChild ($docs_link);
} else {
// If not, append documentation link to description label
$label->appendChild ($docs_link);
// Append label element to paragraph
$paragraph->appendChild ($label);
// Then append setting value element to paragraph
$paragraph->appendChild ($element);
}
// And return paragraph element
return $paragraph;
}
try {
// Do some standard HashOver setup work
require (realpath ('../../backend/standard-setup.php'));
// View setup
require (realpath ('../view-setup.php'));
// Get array of UI elements to create
$ui = ui_array ($hashover->setup, $hashover->locale);
// Check if the form has been submitted
if (isset ($_POST['save'])) {
// Settings JSON file path
$settings_file = $hashover->setup->getAbsolutePath ('config/settings.json');
// Read JSON settings file
$json = $data_files->readJSON ($settings_file);
// Existing JSON settings or an empty array
$settings = ($json !== false) ? $json : array ();
// Run through configurable settings
foreach ($ui as $items) {
// Run through each settings category
foreach ($items as $name => $setting) {
// Use specified type or optional cast
$type = !empty ($setting['cast']) ? 'cast' : 'type';
// Handle specific setting types
switch ($setting[$type]) {
// Set value to boolean based on POST data
case 'checkbox': {
$settings[$name] = isset ($_POST[$name]);
break;
}
// Cast number values to integers
case 'number': {
$settings[$name] = (int)($_POST[$name]);
break;
}
// All other values are strings
default: {
// Check if setting has a value
if (!empty ($_POST[$name])) {
// If so, cast it to string before setting it
$settings[$name] = (string)($_POST[$name]);
} else {
// If not, remove the setting entirely
unset ($settings[$name]);
}
break;
}
}
}
}
// Check if the user login is admin
if ($hashover->login->verifyAdmin () === true) {
// If so, attempt to save the settings
$saved = $data_files->saveJSON ($settings_file, $settings);
// If saved successfully, redirect with success indicator
if ($saved === true) {
redirect ('./?status=success');
}
}
// Otherwise, redirect with failure indicator
redirect ('./?status=failure');
}
// Otherwise, create settings div
$div = new HTMLTag ('div', array (
'id' => 'settings'
));
// Create settings divs
foreach ($ui as $locale => $settings) {
// Replace key with locale string if one exists
if (!empty ($hashover->locale->text[$locale])) {
$locale = $hashover->locale->text[$locale];
}
// Append settings category text to settings div
$div->appendChild (new HTMLTag ('p', array (
'class' => 'settings-category',
'innerHTML' => $locale
), false));
// Create settings items element
$items = new HTMLTag ('div', array (
'class' => 'settings'
));
// Run through settings
foreach ($settings as $name => $setting) {
// Append each settings category items to div if they are to be shown
if (!isset ($setting['show']) or $setting['show'] === true) {
$items->appendChild (create_paragraph ($hashover, $name, $setting));
}
}
// Append settings items element to div
$div->appendChild ($items);
}
// Template data
$template = array (
'title' => $hashover->locale->text['settings'],
'sub-title' => $hashover->locale->text['settings-sub'],
'settings' => $div->asHTML ("\t"),
'save-button' => $hashover->locale->text['save']
);
// Load and parse HTML template
echo parse_templates ('admin', 'settings.html', $template, $hashover);
} catch (\Exception $error) {
echo Misc::displayException ($error);
}