aboutsummaryrefslogtreecommitdiff
path: root/bootstrap/comments/admin
diff options
context:
space:
mode:
authorThedro Neely <thedroneely@gmail.com>2018-08-30 04:40:53 -0400
committerThedro Neely <thedroneely@gmail.com>2018-08-30 04:40:53 -0400
commit2659205908bd5cab508f4ff817123673e078ab74 (patch)
treee455f36b124e9b98f7005e6d4310b71881734623 /bootstrap/comments/admin
downloadedwinmattiacci.com-2659205908bd5cab508f4ff817123673e078ab74.tar.gz
edwinmattiacci.com-2659205908bd5cab508f4ff817123673e078ab74.tar.bz2
edwinmattiacci.com-2659205908bd5cab508f4ff817123673e078ab74.zip
Initialize Repo: First Commit
Diffstat (limited to 'bootstrap/comments/admin')
-rw-r--r--bootstrap/comments/admin/admin.css102
-rw-r--r--bootstrap/comments/admin/admin.html56
-rw-r--r--bootstrap/comments/admin/admin.js41
-rw-r--r--bootstrap/comments/admin/index.php58
-rw-r--r--bootstrap/comments/admin/views/blocklist/blocklist.html44
-rw-r--r--bootstrap/comments/admin/views/blocklist/blocklist.js30
-rw-r--r--bootstrap/comments/admin/views/blocklist/index.php104
-rw-r--r--bootstrap/comments/admin/views/documentation/documentation.html27
-rw-r--r--bootstrap/comments/admin/views/documentation/index.php38
-rw-r--r--bootstrap/comments/admin/views/example/example.html30
-rw-r--r--bootstrap/comments/admin/views/example/index.php38
-rw-r--r--bootstrap/comments/admin/views/login/index.php92
-rw-r--r--bootstrap/comments/admin/views/login/login.css27
-rw-r--r--bootstrap/comments/admin/views/login/login.html37
-rw-r--r--bootstrap/comments/admin/views/login/login.js10
-rw-r--r--bootstrap/comments/admin/views/moderation/index.php88
-rw-r--r--bootstrap/comments/admin/views/moderation/moderation.css3
-rw-r--r--bootstrap/comments/admin/views/moderation/moderation.html30
-rw-r--r--bootstrap/comments/admin/views/moderation/threads.css27
-rw-r--r--bootstrap/comments/admin/views/moderation/threads.html34
-rw-r--r--bootstrap/comments/admin/views/moderation/threads.js21
-rw-r--r--bootstrap/comments/admin/views/moderation/threads.php33
-rw-r--r--bootstrap/comments/admin/views/settings/index.php515
-rw-r--r--bootstrap/comments/admin/views/settings/settings.css15
-rw-r--r--bootstrap/comments/admin/views/settings/settings.html41
-rw-r--r--bootstrap/comments/admin/views/shared/update-elements.js12
-rw-r--r--bootstrap/comments/admin/views/updates/index.php38
-rw-r--r--bootstrap/comments/admin/views/updates/updates.html27
-rw-r--r--bootstrap/comments/admin/views/url-queries/index.php134
-rw-r--r--bootstrap/comments/admin/views/url-queries/url-queries.html44
-rw-r--r--bootstrap/comments/admin/views/url-queries/url-queries.js36
-rw-r--r--bootstrap/comments/admin/views/view-setup.php107
32 files changed, 1939 insertions, 0 deletions
diff --git a/bootstrap/comments/admin/admin.css b/bootstrap/comments/admin/admin.css
new file mode 100644
index 0000000..f2be72b
--- /dev/null
+++ b/bootstrap/comments/admin/admin.css
@@ -0,0 +1,102 @@
+body, html {
+ width: 100%;
+ min-width: 100%;
+ height: 100%;
+ min-height: 100%;
+}
+
+body {
+ padding: 0px 0px 0px 230px;
+}
+
+#sidebar {
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ width: 230px;
+ height: 100%;
+ border-right: #CCCCCC;
+ border-right: 1px solid #CCCCCC;
+ background-color: #F5F5F5;
+ overflow: auto;
+}
+
+#navigation {
+ border-top: 1px solid #CCCCCC;
+ border-bottom: 1px solid #CCCCCC;
+}
+
+#logo,
+#navigation div,
+#navigation a {
+ position: relative;
+ display: block;
+ width: 100%;
+ vertical-align: top;
+ overflow: hidden;
+}
+
+#navigation div {
+ border: none;
+ border-bottom: 1px solid #CCCCCC;
+}
+
+#navigation div:last-child {
+ border-bottom: none;
+}
+
+#logo,
+#navigation a {
+ padding: 15px;
+ margin: 0px;
+ border: none;
+}
+
+#navigation a,
+#navigation:hover a.active {
+ border: none;
+ border-right: 4px solid transparent;
+ color: #222222;
+}
+
+#navigation a.active {
+ color: #266394;
+ border-right: 4px solid #3485C7;
+ background-color: #EEEEEE;
+ font-weight: bold;
+}
+
+#navigation:hover a.active {
+ background-color: transparent;
+}
+
+#navigation a:hover,
+#navigation a.active:hover {
+ color: #266394;
+ border-color: #3485C7;
+ background-color: #EEEEEE;
+}
+
+div#logo,
+div#logo a {
+ height: 234px;
+}
+
+div#logo a,
+#navigation:hover div#logo a {
+ border: none;
+ border-bottom: 4px solid transparent;
+}
+
+div#logo a:hover,
+div#logo a.active {
+ border: none;
+ border-bottom: 4px solid #5E94FF;
+}
+
+#content {
+ width: 100%;
+ height: 100%;
+ vertical-align: top;
+ background-color: #FFFFFF;
+}
diff --git a/bootstrap/comments/admin/admin.html b/bootstrap/comments/admin/admin.html
new file mode 100644
index 0000000..155d743
--- /dev/null
+++ b/bootstrap/comments/admin/admin.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+
+<html lang="en" dir="ltr">
+ <head>
+ <title>HashOver - {hashover:title}</title>
+
+ <link type="text/css" href="../themes/default/general.css" rel="stylesheet">
+ <link type="text/css" href="../themes/default/special.css" rel="stylesheet">
+ <link type="text/css" href="admin.css" rel="stylesheet">
+
+ <script type="text/javascript" src="admin.js"></script>
+
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8">
+ <meta http-equiv="Content-Language" content="EN">
+
+ <link type="image/x-icon" href="../images/favicon.png" rel="shortcut icon">
+ <link type="image/x-icon" href="../images/favicon.png" rel="icon">
+ <link href="https://www.gnu.org/licenses/agpl" rel="copyright">
+ </head>
+
+ <body>
+ <div id="sidebar">
+ <div id="logo">
+ <img src="../images/hashover-logo.png" alt="HashOver" width="100%">
+ </div>
+
+ <div id="navigation">
+ <div>
+ <a class="view-link" href="views/moderation/" target="content">{hashover:moderation}</a>
+ </div>
+
+ <div>
+ <a class="view-link" href="views/blocklist/" target="content">{hashover:block-ip-addresses}</a>
+ </div>
+
+ <div>
+ <a class="view-link" href="views/url-queries/" target="content">{hashover:filter-url-queries}</a>
+ </div>
+
+ <div>
+ <a class="view-link" href="views/updates/" target="content">{hashover:check-for-updates}</a>
+ </div>
+
+ <div>
+ <a class="view-link" href="views/documentation/" target="content">{hashover:documentation}</a>
+ </div>
+
+ <div>
+ <a class="view-link" href="views/settings/" target="content">{hashover:settings}</a>
+ </div>
+ </div>
+ </div>
+
+ <iframe id="content" name="content" src="views/moderation/" frameborder="0"></iframe>
+ </body>
+</html>
diff --git a/bootstrap/comments/admin/admin.js b/bootstrap/comments/admin/admin.js
new file mode 100644
index 0000000..046735b
--- /dev/null
+++ b/bootstrap/comments/admin/admin.js
@@ -0,0 +1,41 @@
+// Wait for the page HTML to be parsed
+document.addEventListener ('DOMContentLoaded', function () {
+ // Get view links
+ var viewLinks = document.getElementsByClassName ('view-link');
+
+ // Get content frame
+ var content = document.getElementById ('content');
+
+ // Execute a given function for each view link
+ function eachViewLink (callback)
+ {
+ for (var i = 0, il = viewLinks.length; i < il; i++) {
+ callback (viewLinks[i]);
+ }
+ }
+
+ // Remove active class from all view links
+ function clearViewTabs ()
+ {
+ eachViewLink (function (link) {
+ link.className = 'view-link';
+ });
+ }
+
+ // Automatically select the proper view tab on page load
+ content.onload = function ()
+ {
+ // Remove active class from all view links
+ clearViewTabs ();
+
+ // Select active proper tab for currently loaded view
+ eachViewLink (function (link) {
+ var regex = new RegExp (link.getAttribute ('href'));
+ var frameUrl = content.contentDocument.location.href;
+
+ if (regex.test (decodeURIComponent (frameUrl))) {
+ link.className += ' active';
+ }
+ });
+ };
+}, false);
diff --git a/bootstrap/comments/admin/index.php b/bootstrap/comments/admin/index.php
new file mode 100644
index 0000000..d7a29bc
--- /dev/null
+++ b/bootstrap/comments/admin/index.php
@@ -0,0 +1,58 @@
+<?php namespace HashOver;
+
+// Copyright (C) 2018 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/>.
+
+
+// Do some standard HashOver setup work
+require (realpath ('../backend/standard-setup.php'));
+
+// Autoload class files
+spl_autoload_register (function ($uri) {
+ $uri = str_replace ('\\', '/', strtolower ($uri));
+ $class_name = basename ($uri);
+
+ if (!@include (realpath ('../backend/classes/' . $class_name . '.php'))) {
+ echo '"' . $class_name . '.php" file could not be included!';
+ exit;
+ }
+});
+
+try {
+ // Instantiate HashOver class
+ $hashover = new \HashOver ();
+ $hashover->initiate ();
+ $hashover->finalize ();
+
+ // Template data
+ $template = array (
+ 'title' => $hashover->locale->text['admin'],
+ 'moderation' => $hashover->locale->text['moderation'],
+ 'block-ip-addresses' => $hashover->locale->text['block-ip-addresses'],
+ 'filter-url-queries' => $hashover->locale->text['filter-url-queries'],
+ 'check-for-updates' => $hashover->locale->text['check-for-updates'],
+ 'documentation' => $hashover->locale->text['documentation'],
+ 'settings' => $hashover->locale->text['settings']
+ );
+
+ // Load and parse HTML template
+ echo $hashover->templater->parseTemplate ('admin.html', $template);
+
+} catch (\Exception $error) {
+ $misc = new Misc ('php');
+ $message = $error->getMessage ();
+ $misc->displayError ($message);
+}
diff --git a/bootstrap/comments/admin/views/blocklist/blocklist.html b/bootstrap/comments/admin/views/blocklist/blocklist.html
new file mode 100644
index 0000000..86dd089
--- /dev/null
+++ b/bootstrap/comments/admin/views/blocklist/blocklist.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+
+<html lang="en" dir="ltr">
+ <head>
+ <title>HashOver - {hashover:title}</title>
+
+ <link type="text/css" href="../../../themes/default/general.css" rel="stylesheet">
+ <link type="text/css" href="../../../themes/default/special.css" rel="stylesheet">
+
+ <script type="text/javascript" src="../shared/update-elements.js"></script>
+ <script type="text/javascript" src="blocklist.js"></script>
+
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8">
+ <meta http-equiv="Content-Language" content="EN">
+
+ <link type="image/x-icon" href="../../../images/favicon.png" rel="shortcut icon">
+ <link type="image/x-icon" href="../../../images/favicon.png" rel="icon">
+ <link href="https://www.gnu.org/licenses/agpl" rel="copyright">
+ </head>
+
+ <body>
+ <h1 class="underlined">
+ <span id="title">{hashover:title}</span>
+
+ {hashover:logout}
+ </h1>
+
+ <div class="p-spaced muted-text">
+ {hashover:sub-title}
+ {hashover:message}
+ </div>
+
+ <form method="post">
+ <p id="ip-list">
+ {hashover:inputs}
+ </p>
+
+ <p>
+ <input id="save-button" type="submit" value="{hashover:save-button}">
+ <input id="new-button" type="button" value="+">
+ </p>
+ </form>
+ </body>
+</html>
diff --git a/bootstrap/comments/admin/views/blocklist/blocklist.js b/bootstrap/comments/admin/views/blocklist/blocklist.js
new file mode 100644
index 0000000..4b04dbf
--- /dev/null
+++ b/bootstrap/comments/admin/views/blocklist/blocklist.js
@@ -0,0 +1,30 @@
+// Wait for the page HTML to be parsed
+document.addEventListener ('DOMContentLoaded', function () {
+ // Get the "New Address" and "Save" buttons
+ var newButton = document.getElementById ('new-button');
+ var ipList = document.getElementById ('ip-list');
+ var saveButton = document.getElementById ('save-button');
+
+ newButton.onclick = function ()
+ {
+ // Create input and indentation
+ var addresses = document.getElementsByClassName ('addresses');
+ var indentation = document.createTextNode ('\n\t\t\t\t');
+
+ // Clone the first address field
+ var input = addresses[0].cloneNode (true);
+
+ // Remove its value
+ input.value = '';
+
+ // Append indentation and input to IP address list
+ ipList.appendChild (indentation);
+ ipList.appendChild (input);
+ };
+
+ // Disable the "Save" button when clicked
+ saveButton.onclick = function ()
+ {
+ this.disabled = true;
+ };
+}, false);
diff --git a/bootstrap/comments/admin/views/blocklist/index.php b/bootstrap/comments/admin/views/blocklist/index.php
new file mode 100644
index 0000000..8d27633
--- /dev/null
+++ b/bootstrap/comments/admin/views/blocklist/index.php
@@ -0,0 +1,104 @@
+<?php namespace HashOver;
+
+// Copyright (C) 2018 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/>.
+
+
+try {
+ // View setup
+ require (realpath ('../view-setup.php'));
+
+ // Default blocklist array
+ $blocklist = array ();
+
+ // Blocklist JSON file location
+ $blocklist_file = $hashover->setup->getAbsolutePath ('config/blocklist.json');
+
+ // Check if the form has been submitted
+ if (!empty ($_POST['addresses']) and is_array ($_POST['addresses'])) {
+ // If so, run through submitted addresses
+ foreach ($_POST['addresses'] as $address) {
+ // Add each non-empty address value to the blocklist array
+ if (!empty ($address)) {
+ $blocklist[] = $address;
+ }
+ }
+
+ // Save the JSON data to the blocklist file
+ if ($hashover->setup->verifyAdmin ($hashover->login->password)
+ and $data_files->saveJSON ($blocklist_file, $blocklist))
+ {
+ // Redirect with success indicator
+ header ('Location: index.php?status=success');
+ } else {
+ // Redirect with failure indicator
+ header ('Location: index.php?status=failure');
+ }
+
+ // Exit after redirect
+ exit;
+ }
+
+ // Otherwise, load and parse blocklist file
+ $json = $data_files->readJSON ($blocklist_file);
+
+ // Check for JSON parse error
+ if (is_array ($json)) {
+ $blocklist = $json;
+ }
+
+ // IP Address inputs
+ $inputs = new HTMLTag ('span');
+
+ // Create IP address inputs
+ for ($i = 0, $il = max (3, count ($blocklist)); $i < $il; $i++) {
+ // Use IP address from file or blank
+ $address = !empty ($blocklist[$i]) ? $blocklist[$i] : '';
+
+ // Create input tag
+ $input = new HTMLTag ('input', array (
+ 'class' => 'addresses',
+ 'type' => 'text',
+ 'name' => 'addresses[]',
+ 'value' => $address,
+ 'size' => '15',
+ 'maxlength' => '15',
+ 'placeholder' => '127.0.0.1',
+ 'title' => $hashover->locale->text['blocklist-ip-tip']
+ ), false, true);
+
+ // Add input to inputs container
+ $inputs->appendChild ($input);
+ }
+
+ // Template data
+ $template = array (
+ 'title' => $hashover->locale->text['blocklist-title'],
+ 'logout' => $logout->asHTML ("\t\t\t"),
+ 'sub-title' => $hashover->locale->text['blocklist-sub'],
+ 'message' => $form_message,
+ 'inputs' => $inputs->getInnerHTML ("\t\t\t\t"),
+ 'save-button' => $hashover->locale->text['save']
+ );
+
+ // Load and parse HTML template
+ echo $hashover->templater->parseTemplate ('blocklist.html', $template);
+
+} catch (\Exception $error) {
+ $misc = new Misc ('php');
+ $message = $error->getMessage ();
+ $misc->displayError ($message);
+}
diff --git a/bootstrap/comments/admin/views/documentation/documentation.html b/bootstrap/comments/admin/views/documentation/documentation.html
new file mode 100644
index 0000000..be82e62
--- /dev/null
+++ b/bootstrap/comments/admin/views/documentation/documentation.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+
+<html lang="en" dir="ltr">
+ <head>
+ <title>HashOver - {hashover:title}</title>
+
+ <link type="text/css" href="../../../themes/default/general.css" rel="stylesheet">
+ <link type="text/css" href="../../../themes/default/special.css" rel="stylesheet">
+
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8">
+ <meta http-equiv="Content-Language" content="EN">
+
+ <link type="image/x-icon" href="../../../images/favicon.png" rel="shortcut icon">
+ <link type="image/x-icon" href="../../../images/favicon.png" rel="icon">
+ <link href="https://www.gnu.org/licenses/agpl" rel="copyright">
+ </head>
+
+ <body>
+ <h1 class="underlined">
+ {hashover:title}
+
+ {hashover:logout}
+ </h1>
+
+ <p class="muted-text">{hashover:sub-title}</p>
+ </body>
+</html>
diff --git a/bootstrap/comments/admin/views/documentation/index.php b/bootstrap/comments/admin/views/documentation/index.php
new file mode 100644
index 0000000..74a62be
--- /dev/null
+++ b/bootstrap/comments/admin/views/documentation/index.php
@@ -0,0 +1,38 @@
+<?php namespace HashOver;
+
+// Copyright (C) 2018 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/>.
+
+
+try {
+ // View setup
+ require (realpath ('../view-setup.php'));
+
+ // Template data
+ $template = array (
+ 'title' => $hashover->locale->text['documentation'],
+ 'logout' => $logout->asHTML ("\t\t\t"),
+ 'sub-title' => $hashover->locale->text['coming-soon']
+ );
+
+ // Load and parse HTML template
+ echo $hashover->templater->parseTemplate ('documentation.html', $template);
+
+} catch (\Exception $error) {
+ $misc = new Misc ('php');
+ $message = $error->getMessage ();
+ $misc->displayError ($message);
+}
diff --git a/bootstrap/comments/admin/views/example/example.html b/bootstrap/comments/admin/views/example/example.html
new file mode 100644
index 0000000..1e4464c
--- /dev/null
+++ b/bootstrap/comments/admin/views/example/example.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+
+<html lang="en" dir="ltr">
+ <head>
+ <title>HashOver - {hashover:title}</title>
+
+ <link type="text/css" href="../../../themes/default/general.css" rel="stylesheet">
+ <link type="text/css" href="../../../themes/default/special.css" rel="stylesheet">
+ <link type="text/css" href="example.css" rel="stylesheet">
+
+ <script type="text/javascript" src="example.js"></script>
+
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8">
+ <meta http-equiv="Content-Language" content="EN">
+
+ <link type="image/x-icon" href="../../../images/favicon.png" rel="shortcut icon">
+ <link type="image/x-icon" href="../../../images/favicon.png" rel="icon">
+ <link href="https://www.gnu.org/licenses/agpl" rel="copyright">
+ </head>
+
+ <body>
+ <h1 class="underlined">
+ {hashover:title}
+
+ {hashover:logout}
+ </h1>
+
+ <p class="muted-text">{hashover:sub-title}</p>
+ </body>
+</html>
diff --git a/bootstrap/comments/admin/views/example/index.php b/bootstrap/comments/admin/views/example/index.php
new file mode 100644
index 0000000..7848daf
--- /dev/null
+++ b/bootstrap/comments/admin/views/example/index.php
@@ -0,0 +1,38 @@
+<?php namespace HashOver;
+
+// Copyright (C) 2018 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/>.
+
+
+try {
+ // View setup
+ require (realpath ('../view-setup.php'));
+
+ // Template data
+ $template = array (
+ 'title' => $hashover->locale->text['example'],
+ 'logout' => $logout->asHTML ("\t\t\t"),
+ 'sub-title' => $hashover->locale->text['example']
+ );
+
+ // Load and parse HTML template
+ echo $hashover->templater->parseTemplate ('example.html', $template);
+
+} catch (\Exception $error) {
+ $misc = new Misc ('php');
+ $message = $error->getMessage ();
+ $misc->displayError ($message);
+}
diff --git a/bootstrap/comments/admin/views/login/index.php b/bootstrap/comments/admin/views/login/index.php
new file mode 100644
index 0000000..1913ece
--- /dev/null
+++ b/bootstrap/comments/admin/views/login/index.php
@@ -0,0 +1,92 @@
+<?php namespace HashOver;
+
+// Copyright (C) 2018 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/>.
+
+
+// Redirects the user back to where they came from
+function redirect ($url = '')
+{
+ // Check if we're redirecting to a specific URL
+ if (!empty ($url)) {
+ // If so, use it
+ header ('Location: ' . $url);
+ } else {
+ // If not, check if there is a redirect specified
+ if (!empty ($_GET['redirect'])) {
+ // If so, use it
+ header ('Location: ' . $_GET['redirect']);
+ } else {
+ // If not, redirect to moderation
+ header ('Location: ../moderation/');
+ }
+ }
+
+ // Exit after redirect
+ exit;
+}
+
+try {
+ // View setup
+ require (realpath ('../view-setup.php'));
+
+ // Check if the user submitted login information
+ if (!empty ($_POST['name']) and !empty ($_POST['password'])) {
+ // If so, attempt to log them in
+ $hashover->login->setLogin ();
+
+ // Check if the user is not admin
+ if ($hashover->setup->adminLogin ($hashover->login->loginHash) === false) {
+ // If so, logout
+ $hashover->login->clearLogin ();
+
+ // Sleep 5 seconds
+ sleep (5);
+ }
+
+ // And redirect user to desired view
+ redirect ();
+ }
+
+ // Check if we're logging out
+ if (isset ($_GET['logout'])) {
+ // If so, attempt to log the user out
+ $hashover->login->clearLogin ();
+
+ // And redirect user to main admin page
+ redirect ($hashover->setup->getHttpPath ('admin'));
+ }
+
+ // Template data
+ $template = array (
+ 'title' => $hashover->locale->text['login'],
+ 'logout' => $logout->asHTML ("\t\t\t"),
+ 'sub-title' => $hashover->locale->text['admin-required'],
+ 'name' => $hashover->locale->text['name'],
+ 'password' => $hashover->locale->text['password'],
+ 'email' => $hashover->locale->optionalize ('email'),
+ 'website' => $hashover->locale->optionalize ('website'),
+ 'login' => $hashover->locale->text['login']
+ );
+
+ // Load and parse HTML template
+ echo $hashover->templater->parseTemplate ('login.html', $template);
+
+} catch (\Exception $error) {
+ $misc = new Misc ('php');
+ $message = $error->getMessage ();
+ $misc->displayError ($message);
+}
diff --git a/bootstrap/comments/admin/views/login/login.css b/bootstrap/comments/admin/views/login/login.css
new file mode 100644
index 0000000..37e0547
--- /dev/null
+++ b/bootstrap/comments/admin/views/login/login.css
@@ -0,0 +1,27 @@
+body {
+ background-image: url('../../../images/white-noise.png');
+}
+
+#login {
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ padding: 15px;
+ margin: -133px 0px 0px -150px;
+ width: 300px;
+ border: 2px solid #CCCCCC;
+ background-color: #FFFFFF;
+ overflow: hidden;
+}
+
+#login input {
+ width: 100%;
+}
+
+input[name="name"], input[name="password"] {
+ font-weight: bold;
+}
+
+#login.red {
+ border-color: #FF0000;
+}
diff --git a/bootstrap/comments/admin/views/login/login.html b/bootstrap/comments/admin/views/login/login.html
new file mode 100644
index 0000000..47dbd29
--- /dev/null
+++ b/bootstrap/comments/admin/views/login/login.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+
+<html lang="en" dir="ltr">
+ <head>
+ <title>HashOver - {hashover:title}</title>
+
+ <link type="text/css" href="../../../themes/default/general.css" rel="stylesheet">
+ <link type="text/css" href="../../../themes/default/special.css" rel="stylesheet">
+ <link type="text/css" href="login.css" rel="stylesheet">
+
+ <script type="text/javascript" src="login.js"></script>
+
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8">
+ <meta http-equiv="Content-Language" content="EN">
+
+ <link type="image/x-icon" href="../../../images/favicon.png" rel="shortcut icon">
+ <link type="image/x-icon" href="../../../images/favicon.png" rel="icon">
+ <link href="https://www.gnu.org/licenses/agpl" rel="copyright">
+ </head>
+
+ <body>
+ <div id="login" class="red animate-all">
+ <h1 class="underlined">{hashover:title}</h1>
+
+ <form method="post">
+ <div><input type="text" name="name" placeholder="{hashover:name}"></div>
+ <div><input type="password" name="password" placeholder="{hashover:password}"></div>
+ <div><input type="text" name="email" placeholder="{hashover:email}"></div>
+ <div><input type="text" name="website" placeholder="{hashover:website}"></div>
+
+ <p>
+ <input type="submit" value="{hashover:login}">
+ </p>
+ </form>
+ </div>
+ </body>
+</html>
diff --git a/bootstrap/comments/admin/views/login/login.js b/bootstrap/comments/admin/views/login/login.js
new file mode 100644
index 0000000..a94955c
--- /dev/null
+++ b/bootstrap/comments/admin/views/login/login.js
@@ -0,0 +1,10 @@
+// Wait for the page HTML to be parsed
+document.addEventListener ('DOMContentLoaded', function () {
+ // Get login dialog
+ var login = document.getElementById ('login');
+
+ // Remove red border class
+ setTimeout (function () {
+ login.className = login.className.replace (/red ?/, '');
+ }, 1000);
+}, false);
diff --git a/bootstrap/comments/admin/views/moderation/index.php b/bootstrap/comments/admin/views/moderation/index.php
new file mode 100644
index 0000000..2252389
--- /dev/null
+++ b/bootstrap/comments/admin/views/moderation/index.php
@@ -0,0 +1,88 @@
+<?php namespace HashOver;
+
+// Copyright (C) 2018 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/>.
+
+
+try {
+ // View setup
+ require (realpath ('../view-setup.php'));
+
+ // Create comment thread table
+ $table = new HTMLTag ('table', array (
+ 'id' => 'threads',
+ 'class' => 'striped-rows-odd',
+ 'cellspacing' => '0',
+ 'cellpadding' => '4'
+ ));
+
+ // Get comment threads
+ $threads = $hashover->thread->queryThreads ();
+
+ // Run through comment threads
+ foreach ($threads as $thread) {
+ // Create table row and cell
+ $tr = new HTMLTag ('tr');
+ $td = new HTMLTag ('td');
+
+ // Read and parse JSON metadata file
+ $data = $hashover->thread->data->readMeta ('page-info', $thread);
+
+ // Check if metadata was read successfully
+ if ($data === false or empty ($data['url']) or empty ($data['title'])) {
+ continue;
+ }
+
+ // Create thread hyperlink
+ $thread_link = new HTMLTag ('a', array (
+ 'href' => 'threads.php?' . implode ('&', array (
+ 'thread=' . urlencode ($thread),
+ 'title=' . urlencode ($data['title']),
+ 'url=' . urlencode ($data['url'])
+ )),
+
+ 'innerHTML' => $data['title']
+ ));
+
+ // Append thread hyperlink to cell
+ $td->appendChild ($thread_link);
+
+ // Append page URL to row
+ $td->appendChild (new HTMLTag ('p', new HTMLTag ('small', $data['url'])));
+
+ // Append cell to row
+ $tr->appendChild ($td);
+
+ // Append row to table
+ $table->appendChild ($tr);
+ }
+
+ // Template data
+ $template = array (
+ 'title' => $hashover->locale->text['moderation'],
+ 'logout' => $logout->asHTML ("\t\t\t"),
+ 'sub-title' => $hashover->locale->text['moderation-sub'],
+ 'threads' => $table->asHTML ("\t\t")
+ );
+
+ // Load and parse HTML template
+ echo $hashover->templater->parseTemplate ('moderation.html', $template);
+
+} catch (\Exception $error) {
+ $misc = new Misc ('php');
+ $message = $error->getMessage ();
+ $misc->displayError ($message);
+}
diff --git a/bootstrap/comments/admin/views/moderation/moderation.css b/bootstrap/comments/admin/views/moderation/moderation.css
new file mode 100644
index 0000000..ec6c389
--- /dev/null
+++ b/bootstrap/comments/admin/views/moderation/moderation.css
@@ -0,0 +1,3 @@
+#threads small {
+ color: #5AB3FA;
+}
diff --git a/bootstrap/comments/admin/views/moderation/moderation.html b/bootstrap/comments/admin/views/moderation/moderation.html
new file mode 100644
index 0000000..a9d9df7
--- /dev/null
+++ b/bootstrap/comments/admin/views/moderation/moderation.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+
+<html lang="en" dir="ltr">
+ <head>
+ <title>HashOver - {hashover:title}</title>
+
+ <link type="text/css" href="../../../themes/default/general.css" rel="stylesheet">
+ <link type="text/css" href="../../../themes/default/special.css" rel="stylesheet">
+ <link type="text/css" href="moderation.css" rel="stylesheet">
+
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8">
+ <meta http-equiv="Content-Language" content="EN">
+
+ <link type="image/x-icon" href="../../../images/favicon.png" rel="shortcut icon">
+ <link type="image/x-icon" href="../../../images/favicon.png" rel="icon">
+ <link href="https://www.gnu.org/licenses/agpl" rel="copyright">
+ </head>
+
+ <body>
+ <h1 class="underlined">
+ {hashover:title}
+
+ {hashover:logout}
+ </h1>
+
+ <p class="muted-text">{hashover:sub-title}</p>
+
+ {hashover:threads}
+ </body>
+</html>
diff --git a/bootstrap/comments/admin/views/moderation/threads.css b/bootstrap/comments/admin/views/moderation/threads.css
new file mode 100644
index 0000000..48258da
--- /dev/null
+++ b/bootstrap/comments/admin/views/moderation/threads.css
@@ -0,0 +1,27 @@
+.special a,
+.special a:link {
+ display: inline-block;
+ border-bottom: 0.1em solid transparent;
+ margin-bottom: -0.1em;
+}
+
+.special a:hover {
+ border-bottom-color: #266394;
+}
+
+#loading {
+ display: block ! important;
+ position: absolute;
+ top: 50%;
+ left: 0px;
+ width: 100%;
+ margin-top: -15px;
+}
+
+#loading img {
+ vertical-align: top;
+}
+
+.hashover > div {
+ display: none;
+}
diff --git a/bootstrap/comments/admin/views/moderation/threads.html b/bootstrap/comments/admin/views/moderation/threads.html
new file mode 100644
index 0000000..68c57a5
--- /dev/null
+++ b/bootstrap/comments/admin/views/moderation/threads.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+
+<html lang="en" dir="ltr">
+ <head>
+ <title>HashOver - {hashover:title}</title>
+
+ <link type="text/css" href="../../../themes/default/general.css" rel="stylesheet">
+ <link type="text/css" href="threads.css" rel="stylesheet">
+
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8">
+ <meta http-equiv="Content-Language" content="EN">
+
+ <link type="image/x-icon" href="../../../images/favicon.png" rel="shortcut icon">
+ <link type="image/x-icon" href="../../../images/favicon.png" rel="icon">
+ <link href="https://www.gnu.org/licenses/agpl" rel="copyright">
+ </head>
+
+ <body>
+ <h1 class="underlined special">
+ &#8592; <a href="../moderation/">{hashover:title}</a>
+
+ {hashover:logout}
+ </h1>
+
+ <div id="hashover">
+ <center id="loading">
+ <img src="../../../images/loading-ltr.gif" alt="Loading..." width="90" height="30">
+ </center>
+ </div>
+
+ <script type="text/javascript" src="../../../comments.php?nodefault"></script>
+ <script type="text/javascript" src="threads.js"></script>
+ </body>
+</html>
diff --git a/bootstrap/comments/admin/views/moderation/threads.js b/bootstrap/comments/admin/views/moderation/threads.js
new file mode 100644
index 0000000..851cad3
--- /dev/null
+++ b/bootstrap/comments/admin/views/moderation/threads.js
@@ -0,0 +1,21 @@
+var fullQueries = window.location.search.substr (1);
+var queries = fullQueries.split ('&');
+var options = {};
+
+// Parse URL queries as HashOver options
+for (var i = 0, il = queries.length; i < il; i++) {
+ var queryParts = queries[i].split ('=');
+ var queryName = queryParts[0];
+ var queryValue = queryParts[1];
+
+ if (queryName && queryValue) {
+ queryValue = queryValue.replace (/\+/g, '%20');
+ queryValue = decodeURIComponent (queryValue);
+
+ // Set option
+ options[queryName] = queryValue;
+ }
+}
+
+// Instantiate HashOver
+var hashover = new HashOver (options);
diff --git a/bootstrap/comments/admin/views/moderation/threads.php b/bootstrap/comments/admin/views/moderation/threads.php
new file mode 100644
index 0000000..b22c692
--- /dev/null
+++ b/bootstrap/comments/admin/views/moderation/threads.php
@@ -0,0 +1,33 @@
+<?php namespace HashOver;
+
+// Copyright (C) 2018 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/>.
+
+
+try {
+ // View setup
+ require (realpath ('../view-setup.php'));
+
+ // Load and parse HTML template
+ echo $hashover->templater->parseTemplate ('threads.html', array (
+ 'title' => $hashover->locale->text['back'],
+ 'logout' => $logout->asHTML ("\t\t\t")
+ ));
+} catch (\Exception $error) {
+ $misc = new Misc ('php');
+ $message = $error->getMessage ();
+ $misc->displayError ($message);
+}
diff --git a/bootstrap/comments/admin/views/settings/index.php b/bootstrap/comments/admin/views/settings/index.php
new file mode 100644
index 0000000..37a72b3
--- /dev/null
+++ b/bootstrap/comments/admin/views/settings/index.php
@@ -0,0 +1,515 @@
+<?php namespace HashOver;
+
+// Copyright (C) 2018 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)
+{
+ // 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;
+ }
+
+ // Return array of settings allowed to be changed
+ return array (
+ 'language' => array (
+ 'type' => 'select',
+ 'value' => $setup->language,
+
+ 'options' => array (
+ 'auto' => 'auto',
+ 'en' => 'English',
+ 'da' => 'Danish',
+ 'el' => 'Greek',
+ 'de' => 'German',
+ 'es' => 'Spanish',
+ 'fa' => 'Persian',
+ 'fr' => 'French',
+ 'jp' => 'Japanese',
+ 'ko' => 'Korean',
+ 'lt' => 'Lithuanian',
+ 'nl' => 'Dutch',
+ 'pl' => 'Polish',
+ 'pt-br' => 'Brazilian Portuguese',
+ 'ro' => 'Romanian',
+ 'tr' => 'Turkish',
+ 'zh-cn' => 'Simplified Chinese'
+ )
+ ),
+ 'theme' => array (
+ 'type' => 'select',
+ 'value' => $setup->theme,
+ 'options' => $themes
+ ),
+ 'uses-moderation' => array (
+ 'type' => 'checkbox',
+ 'value' => $setup->usesModeration
+ ),
+ 'pends-user-edits' => array (
+ 'type' => 'checkbox',
+ 'value' => $setup->pendsUserEdits
+ ),
+ 'data-format' => array (
+ 'type' => 'select',
+ 'value' => $setup->dataFormat,
+
+ 'options' => array (
+ 'xml' => 'XML',
+ 'json' => 'JSON',
+ 'sql' => 'SQL'
+ )
+ ),
+ 'default-name' => array (
+ 'type' => 'text',
+ 'value' => $setup->defaultName
+ ),
+ 'allows-images' => array (
+ 'type' => 'checkbox',
+ 'value' => $setup->allowsImages
+ ),
+ 'allows-login' => array (
+ 'type' => 'checkbox',
+ 'value' => $setup->allowsLogin
+ ),
+ 'allows-likes' => array (
+ 'type' => 'checkbox',
+ 'value' => $setup->allowsLikes
+ ),
+ 'allows-dislikes' => array (
+ 'type' => 'checkbox',
+ 'value' => $setup->allowsDislikes
+ ),
+ 'uses-ajax' => array (
+ 'type' => 'checkbox',
+ 'value' => $setup->usesAjax
+ ),
+ 'collapses-interface' => array (
+ 'type' => 'checkbox',
+ 'value' => $setup->collapsesInterface
+ ),
+ 'collapses-comments' => array (
+ 'type' => 'checkbox',
+ 'value' => $setup->collapsesComments
+ ),
+ 'collapse-limit' => array (
+ 'type' => 'number',
+ 'value' => $setup->collapseLimit
+ ),
+ 'reply-mode' => array (
+ 'type' => 'select',
+ 'value' => $setup->replyMode,
+
+ 'options' => array (
+ 'thread' => 'Threaded',
+ 'stream' => 'Stream'
+ )
+ ),
+ 'stream-depth' => array (
+ 'type' => 'number',
+ 'value' => $setup->streamDepth
+ ),
+ 'popularity-threshold' => array (
+ 'type' => 'number',
+ 'value' => $setup->popularityThreshold
+ ),
+ 'popularity-limit' => array (
+ 'type' => 'number',
+ 'value' => $setup->popularityLimit
+ ),
+ 'uses-markdown' => array (
+ 'type' => 'checkbox',
+ 'value' => $setup->usesMarkdown
+ ),
+ '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
+ ),
+ 'time-format' => array (
+ 'documentation' => 'http://php.net/manual/en/function.date.php',
+ 'type' => 'text',
+ 'value' => $setup->timeFormat
+ ),
+ 'date-format' => array (
+ 'documentation' => 'http://php.net/manual/en/function.date.php',
+ 'type' => 'text',
+ 'value' => $setup->dateFormat
+ ),
+ 'displays-title' => array (
+ 'type' => 'checkbox',
+ 'value' => $setup->displaysTitle
+ ),
+ 'form-position' => array (
+ 'type' => 'select',
+ 'value' => $setup->formPosition,
+
+ 'options' => array (
+ 'top' => 'Top',
+ 'bottom' => 'Bottom'
+ )
+ ),
+ 'uses-auto-login' => array (
+ 'type' => 'checkbox',
+ 'value' => $setup->usesAutoLogin
+ ),
+ 'shows-reply-count' => array (
+ 'type' => 'checkbox',
+ 'value' => $setup->showsReplyCount
+ ),
+ 'count-includes-deleted' => array (
+ 'type' => 'checkbox',
+ 'value' => $setup->countIncludesDeleted
+ ),
+ 'icon-mode' => array (
+ 'type' => 'select',
+ 'value' => $setup->iconMode,
+
+ 'options' => array (
+ 'image' => 'Image',
+ 'count' => 'Count',
+ 'none' => 'None'
+ )
+ ),
+ 'icon-size' => array (
+ 'type' => 'number',
+ 'value' => $setup->iconSize
+ ),
+ 'image-format' => array (
+ 'type' => 'select',
+ 'value' => $setup->imageFormat,
+
+ 'options' => array (
+ 'png' => 'PNG',
+ 'svg' => 'SVG'
+ )
+ ),
+ 'uses-labels' => array (
+ 'type' => 'checkbox',
+ 'value' => $setup->usesLabels
+ ),
+ 'uses-cancel-buttons' => array (
+ 'type' => 'checkbox',
+ 'value' => $setup->usesCancelButtons
+ ),
+ 'appends-css' => array (
+ 'type' => 'checkbox',
+ 'value' => $setup->appendsCss
+ ),
+ 'appends-rss' => array (
+ 'type' => 'checkbox',
+ 'value' => $setup->appendsRss
+ ),
+ 'login-method' => array (
+ 'type' => 'select',
+ 'value' => $setup->loginMethod,
+ 'options' => array ('defaultLogin' => 'Default Login')
+ ),
+ 'sets-cookies' => array (
+ 'type' => 'checkbox',
+ 'value' => $setup->setsCookies
+ ),
+ 'secure-cookies' => array (
+ 'type' => 'checkbox',
+ 'value' => $setup->secureCookies
+ ),
+ 'stores-ip-address' => array (
+ 'type' => 'checkbox',
+ 'value' => $setup->storesIpAddress
+ ),
+ 'subscribes-user' => array (
+ 'type' => 'checkbox',
+ 'value' => $setup->subscribesUser
+ ),
+ 'allows-user-replies' => array (
+ 'type' => 'checkbox',
+ 'value' => $setup->allowsUserReplies
+ ),
+ 'noreply-email' => array (
+ 'type' => 'text',
+ 'value' => $setup->noreplyEmail
+ ),
+ 'spam-batabase' => 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 (
+ 'json' => 'JSON',
+ 'php' => 'PHP',
+ 'both' => 'Both'
+ )
+ ),
+ 'gravatar-force' => array (
+ 'type' => 'checkbox',
+ 'value' => $setup->gravatarForce
+ ),
+ 'gravatar-default' => array (
+ 'type' => 'select',
+ 'value' => $setup->gravatarDefault,
+
+ 'options' => array (
+ 'custom' => 'Custom',
+ 'identicon' => 'Identicon',
+ 'monsterid' => 'Monsterid',
+ 'wavatar' => 'Wavatar',
+ 'retro' => 'Retro'
+ )
+ ),
+ '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)'
+ )
+ ),
+ 'allow-local-metadata' => array (
+ 'type' => 'checkbox',
+ 'value' => $setup->allowLocalMetadata
+ )
+ );
+}
+
+try {
+ // View setup
+ require (realpath ('../view-setup.php'));
+
+ // Get array of UI elements to create
+ $ui = ui_array ($hashover->setup);
+
+ // 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 $name => $setting) {
+ // Use specified type or optional cast
+ $type = !empty ($setting['cast']) ? 'cast' : 'type';
+
+ 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: {
+ $settings[$name] = (string)($_POST[$name]);
+ break;
+ }
+ }
+ }
+
+ // Save the settings to the JSON settings file
+ if ($hashover->setup->verifyAdmin ($hashover->login->password)
+ and $data_files->saveJSON ($settings_file, $settings))
+ {
+ // Redirect with success indicator
+ header ('Location: index.php?status=success');
+ } else {
+ // Redirect with failure indicator
+ header ('Location: index.php?status=failure');
+ }
+
+ // Exit after redirect
+ exit;
+ }
+
+ // Otherwise, create settings table
+ $table = new HTMLTag ('table', array (
+ 'id' => 'settings',
+ 'class' => 'p-spaced',
+ 'cellspacing' => '0',
+ 'cellpadding' => '4'
+ ));
+
+ // Create settings table
+ foreach ($ui as $name => $setting) {
+ // Create table row
+ $tr = new HTMLTag ('tr');
+
+ // Create setting description cell
+ $description = new HTMLTag ('td');
+
+ // Create description label
+ $label = new HTMLTag ('label', array (
+ 'for' => $name,
+ 'innerHTML' => $hashover->locale->text['setting-' . $name]
+ ), false);
+
+ // Check for documentation URL
+ if (!empty ($setting['documentation'])) {
+ // Create documentation link
+ $docs = new HTMLTag ('a', array (
+ 'href' => $setting['documentation'],
+ 'target' => '_blank',
+ 'innerHTML' => mb_strtolower ($hashover->locale->text['documentation'])
+ ), false);
+
+ // Append documentation in parentheses
+ $label->appendInnerHTML ('(' . $docs->asHTML () . ')');
+ }
+
+ // Append label to description cell
+ $description->appendChild ($label);
+
+ // Append description cell to settings table row
+ $tr->appendChild ($description);
+
+ // Create setting value cell
+ $field = new HTMLTag ('td');
+
+ switch ($setting['type']) {
+ case 'checkbox': {
+ // Create checkbox for enabling/disabling the setting
+ $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': {
+ $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': {
+ // 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
+ ));
+
+ foreach ($setting['options'] as $value => $text) {
+ // Create setting option
+ $option = new HTMLTag ('option', array (
+ 'value' => $value,
+ 'innerHTML' => $text
+ ), 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);
+
+ break;
+ }
+ }
+
+ // Append the setting value element to the setting value cell
+ $field->appendChild ($element);
+
+ // Append the setting value cell to the settings table row
+ $tr->appendChild ($field);
+
+ // Add row to settings table
+ $table->appendChild ($tr);
+ }
+
+ // Template data
+ $template = array (
+ 'title' => $hashover->locale->text['settings'],
+ 'logout' => $logout->asHTML ("\t\t\t"),
+ 'sub-title' => $hashover->locale->text['settings-sub'],
+ 'message' => $form_message,
+ 'settings' => $table->asHTML ("\t\t\t"),
+ 'save-button' => $hashover->locale->text['save']
+ );
+
+ // Load and parse HTML template
+ echo $hashover->templater->parseTemplate ('settings.html', $template);
+
+} catch (\Exception $error) {
+ $misc = new Misc ('php');
+ $message = $error->getMessage ();
+ $misc->displayError ($message);
+}
diff --git a/bootstrap/comments/admin/views/settings/settings.css b/bootstrap/comments/admin/views/settings/settings.css
new file mode 100644
index 0000000..1e1f1da
--- /dev/null
+++ b/bootstrap/comments/admin/views/settings/settings.css
@@ -0,0 +1,15 @@
+#settings {
+ width: 100%;
+}
+
+#settings tr:nth-child(odd) {
+ background-color: #F5F5F5;
+}
+
+input, textarea, select, .select-wrapper {
+ margin: 0px;
+}
+
+table tr {
+ height: 35px;
+}
diff --git a/bootstrap/comments/admin/views/settings/settings.html b/bootstrap/comments/admin/views/settings/settings.html
new file mode 100644
index 0000000..bf4330a
--- /dev/null
+++ b/bootstrap/comments/admin/views/settings/settings.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+
+<html lang="en" dir="ltr">
+ <head>
+ <title>HashOver - {hashover:title}</title>
+
+ <link type="text/css" href="../../../themes/default/general.css" rel="stylesheet">
+ <link type="text/css" href="../../../themes/default/special.css" rel="stylesheet">
+ <link type="text/css" href="settings.css" rel="stylesheet">
+
+ <script type="text/javascript" src="../shared/update-elements.js"></script>
+
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8">
+ <meta http-equiv="Content-Language" content="EN">
+
+ <link type="image/x-icon" href="../../../images/favicon.png" rel="shortcut icon">
+ <link type="image/x-icon" href="../../../images/favicon.png" rel="icon">
+ <link href="https://www.gnu.org/licenses/agpl" rel="copyright">
+ </head>
+
+ <body>
+ <h1 class="underlined">
+ <span id="title">{hashover:title}</span>
+
+ {hashover:logout}
+ </h1>
+
+ <div class="p-spaced muted-text">
+ {hashover:sub-title}
+ {hashover:message}
+ </div>
+
+ <form method="post">
+ {hashover:settings}
+
+ <p>
+ <input id="save-button" type="submit" name="save" value="{hashover:save-button}">
+ </p>
+ </form>
+ </body>
+</html>
diff --git a/bootstrap/comments/admin/views/shared/update-elements.js b/bootstrap/comments/admin/views/shared/update-elements.js
new file mode 100644
index 0000000..a6d2d74
--- /dev/null
+++ b/bootstrap/comments/admin/views/shared/update-elements.js
@@ -0,0 +1,12 @@
+// Wait for the page HTML to be parsed
+document.addEventListener ('DOMContentLoaded', function () {
+ // Get message element
+ var message = document.getElementById ('message');
+
+ // Hide message after 5 seconds
+ if (message !== null) {
+ setTimeout (function () {
+ message.className = message.className.replace ('success', 'hide');
+ }, 1000 * 5);
+ }
+}, false);
diff --git a/bootstrap/comments/admin/views/updates/index.php b/bootstrap/comments/admin/views/updates/index.php
new file mode 100644
index 0000000..660abf1
--- /dev/null
+++ b/bootstrap/comments/admin/views/updates/index.php
@@ -0,0 +1,38 @@
+<?php namespace HashOver;
+
+// Copyright (C) 2018 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/>.
+
+
+try {
+ // View setup
+ require (realpath ('../view-setup.php'));
+
+ // Template data
+ $template = array (
+ 'title' => $hashover->locale->text['check-for-updates'],
+ 'logout' => $logout->asHTML ("\t\t\t"),
+ 'sub-title' => $hashover->locale->text['coming-soon']
+ );
+
+ // Load and parse HTML template
+ echo $hashover->templater->parseTemplate ('updates.html', $template);
+
+} catch (\Exception $error) {
+ $misc = new Misc ('php');
+ $message = $error->getMessage ();
+ $misc->displayError ($message);
+}
diff --git a/bootstrap/comments/admin/views/updates/updates.html b/bootstrap/comments/admin/views/updates/updates.html
new file mode 100644
index 0000000..be82e62
--- /dev/null
+++ b/bootstrap/comments/admin/views/updates/updates.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+
+<html lang="en" dir="ltr">
+ <head>
+ <title>HashOver - {hashover:title}</title>
+
+ <link type="text/css" href="../../../themes/default/general.css" rel="stylesheet">
+ <link type="text/css" href="../../../themes/default/special.css" rel="stylesheet">
+
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8">
+ <meta http-equiv="Content-Language" content="EN">
+
+ <link type="image/x-icon" href="../../../images/favicon.png" rel="shortcut icon">
+ <link type="image/x-icon" href="../../../images/favicon.png" rel="icon">
+ <link href="https://www.gnu.org/licenses/agpl" rel="copyright">
+ </head>
+
+ <body>
+ <h1 class="underlined">
+ {hashover:title}
+
+ {hashover:logout}
+ </h1>
+
+ <p class="muted-text">{hashover:sub-title}</p>
+ </body>
+</html>
diff --git a/bootstrap/comments/admin/views/url-queries/index.php b/bootstrap/comments/admin/views/url-queries/index.php
new file mode 100644
index 0000000..461f62a
--- /dev/null
+++ b/bootstrap/comments/admin/views/url-queries/index.php
@@ -0,0 +1,134 @@
+<?php namespace HashOver;
+
+// Copyright (C) 2018 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/>.
+
+
+try {
+ // View setup
+ require (realpath ('../view-setup.php'));
+
+ // Default URL Query Pair array
+ $ignored_queries = array ();
+
+ // Ignored URL queries JSON file location
+ $ignored_queries_file = $hashover->setup->getAbsolutePath ('config/ignored-queries.json');
+
+ // Check if the form has been submitted
+ if (!empty ($_POST['names']) and is_array ($_POST['names'])
+ and !empty ($_POST['values']) and is_array ($_POST['values']))
+ {
+ // If so, run through submitted queries
+ for ($i = 0, $il = count ($_POST['names']); $i < $il; $i++) {
+ // Add each non-empty query to the pair array
+ if (!empty ($_POST['names'][$i])) {
+ // Start the query pair with the query name
+ $query_pair = $_POST['names'][$i];
+
+ // Check if the query has a value
+ if (!empty ($_POST['values'][$i])) {
+ // If so, add it to the pair
+ $query_pair .= '=' . $_POST['values'][$i];
+ }
+
+ // Add the query pair to the URL Query Pair array
+ $ignored_queries[] = $query_pair;
+ }
+ }
+
+ // Save the JSON data to the URL Query Pairs file
+ if ($hashover->setup->verifyAdmin ($hashover->login->password)
+ and $data_files->saveJSON ($ignored_queries_file, $ignored_queries))
+ {
+ // Redirect with success indicator
+ header ('Location: index.php?status=success');
+ } else {
+ // Redirect with failure indicator
+ header ('Location: index.php?status=failure');
+ }
+
+ // Exit after redirect
+ exit;
+ }
+
+ // Otherwise, load and parse URL Query Pairs file
+ $json = $data_files->readJSON ($ignored_queries_file);
+
+ // Check for JSON parse error
+ if (is_array ($json)) {
+ $ignored_queries = $json;
+ }
+
+ // URL Query Pair inputs
+ $inputs = new HTMLTag ('span');
+
+ // Create URL Query Pair inputs
+ for ($i = 0, $il = max (3, count ($ignored_queries)); $i < $il; $i++) {
+ // Use URL query pairs from file or blank
+ $query = !empty ($ignored_queries[$i]) ? $ignored_queries[$i] : '';
+
+ // Split query pair into name and value
+ $query_parts = explode ('=', $query);
+ $query_name = $query_parts[0];
+ $query_value = !empty ($query_parts[1]) ? $query_parts[1] : '';
+
+ // Create input tag
+ $input = new HTMLTag ('div', array (
+ 'children' => array (
+ new HTMLTag ('input', array (
+ 'class' => 'name',
+ 'type' => 'text',
+ 'name' => 'names[]',
+ 'value' => $query_name,
+ 'size' => '15',
+ 'placeholder' => $hashover->locale->text['name'],
+ 'title' => $hashover->locale->text['url-queries-name-tip']
+ ), false, true),
+
+ new HTMLTag ('input', array (
+ 'class' => 'value',
+ 'type' => 'text',
+ 'name' => 'values[]',
+ 'value' => $query_value,
+ 'size' => '25',
+ 'placeholder' => $hashover->locale->text['value'],
+ 'title' => $hashover->locale->text['url-queries-value-tip']
+ ), false, true)
+ )
+ ));
+
+ // Add input to inputs container
+ $inputs->appendChild ($input);
+ }
+
+ // Template data
+ $template = array (
+ 'title' => $hashover->locale->text['url-queries-title'],
+ 'logout' => $logout->asHTML ("\t\t\t"),
+ 'sub-title' => $hashover->locale->text['url-queries-sub'],
+ 'message' => $form_message,
+ 'inputs' => $inputs->getInnerHTML ("\t\t\t\t"),
+ 'save-button' => $hashover->locale->text['save']
+ );
+
+ // Load and parse HTML template
+ echo $hashover->templater->parseTemplate ('url-queries.html', $template);
+
+} catch (\Exception $error) {
+ $misc = new Misc ('php');
+ $message = $error->getMessage ();
+ $misc->displayError ($message);
+}
diff --git a/bootstrap/comments/admin/views/url-queries/url-queries.html b/bootstrap/comments/admin/views/url-queries/url-queries.html
new file mode 100644
index 0000000..fe350ce
--- /dev/null
+++ b/bootstrap/comments/admin/views/url-queries/url-queries.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+
+<html lang="en" dir="ltr">
+ <head>
+ <title>HashOver - {hashover:title}</title>
+
+ <link type="text/css" href="../../../themes/default/general.css" rel="stylesheet">
+ <link type="text/css" href="../../../themes/default/special.css" rel="stylesheet">
+
+ <script type="text/javascript" src="../shared/update-elements.js"></script>
+ <script type="text/javascript" src="url-queries.js"></script>
+
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8">
+ <meta http-equiv="Content-Language" content="EN">
+
+ <link type="image/x-icon" href="../../../images/favicon.png" rel="shortcut icon">
+ <link type="image/x-icon" href="../../../images/favicon.png" rel="icon">
+ <link href="https://www.gnu.org/licenses/agpl" rel="copyright">
+ </head>
+
+ <body>
+ <h1 class="underlined">
+ <span id="title">{hashover:title}</span>
+
+ {hashover:logout}
+ </h1>
+
+ <div class="p-spaced muted-text">
+ {hashover:sub-title}
+ {hashover:message}
+ </div>
+
+ <form method="post">
+ <div id="queries-list" class="p-spaced">
+ {hashover:inputs}
+ </div>
+
+ <p>
+ <input id="save-button" type="submit" value="{hashover:save-button}">
+ <input id="new-button" type="button" value="+">
+ </p>
+ </form>
+ </body>
+</html>
diff --git a/bootstrap/comments/admin/views/url-queries/url-queries.js b/bootstrap/comments/admin/views/url-queries/url-queries.js
new file mode 100644
index 0000000..6267691
--- /dev/null
+++ b/bootstrap/comments/admin/views/url-queries/url-queries.js
@@ -0,0 +1,36 @@
+// Wait for the page HTML to be parsed
+document.addEventListener ('DOMContentLoaded', function () {
+ // Get the "New Query Pair" and "Save" buttons
+ var newButton = document.getElementById ('new-button');
+ var queriesList = document.getElementById ('queries-list');
+ var saveButton = document.getElementById ('save-button');
+
+ newButton.onclick = function ()
+ {
+ // Create inputs and indentation
+ var div = document.createElement ('div');
+ var names = document.getElementsByClassName ('name');
+ var values = document.getElementsByClassName ('value');
+ var indentation = document.createTextNode ('\n\t\t\t\t\t');
+
+ // Clone the first name and value fields
+ var nameInput = names[0].cloneNode (true);
+ var valueInput = values[0].cloneNode (true);
+
+ // Remove their values
+ nameInput.value = '';
+ valueInput.value = '';
+
+ // Append indentation and input to URL Query Pair list
+ div.appendChild (nameInput);
+ div.appendChild (indentation);
+ div.appendChild (valueInput);
+ queriesList.appendChild (div);
+ };
+
+ // Disable the "Save" button when clicked
+ saveButton.onclick = function ()
+ {
+ this.disabled = true;
+ };
+}, false);
diff --git a/bootstrap/comments/admin/views/view-setup.php b/bootstrap/comments/admin/views/view-setup.php
new file mode 100644
index 0000000..adcf93d
--- /dev/null
+++ b/bootstrap/comments/admin/views/view-setup.php
@@ -0,0 +1,107 @@
+<?php namespace HashOver;
+
+// Copyright (C) 2018 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/>.
+
+
+// Do some standard HashOver setup work
+require (realpath ('../../../backend/standard-setup.php'));
+
+// Autoload class files
+spl_autoload_register (function ($uri) {
+ $uri = str_replace ('\\', '/', strtolower ($uri));
+ $class_name = basename ($uri);
+
+ if (!@include (realpath ('../../../backend/classes/' . $class_name . '.php'))) {
+ echo '"' . $class_name . '.php" file could not be included!';
+ exit;
+ }
+});
+
+// Instantiate HashOver class
+$hashover = new \HashOver ();
+$hashover->initiate ();
+$hashover->finalize ();
+
+// Instantiate FileWriter class
+$data_files = new DataFiles ($hashover->setup);
+
+// Exit if the user isn't logged in as admin
+if ($hashover->login->userIsAdmin !== true) {
+ $uri = $_SERVER['REQUEST_URI'];
+ $uri_parts = explode ('?', $uri);
+
+ if (basename ($uri_parts[0]) !== 'login') {
+ header ('Location: ../login/?redirect=' . urlencode ($uri));
+ exit;
+ }
+}
+
+// Create logout hyperlink
+$logout = new HTMLTag ('span', array (
+ 'class' => 'right',
+
+ 'children' => array (
+ new HTMLTag ('a', array (
+ 'href' => '../login/?logout=true',
+ 'target' => '_parent',
+ 'innerHTML' => $hashover->locale->text['logout']
+ ))
+ )
+));
+
+// Check if the form has been submitted
+if (!empty ($_GET['status'])) {
+ // Check if the form submission was successful
+ if ($_GET['status'] === 'success') {
+ // If so, create message element for success message
+ $message = new HTMLTag ('div', array (
+ 'id' => 'message',
+ 'class' => 'success',
+
+ 'children' => array (
+ new HTMLTag ('p', array (
+ 'innerHTML' => $hashover->locale->text['successful-save']
+ ), false)
+ )
+ ));
+ } else {
+ // If so, create message element for error message
+ $message = new HTMLTag ('div', array (
+ 'id' => 'message',
+ 'class' => 'error',
+
+ 'children' => array (
+ // Main error message
+ new HTMLTag ('p', array (
+ 'innerHTML' => $hashover->locale->text['failed-to-save']
+ ), false),
+
+ // File permissions explanation
+ new HTMLTag ('p', array (
+ 'innerHTML' => $hashover->locale->permissionsInfo ('config')
+ ), false)
+ )
+ ));
+ }
+
+ // Set message as HTML
+ $form_message = $message->asHTML ("\t\t");
+
+} else {
+ // If not, set the message as an empty string
+ $form_message = '';
+}