Objectify locale negotiator and template files, add documentation

This commit is contained in:
Vojtěch Sajdl
2018-01-07 20:39:10 +01:00
parent 9820686776
commit 61f563c9b0
20 changed files with 440 additions and 230 deletions
+12
View File
@@ -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;
+22
View File
@@ -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;
+105
View File
@@ -0,0 +1,105 @@
<?php
/**
* This class is used to negotiate language displayed to user.
* Reads browser preferences and chooses the best language from list
*/
class LocaleNegotiator
{
private $accepted_langs = [];
private $default_language;
/**
* This method scans for languages and creates a list of language and its name (localized ofc.)
* @param String $default_language language displayed to user in case no suitable lang is found
*/
function __construct($default_language)
{
$tmp = glob(__DIR__ . '/locale/*' , GLOB_ONLYDIR);
$this->default_language = $default_language;
//Works only if the server supports the locale
//This basically means $accepted_langs[<lang_code>] = "<lang name>";
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;
}
}
+39 -1
View File
@@ -1,6 +1,6 @@
<?php
/**
* Class for creating and rendering an incident
* Class for managing services
*/
class Service
{
@@ -8,28 +8,53 @@ class Service
private $name;
private $status;
/**
* Constructs service from its data.
* @param int $id service ID
* @param String $name service name
* @param int $status current service status
*/
function __construct($id, $name, $status=3)
{
//TODO: Maybe get data from ID?
$this->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 '</div>';
}
/**
* Renders this service.
* @return void
*/
public function render(){
global $statuses;
global $classes;
+19
View File
@@ -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;
+87 -7
View File
@@ -1,6 +1,6 @@
<?php
/**
* Class for creating and rendering an incident
* Class that encapsulates everything that can be done with a user
*/
class User
{
@@ -12,6 +12,10 @@ class User
private $rank;
private $active;
/**
* Gets user data from database and creates the class
* @param int $id user ID
*/
function __construct($id)
{
global $mysqli;
@@ -36,26 +40,47 @@ class User
$this->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)