diff --git a/admin/index.php b/admin/index.php
index f9e64a2..876f77a 100644
--- a/admin/index.php
+++ b/admin/index.php
@@ -2,8 +2,7 @@
session_start();
require("../config.php");
require("../classes/constellation.php");
-require("../header.php");
-require("../footer.php");
+require("../template.php");
if(isset($_COOKIE['user'])&&!isset($_SESSION['user']))
{
@@ -66,5 +65,5 @@ else
break;
}
- render_footer(true);
+ Template::render_footer(true);
}
\ No newline at end of file
diff --git a/admin/login-form.php b/admin/login-form.php
index 17af92d..d87afad 100644
--- a/admin/login-form.php
+++ b/admin/login-form.php
@@ -1,5 +1,5 @@
diff --git a/admin/settings.php b/admin/settings.php
index 3178883..500e931 100644
--- a/admin/settings.php
+++ b/admin/settings.php
@@ -9,7 +9,7 @@ if (isset($_GET['delete']))
Service::delete();
}
-render_header(_("Settings"), true);
+Template::render_header(_("Settings"), true);
?>
Settings
diff --git a/admin/user.php b/admin/user.php
index da2a7c0..9148f1a 100644
--- a/admin/user.php
+++ b/admin/user.php
@@ -32,7 +32,7 @@ if (isset($_GET['what']) && $_GET['what']=='toggle')
$displayed_user->toggle();
}
-render_header(_("User"), true);
+Template::render_header(_("User"), true);
?>
diff --git a/classes/constellation.php b/classes/constellation.php
index 6758690..eafec8c 100644
--- a/classes/constellation.php
+++ b/classes/constellation.php
@@ -10,6 +10,13 @@ require(__DIR__ . "/token.php");
class Constellation
{
+ /**
+ * Renders incidents matching specified constraints.
+ * @param Boolean $future - specifies whether to render old or upcoming incidents
+ * @param int $offset - specifies offset - used for pagination
+ * @param int $limit - limits the number of incidents rendered
+ * @param Boolean $admin - specifies whether to render admin controls
+ */
public function render_incidents($future=false, $offset=0, $limit = 5, $admin = 0){
global $mysqli;
if ($offset<0)
@@ -57,6 +64,11 @@ class Constellation
}
}
+ /**
+ * Renders service status - in admin page it returns array so it can be processed further.
+ * @param boolean $admin
+ * @return array of services
+ */
public function render_status($admin = 0){
global $mysqli;
diff --git a/classes/incident.php b/classes/incident.php
index e30f5f5..7350a52 100644
--- a/classes/incident.php
+++ b/classes/incident.php
@@ -12,8 +12,13 @@ class Incident
private $title;
private $username;
+ /**
+ * Constructs service from its data.
+ * @param array $data incident data
+ */
function __construct($data)
{
+ //TODO: Maybe get data from id?
$this->id = $data['status_id'];
$this->date = new DateTime("@".$data['time']);
$this->date = $this->date->format('Y-m-d H:i:sP');
@@ -27,7 +32,12 @@ class Incident
$this->username = $data['username'];
}
+ /**
+ * Deletes incident by ID.
+ * @param int ID
+ */
public static function delete($id){
+ //TODO: This should check whether it's admin or their own post...
global $mysqli, $message;
$stmt = $mysqli->prepare("DELETE FROM services_status WHERE status_id = ?");
@@ -42,6 +52,12 @@ class Incident
header("Location: /admin");
}
+ /**
+ * Processes submitted form and adds incident unless problem is encountered,
+ * calling this is possible only for admin or higher rank. Also checks requirements
+ * for char limits.
+ * @return void
+ */
public static function add()
{
global $mysqli, $message;
@@ -120,6 +136,12 @@ class Incident
}
}
+
+ /**
+ * Renders incident
+ * @param Boolean $admin - decides whether admin controls should be rendered
+ * @return void
+ */
public function render($admin=0){
global $icons;
global $classes, $user;
diff --git a/classes/locale-negotiator.php b/classes/locale-negotiator.php
new file mode 100644
index 0000000..613d16b
--- /dev/null
+++ b/classes/locale-negotiator.php
@@ -0,0 +1,105 @@
+default_language = $default_language;
+ //Works only if the server supports the locale
+ //This basically means $accepted_langs[] = "";
+ foreach ($accepted_langs as $key => $value) {
+ $this->accepted_langs[basename($value)] = self::mb_ucfirst(locale_get_display_language($lang, $lang));
+ }
+ }
+
+ /**
+ * Returns list of accepted langs so it can be reused for rendering language list for switching...
+ */
+ public function get_accepted_langs(){
+ return $this->accepted_langs;
+ }
+
+ /**
+ * This methid does ucfirst() on multibyte encodings like UTF-8 - good for edge cases when locale starts with Č or similar.
+ * @param String $string string
+ * @return String string with first char uppercase
+ */
+ private static function mb_ucfirst($string)
+ {
+ return mb_strtoupper(mb_substr($string, 0, 1)).mb_strtolower(mb_substr($string, 1));
+ }
+
+ /**
+ * This method does the actual negotiation. It has override parameter in case user wants to switch
+ * languages.
+ * @param String $override adds language to list of preffered languages with highest priority
+ * @return String language code that matched best with browser preferences
+ */
+ public function negotiate($override = null){
+ $langs = [];
+
+ if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
+ preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $lang_parse);
+
+ if (count($lang_parse[1])) {
+ $langs = array_combine($lang_parse[1], $lang_parse[4]);
+
+ foreach ($langs as $lang => $val) {
+ //If browser didn't send quality of language, it is 1 by default
+ if ($val === '') $langs[$lang] = 1;
+ }
+
+ if (isset($override))
+ {
+ //More important than the best lang of browser
+ $langs[$override] = 2;
+ }
+
+ arsort($langs, SORT_NUMERIC);
+ }
+ }
+
+ //So we have lang code as value
+ $langs = array_flip($langs);
+ //False unless we set it, so we know to set default locale
+ $best_match = false;
+ //So we have also lang code as value
+ $accepted_langs = array_flip($this->accepted_langs);
+ foreach ($langs as $lang) {
+ if (strlen($lang)>2){
+ if (in_array($lang, $accepted_langs)){
+ $best_match = $lang;
+ break;
+ }
+ }else{
+ $possible = array_filter($accepted_langs, function($key) {
+ global $lang;
+ return strpos($key, $lang) === 0;
+ });
+
+ if (count($possible)){
+ $best_match = $possible[0];
+ break;
+ }
+ }
+ }
+
+ if ($best_match === false){
+ $best_match = $this->default_language;
+ }
+
+ return $best_match;
+ }
+}
+
diff --git a/classes/service.php b/classes/service.php
index 2cad89f..8f11e42 100644
--- a/classes/service.php
+++ b/classes/service.php
@@ -1,6 +1,6 @@
id = $id;
$this->name = $name;
$this->status = $status;
}
+ /**
+ * Returns status of this service
+ * @return int status
+ */
public function get_status()
{
return $this->status;
}
+ /**
+ * Returns id of this service
+ * @return int id
+ */
public function get_id()
{
return $this->id;
}
+ /**
+ * Returns name of this service
+ * @return String name
+ */
public function get_name()
{
return $this->name;
}
+ /**
+ * Processes submitted form and adds service unless problem is encountered,
+ * calling this is possible only for admin or higher rank. Also checks requirements
+ * for char limits.
+ * @return void
+ */
public static function add()
{
global $user, $message;
@@ -57,6 +82,10 @@ class Service
}
}
+ /**
+ * Deletes this service - first checks if user has permission to do that.
+ * @return void
+ */
public static function delete()
{
global $user;
@@ -95,6 +124,11 @@ class Service
}
}
+ /**
+ * Renders current status for services from passed array of services.
+ * @param Service[] $array array of services
+ * @return void
+ */
public static function current_status($array){
global $all, $some, $classes;
$statuses = array(0,0,0,0);
@@ -118,6 +152,10 @@ class Service
echo '
';
}
+ /**
+ * Renders this service.
+ * @return void
+ */
public function render(){
global $statuses;
global $classes;
diff --git a/classes/token.php b/classes/token.php
index d334223..2b0da73 100644
--- a/classes/token.php
+++ b/classes/token.php
@@ -4,6 +4,13 @@
*/
class Token
{
+ /**
+ * Generates a new token from user id and randomly generated salt.
+ * @param int $user ID
+ * @param String $data associated with token that are important
+ * @param timestamp $expire expiration time
+ * @return String token
+ */
public static function new($id, $data, $expire)
{
global $mysqli;
@@ -16,6 +23,13 @@ class Token
return $token;
}
+ /**
+ * Checks whether token exists in the database and has not expired.
+ * @param String $token
+ * @param int $id user ID
+ * @param String $data
+ * @return int count of results in database
+ */
public static function validate_token($token, $id, $data)
{
global $mysqli;
@@ -27,6 +41,11 @@ class Token
return $query->fetch_assoc()['count'];
}
+ /**
+ * Deletes token.
+ * @param String $token
+ * @return void
+ */
public static function delete($token)
{
global $mysqli;
diff --git a/classes/user.php b/classes/user.php
index 0193615..3888e53 100644
--- a/classes/user.php
+++ b/classes/user.php
@@ -1,6 +1,6 @@
rank = $result['permission'];
}
+ /**
+ * Returns username of this user
+ * @return String username
+ */
public function get_username()
{
return $this->username;
}
-
+
+ /**
+ * Returns whether this user is active
+ * @return Boolean user active status
+ */
public function is_active()
{
return $this->active;
}
-
+
+ /**
+ * Returns rank of this user
+ * @return int rank
+ */
public function get_rank()
{
return $this->rank;
}
-
+
+ /**
+ * Returns full name of this user
+ * @return String name in "Name Surname" format
+ */
public function get_name()
{
return $this->name . " " . $this->surname;
}
+ /**
+ * Toggles active status of this user. First checks if the user
+ * making the change has permission to do that.
+ * @return void
+ */
public function toggle()
{
global $mysqli, $message, $user;
@@ -78,6 +103,13 @@ class User
}
}
+ /**
+ * Processes submitted form and adds user unless problem is encountered,
+ * calling this is possible only for Superadmin (other ranks cannot add users)
+ * or when the installation script is being run. Also checks requirements
+ * for username and email being unique and char limits.
+ * @return void
+ */
public static function add()
{
global $user, $message, $mysqli;
@@ -146,6 +178,13 @@ class User
}
}
+ /**
+ * Processes submitted form and logs user in, unless the user is deactivated or wrong
+ * password or email has been submitted. The script doesn't let anyone know which
+ * field was wrong as it is not possible to verify email address from outside admin panel,
+ * so this actually helps with security :)
+ * @return void
+ */
public static function login()
{
global $message, $mysqli;
@@ -200,6 +239,12 @@ class User
}
}
+ /**
+ * Checks whether token is valid (this means is in database and associated
+ * with the user) and sets session data if it is, so user remains logged in.
+ * The script deletes the token either way.
+ * @return void
+ */
public static function restore_session()
{
global $mysqli, $message;
@@ -225,7 +270,10 @@ class User
Token::delete($token);
}
-
+ /**
+ * Renders settings for this user so it can be displayed in admin panel.
+ * @return void
+ */
public function render_user_settings()
{
global $permissions, $user;
@@ -307,7 +355,12 @@ class User
}
-
+ /**
+ * Changes user password and deletes all remember tokens so all other sessions
+ * won't stay logged in without knowing new pass. Uses token when reseting password.
+ * @param String $token
+ * @return void
+ */
public function change_password($token = false)
{
global $mysqli, $user, $message;
@@ -344,6 +397,10 @@ class User
$stmt->bind_param("si", $hash, $id);
$stmt->execute();
$stmt->close();
+ $stmt = $mysqli->prepare("DELETE FROM tokens WHERE user = ? AND data = 'remember'");
+ $stmt->bind_param("d", $id);
+ $stmt->execute();
+ $query = $stmt->get_result();
User::logout();
}
else{
@@ -366,6 +423,10 @@ class User
$stmt->bind_param("si", $hash,$id);
$stmt->execute();
$stmt->close();
+ $stmt = $mysqli->prepare("DELETE FROM tokens WHERE user = ? AND data = 'remember'");
+ $stmt->bind_param("d", $id);
+ $stmt->execute();
+ $query = $stmt->get_result();
}
else
{
@@ -377,6 +438,10 @@ class User
}
}
+ /**
+ * Sends email with link for password reset, link is token protected and valid only once.
+ * @return void
+ */
public static function password_link()
{
global $mysqli;
@@ -405,6 +470,10 @@ class User
mail($to, $subject, $msg, $headers);
}
+ /**
+ * Sends email with link for email change confirmation (security reasons), link is token protected and valid only once.
+ * @return void
+ */
public function email_link(){
global $mysqli;
$email = $_POST['email'];
@@ -426,9 +495,12 @@ class User
mail($to, $subject, $msg, $headers);
}
+ /**
+ * Changes email.
+ * @return void
+ */
public function change_email()
{
- //TODO: Get message from this somehow
global $mysqli, $message;
$time = time();
$token = $_GET['token'];
@@ -456,6 +528,10 @@ class User
}
+ /**
+ * Logs current user out.
+ * @return void
+ */
public static function logout(){
global $mysqli;
session_unset();
@@ -469,6 +545,10 @@ class User
header("Location: /admin");
}
+ /**
+ * Changes permissions of current user - only super admin can do this, so it checks permission first.
+ * @return void
+ */
public function change_permission(){
global $mysqli, $message, $user;
if ($user->get_rank()==0)
diff --git a/config.php.template b/config.php.template
index 7c5506e..5149d86 100644
--- a/config.php.template
+++ b/config.php.template
@@ -9,13 +9,27 @@ define("MAILER_ADDRESS", "##mailer_email##"); //Mailer address
define("INSTALL_OVERRIDE", false);
define("DEFAULT_LANGUAGE", "en_GB");
-require("locale.php");
+require("classes/locale-negotiator.php");
+
+if (!isset($_SESSION['locale'])||isset($_GET['lang']))
+{
+ $override = ((isset($_GET['lang']))?$_GET['lang']:null);
+ $negotiator = new LocaleNegotiator(DEFAULT_LANGUAGE);
+ $best_match = $negotiator->negotiate($override);
+ $_SESSION['locale'] = $best_match;
+ setlocale(LC_ALL, $_SESSION['locale'].".UTF-8");
+
+ bindtextdomain("server-status", __DIR__ . "/locale/");
+ bind_textdomain_codeset($_SESSION['locale'], "utf-8");
+ textdomain("server-status");
+}
+
//Database connection
$mysqli = new mysqli("##server##","##user##","##password##","##database##");
if ($mysqli->connect_errno) {
- printf("Connection failed: %s\n", $mysqli->connect_error);
+ printf(_("Connection failed: %s\n"), $mysqli->connect_error);
exit();
}
diff --git a/footer.php b/footer.php
deleted file mode 100644
index 501e94e..0000000
--- a/footer.php
+++ /dev/null
@@ -1,45 +0,0 @@
-
-