Object-oriented authentication, and WIP translations

templating
Eirik Th S 2022-06-19 23:08:15 +02:00
parent b5fb90d290
commit c33f8004e6
16 changed files with 789 additions and 176 deletions

View File

@ -16,11 +16,14 @@ require_once 'vendor/autoload.php';
// Application and Models autoload
spl_autoload_register(function ($class_name) {
if(file_exists(__DIR__."/application/".$class_name.'.php')){
include __DIR__."/application/".$class_name.'.php';
if (file_exists(__DIR__ . "/application/" . $class_name . '.php')) {
include __DIR__ . "/application/" . $class_name . '.php';
// echo "Load class " . __DIR__ . "/application/" . $class_name . '.php' . "<br>\n";
}
elseif(file_exists(__DIR__."/models/".$class_name.'.php')) {
include __DIR__."/models/".$class_name.'.php';
elseif(file_exists(__DIR__ . "/models/" . $class_name . ".php")) {
include_once __DIR__."/models/Model.php";
include_once __DIR__ . "/models/" . $class_name . ".php";
// echo "Load class ".__DIR__."/models/".$class_name.'.php'."<br>\n";
}
});
@ -31,6 +34,12 @@ $twigSettings = PRODUCTION?['cache' => __dir__.'/tmp/']:[];
$loader = new Twig\Loader\FilesystemLoader(__dir__ . '/templates/'.Config::get('system', 'template', 'default')."/");
$twig = new Twig\Environment($loader, $twigSettings);
$filter = new Twig\TwigFilter('t', function ($string) {
return _(strip_tags($string));
});
$twig->addFilter($filter);
class Config {
private static $instance = null;
@ -53,4 +62,16 @@ class Config {
}
return $default;
}
}
function model($name){
return;
if(file_exists(__DIR__ . "/models/" . $name . ".php")){
include_once __DIR__ . "/models/Model.php";
include_once __DIR__ . "/models/" . $name . ".php";
}
else {
echo "FAULT<br>\n";
echo __DIR__ . "/models/" . $name . ".php". " doesn't exist";
}
}

View File

@ -17,10 +17,21 @@ class Auth {
}
public static function loginWithCredentials($email, $pass, bool $stayLoggedIn = false){
$Auth = new Auth();
session_destroy();
self::$instance = null;
$Auth = Auth::getInstance();
$err = [];
if(strlen($email) < 3){
$err[] = __("Please enter a valid email.");
}
if(strlen($pass) < 3){
$err[] = __("Please enter a valid password. Hint: The passwords need to have at least 6 characters.");
}
if(!empty($err)){ return $err; }
// get user from database
$getUserSQL = "SELECT pwd, user_id, user_email FROM user WHERE user_email = ? LIMIT 1;";
$getUserRes = DB::query($getUserSQL, $email);
@ -41,14 +52,16 @@ class Auth {
$userKey = GenKey();
$shaUserKey = sha1($userKey);
$expire = $stayLoggedIn?30:0; // if "stay logged in", stay logged in for 30 days.
$ctime = time();
$updateUserSQL = "INSERT INTO user_login (user_id, ckey, ctime, expire, agent) VALUES (?, ?, ?, ?, ?);";
try {
DB::query($updateUserSQL, $dbUserId, $userKey, time(), $expire, $md5agent);
DB::query($updateUserSQL, $dbUserId, $userKey, $ctime, $expire, $md5agent);
$_SESSION['user_id'] = $dbUserId;
$_SESSION['user_name'] = $dbUserName;
$_SESSION['user_agent'] = $md5agent;
$_SESSION['user_key'] = $shaUserKey;
$_SESSION['user_ctime'] = $ctime;
if($stayLoggedIn){
setcookie("auth", $shaUserKey.".".$dbUserId, time() + (30 * 24*60*60), "/", $_SERVER['HTTP_HOST'], isset($_SERVER['HTTPS']), true );
@ -56,11 +69,11 @@ class Auth {
return true;
}
catch(DatabaseException $e){
$err[] = "Failed to login.\n".$e;
$err[] = __("Failed to login.")."\n".$e;
}
}
else {
$err[] = "Username and/or Password is wrong.";
$err[] = __("The provided combination of username and password is not recognized in the system. Verify your details or create an account.");
}
return $err;
@ -102,8 +115,76 @@ class Auth {
return false;
}
public static function logout(){}
public static function logout($everywhere = false){
if(Auth::checkLogin()){
$logoutSql = "DELETE FROM user_login WHERE user_id = ?";
$sqlparams[] = $_SESSION['user_id'];
if(!$everywhere){
// $logoutSql .= " AND agent = ?";
// $sqlparams[] = md5($_SERVER['HTTP_USER_AGENT']);
$logoutSql .= " AND ctime = ?";
$sqlparams[] = $_SESSION['user_ctime'];
}
DB::query($logoutSql, $sqlparams);
}
// Delete session-cookie
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params["path"], $params["domain"],
$params["secure"], $params["httponly"]
);
setcookie('auth', '', time() - 42000, "/", $_SERVER['HTTP_HOST'], isset($_SERVER['HTTPS']), true );
// Finally, destroy the session.
session_destroy();
}
public static function register($userEmail, $userPass, $metadata = []){
$err = [];
$userName = $metadata['newName'] ?? '';
if(strlen($userPass) < 6){
$err[] = __("Password is too short. Password needs to be at least 6 characters!");
}
else if(strlen($userPass) > 30){
$err[] = __("Password is too long. Max length is set to 30 characters. If you believe it should be higher please contact the developers.");
}
if(strlen($userEmail) > 220){
$err[] = __("Your email-address is too long. Can you please register with another email?");
}
if(strlen($userName) > 200){
$err[] = __("Your name seems to be too long to fit this system. Maybe you can short it down somehow?");
}
if(empty($err)){
// HASH PASSWORD
$newPass = PwdGen($userPass, true);
$createUserSQL = "INSERT INTO user SET user_email = ?, pwd = ?, full_name = ?;";
try {
DB::query($createUserSQL, $userEmail, $newPass, $userName);
$newID = DB::insert_id();
if(empty($newID)){ return false; }
$updateUserSQL = "UPDATE user SET md5_id = ? WHERE user_id = ?;";
DB::query($updateUserSQL, md5($newID), $newID);
return true;
} catch (DatabaseException $e) {
$err[] = __("Something went wrong").":<br>" . $e;
}
}
return $err;
}
public static function checkLogin($strict = false): bool {
$Auth = Auth::getInstance();

View File

@ -4,16 +4,24 @@ class DB {
private static $instance = null;
private $db;
/**
* @throws DatabaseException
*/
private function __construct(){
$this->db = new mysqli(
Config::get('database', 'host'),
Config::get('database', "user"),
Config::get('database', "pass"),
Config::get('database', "database")
);
try {
$this->db = new mysqli(
Config::get('database', 'host'),
Config::get('database', "user"),
Config::get('database', "pass"),
Config::get('database', "database")
);
if($this->db->connect_error){
die("Connection failed: " . $this->db->connect_error);
if($this->db->connect_error){
throw new DatabaseException("Database connection failed: ". $this->db->connect_error);
}
}
catch (mysqli_sql_exception $e){
throw new DatabaseException($e->getMessage(), $e->getCode());
}
}
@ -52,16 +60,12 @@ class DB {
}
/**
* @param integer $mode 0: Execute normally - 1: Execute, but print query - 2: Only print query
* @param string $sql Input SQL as string
* @param mixed ...$params A list or an array of values for the query
* @throws DatabaseException
*/
private static function doQuery($mode, $sql, ...$params){
/*
Modes:
0: Execute normally
1: Execute, but print query
2: Only print query
*/
private static function doQuery(int $mode, string $sql, ...$params){
// Check if query parameters comes as an array, or as individual arguments
if(count($params) == 1 && is_array($params[0])){
$params = $params[0];
@ -69,7 +73,7 @@ class DB {
// Create type-specification for query
$types = "";
foreach($params as &$value){
foreach($params as $value){
if(is_double($value)){
$types .= "d";
}
@ -90,17 +94,44 @@ class DB {
return [];
}
$stmt = $db->db->prepare($sql);
if(
$stmt === false ||
$stmt->bind_param($types, ...$params) === false ||
$stmt->execute() === false
){
throw new DatabaseException($db->db->error);
try {
$stmt = $db->db->prepare($sql);
if(
$stmt === false ||
(!empty($params) && $stmt->bind_param($types, ...$params) === false) ||
$stmt->execute() === false
){
throw new DatabaseException($db->db->error);
}
}
catch (mysqli_sql_exception $e){
throw new DatabaseException($e->getMessage(), $e->getCode());
}
return $stmt->get_result();
}
/**
* @throws DatabaseException
*/
public static function multiQuery(...$sql){
$db = DB::getInstance();
foreach ($sql as $s){
try {
$db->db->multi_query($s);
}
catch (mysqli_sql_exception $e){
throw new DatabaseException($e->getMessage(), $e->getCode());
}
}
}
public static function insert_id(){
$db = DB::getInstance();
return $db->db->insert_id;
}
}
class DatabaseException extends Exception {

170
application/Translator.php Normal file
View File

@ -0,0 +1,170 @@
<?php
class Translator {
public function __construct(){
$this->language = "nb";
$this->country = "NO";
$this->charset = "utf8";
$this->locale = $this->language."_".$this->country.".".$this->charset;
if (defined('LC_MESSAGES')) {
setlocale(LC_MESSAGES, $this->locale);
bindtextdomain("domain1", __dir__."/../locale/");
} else {
echo "IS IN THE ELSE - PART";
}
textdomain("domain1");
}
}
/*
use Gettext\Loader\PoLoader;
class Translator {
private static $instance = null;
private $translations;
private $language;
private $country;
private $charset;
private $locale;
private function __construct($lang = "nb", $contry = "NO", $charset = "utf8"){
$this->language = $lang;
$this->country = $contry;
$this->charset = $charset;
$this->locale = $this->language."_".$this->country.".".$this->charset;
//import from a .po file:
$poloader = new PoLoader();
$this->translations = $poloader->loadFile('locale/'.$this->language.'.po');
}
public static function getInstance(): Translator {
if(self::$instance == null){
self::$instance = new Translator();
}
return self::$instance;
}
static public function translate($string, $domain = "messages"): string {
$trans = self::getInstance();
return $trans->translations;
}
static public function getBrowserSupportedLanguage(){
$language = null;
$input = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
$supported_languages = ["en", "no"];
$browserLanguages = explode(",", str_replace(' ', '', strtolower($input)));
foreach ($browserLanguages as $languageString){
// Stop looking for languages if one is found
if(!empty($language)){ break; }
// Check for language in format "en_US"
$stringParts = explode('-', $languageString);
// If not in that format, check for "en;q=0.9" format
if(count($stringParts) <= 1){
$stringParts = explode(';', $languageString);
}
// If whichever format, check if the first key is a supported language
if(count($stringParts) > 1 && in_array($stringParts[0], $supported_languages)){
$language = $stringParts[0];
}
// If in neither format, check the whole string
elseif(in_array($languageString, $supported_languages)){
$language = $languageString;
}
}
// If no language found, set the first one in the supported-list
if(empty($language)){
$language = $supported_languages[0];
}
return $language;
}
}
*/
use Gettext\Scanner\PhpScanner;
use Gettext\Translations;
use Gettext\Generator\PoGenerator;
use Gettext\Generator\MoGenerator;
class TranslatorScanning {
public $scanner;
public function __construct($domain = "default"){
$phpScanner = new PhpScanner(
Translations::create($domain)
);
//Set a default domain, so any translations with no domain specified, will be added to that domain
$phpScanner->setDefaultDomain($domain);
//Extract all comments starting with 'i18n:' and 'Translators:'
$phpScanner->extractCommentsStartingWith('i18n:', 'Translators:');
//Scan files
foreach (glob(__DIR__.'/../www/{*,*/*,*/*/*}.php', GLOB_BRACE) as $file) {
// echo $file."<br>\n";
$phpScanner->scanFile($file);
}
$this->scanner = $phpScanner;
}
public function save(){
//Save the translations in .po files
$generator = new PoGenerator();
foreach ($this->phpScanner->getTranslations() as $domain => $translations) {
echo "Generate file: ".__DIR__."/../www/{$domain}.po<br>\n";
$generator->generateFile($translations, __DIR__."/../www/{$domain}.po");
}
}
public static function compiler(){
$loader = new PoLoader();
//From a file
$translations = $loader->loadFile(__DIR__."/../www/domain1.po");
$generator = new MoGenerator();
$generator->generateFile($translations, __DIR__."/../www/domain1.mo");
// From a string
// $string = file_get_contents('locales2/en.po');
// $translations = $loader->loadString($string);
}
}
// Resources: https://packagist.org/packages/gettext/gettext
// Resources: https://www.sitepoint.com/easy-multi-language-twig-apps-with-gettext/
function __($string, ...$values): string {
if(DEBUG){
logTranslationKey($string);
}
return sprintf( _($string), $values);
}
function logTranslationKey($string){
file_put_contents("translationkeys.txt", file_get_contents("translationkeys.txt") . "\n" . $string);
}

View File

@ -18,5 +18,16 @@ class Utils {
}
}
public static function debug(...$args){
foreach($args as $arg){
echo "<pre>";
if(is_bool($arg)){
var_dump($arg);
}
else {
echo print_r($arg, true);
}
echo "</pre>\n";
}
}
}

View File

@ -17,6 +17,8 @@ class WebPage {
public function __construct(){
new Translator();
$this->pr = Config::get('system', 'projectroot', '');
$this->loggedIn = Auth::checkLogin();
@ -28,6 +30,11 @@ class WebPage {
}
try {
if(isset($_POST) && !empty($_POST)){
$this->sanitizePost();
$this->doPost();
}
$this->prepareNavbar();
$this->load();
@ -35,7 +42,7 @@ class WebPage {
$this->template = str_replace('.php', '.html', $_SERVER['SCRIPT_NAME']);
}
if($this->httpStatus == 200){
if($this->httpStatus == 200 || $this instanceof ErrorPage){
global $twig;
if(DEBUG){
@ -43,7 +50,7 @@ class WebPage {
header("X-Script-name: ".$_SERVER['SCRIPT_NAME']);
}
http_response_code(200);
http_response_code($this->httpStatus);
echo $twig->render($this->template, $this->vars());
}
@ -55,7 +62,9 @@ class WebPage {
catch (\Twig\Error\LoaderError $e){
http_response_code(500);
if( $e->getCode() == 0){
echo "<pre>";
print_r($e->getMessage());
echo "</pre>";
}
else {
echo $e;
@ -63,12 +72,21 @@ class WebPage {
}
catch(\Twig\Error\RuntimeError | \Twig\Error\SyntaxError | Exception $e) {
http_response_code(500);
echo $e;
print_r( $this->vars() );
if(!$this instanceof ErrorPage){
$e = new ErrorPage(500, $e, print_r( $this->vars(), true ));
}
else {
echo "<h1>A critical error occurred!</h1>";
echo "<pre>".$e."</pre>";
}
}
}
function load(){ throw new Exception("Incomplete implementation"); }
function load(){ throw new Exception("Incomplete implementation"); }
function doPost(){ throw new Exception("Incomplete implementation"); }
function vars(): array {
return (array) $this;
@ -83,6 +101,20 @@ class WebPage {
["href"=>"/review/", "name"=>"Review", "active"=>$this->activePage=="/review/"?'active':'']
], $this->navbar ?? []);
}
protected function sanitizePost(){
$data = [];
foreach($_POST as $key => $value){
if(($data[$key] = Utils::filter($value)) === false){
$err[] = __("Failed to sanitize: `%s`: %s \t-\t type: %s\n", $key, $value, gettype($value));
}
}
if(!empty($err)){ return $err; }
$this->data = $data;
return true;
}
}
class WebPageAuth extends WebPage {
@ -91,4 +123,38 @@ class WebPageAuth extends WebPage {
parent::__construct();
}
}
}
class ErrorPage extends WebPage {
public $errCode = 500;
public $errormessage = "An unexpected error occurred";
public $errorDescription = "";
public $template = "errors/500.html";
public function load(){
if($this->errCode != 500){
$this->template = "errors/$this->errCode.html";
}
}
public function __construct($errCode = 500, $e = "", $extra = ""){
$this->errCode = $errCode;
if($e instanceof Exception){
$this->errormessage = $e->getMessage();
}
$this->errorDescription = $e;
$this->errorDescription .= "\n".$extra;
parent::__construct();
}
}
function isExecutingPage($file) : bool {
$file1 = explode("/", $file);
$request = str_replace('/','', $_SERVER['REQUEST_URI']);
if(strstr($request, '?')){
$request = explode('?', $request)[0];
}
return $request == $file1[ array_key_last($file1) ];
}

View File

@ -2,6 +2,9 @@
"require": {
"twbs/bootstrap": "5.1.3",
"twig/twig": "^3.3",
"ext-mysqli": "*"
"ext-mysqli": "*",
"gettext/gettext": "^5.6",
"ext-gettext": "*",
"gettext/php-scanner": "^1.3"
}
}

272
composer.lock generated
View File

@ -4,8 +4,271 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "0daa3afffc94d70bd593d310cef916f8",
"content-hash": "6111dbb0fd97c255b22341c3bbfb4f67",
"packages": [
{
"name": "gettext/gettext",
"version": "v5.6.1",
"source": {
"type": "git",
"url": "https://github.com/php-gettext/Gettext.git",
"reference": "017e249601d32b9a88c2eb4c10eac89bf582a7d3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-gettext/Gettext/zipball/017e249601d32b9a88c2eb4c10eac89bf582a7d3",
"reference": "017e249601d32b9a88c2eb4c10eac89bf582a7d3",
"shasum": ""
},
"require": {
"gettext/languages": "^2.3",
"php": "^7.2|^8.0"
},
"require-dev": {
"brick/varexporter": "^0.3.5",
"friendsofphp/php-cs-fixer": "^3.2",
"oscarotero/php-cs-fixer-config": "^2.0",
"phpunit/phpunit": "^8.0|^9.0",
"squizlabs/php_codesniffer": "^3.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Gettext\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Oscar Otero",
"email": "oom@oscarotero.com",
"homepage": "http://oscarotero.com",
"role": "Developer"
}
],
"description": "PHP gettext manager",
"homepage": "https://github.com/php-gettext/Gettext",
"keywords": [
"JS",
"gettext",
"i18n",
"mo",
"po",
"translation"
],
"support": {
"email": "oom@oscarotero.com",
"issues": "https://github.com/php-gettext/Gettext/issues",
"source": "https://github.com/php-gettext/Gettext/tree/v5.6.1"
},
"funding": [
{
"url": "https://paypal.me/oscarotero",
"type": "custom"
},
{
"url": "https://github.com/oscarotero",
"type": "github"
},
{
"url": "https://www.patreon.com/misteroom",
"type": "patreon"
}
],
"time": "2021-12-04T11:33:21+00:00"
},
{
"name": "gettext/languages",
"version": "2.9.0",
"source": {
"type": "git",
"url": "https://github.com/php-gettext/Languages.git",
"reference": "ed56dd2c7f4024cc953ed180d25f02f2640e3ffa"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-gettext/Languages/zipball/ed56dd2c7f4024cc953ed180d25f02f2640e3ffa",
"reference": "ed56dd2c7f4024cc953ed180d25f02f2640e3ffa",
"shasum": ""
},
"require": {
"php": ">=5.3"
},
"require-dev": {
"phpunit/phpunit": "^4.8 || ^5.7 || ^6.5 || ^7.5 || ^8.4"
},
"bin": [
"bin/export-plural-rules"
],
"type": "library",
"autoload": {
"psr-4": {
"Gettext\\Languages\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michele Locati",
"email": "mlocati@gmail.com",
"role": "Developer"
}
],
"description": "gettext languages with plural rules",
"homepage": "https://github.com/php-gettext/Languages",
"keywords": [
"cldr",
"i18n",
"internationalization",
"l10n",
"language",
"languages",
"localization",
"php",
"plural",
"plural rules",
"plurals",
"translate",
"translations",
"unicode"
],
"support": {
"issues": "https://github.com/php-gettext/Languages/issues",
"source": "https://github.com/php-gettext/Languages/tree/2.9.0"
},
"funding": [
{
"url": "https://paypal.me/mlocati",
"type": "custom"
},
{
"url": "https://github.com/mlocati",
"type": "github"
}
],
"time": "2021-11-11T17:30:39+00:00"
},
{
"name": "gettext/php-scanner",
"version": "v1.3.1",
"source": {
"type": "git",
"url": "https://github.com/php-gettext/PHP-Scanner.git",
"reference": "989a2cffa1d0f43d13b14c83a50429119b5eb8e4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-gettext/PHP-Scanner/zipball/989a2cffa1d0f43d13b14c83a50429119b5eb8e4",
"reference": "989a2cffa1d0f43d13b14c83a50429119b5eb8e4",
"shasum": ""
},
"require": {
"gettext/gettext": "^5.5.0",
"nikic/php-parser": "^4.2",
"php": ">=7.2"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.15",
"oscarotero/php-cs-fixer-config": "^1.0",
"phpunit/phpunit": "^8.0",
"squizlabs/php_codesniffer": "^3.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Gettext\\Scanner\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Oscar Otero",
"email": "oom@oscarotero.com",
"homepage": "http://oscarotero.com",
"role": "Developer"
}
],
"description": "PHP scanner for gettext",
"homepage": "https://github.com/php-gettext/PHP-Scanner",
"keywords": [
"gettext",
"i18n",
"php",
"scanner",
"translation"
],
"support": {
"email": "oom@oscarotero.com",
"issues": "https://github.com/php-gettext/PHP-Scanner/issues",
"source": "https://github.com/php-gettext/PHP-Scanner/tree/v1.3.1"
},
"time": "2022-03-18T11:47:55+00:00"
},
{
"name": "nikic/php-parser",
"version": "v4.13.2",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "210577fe3cf7badcc5814d99455df46564f3c077"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/210577fe3cf7badcc5814d99455df46564f3c077",
"reference": "210577fe3cf7badcc5814d99455df46564f3c077",
"shasum": ""
},
"require": {
"ext-tokenizer": "*",
"php": ">=7.0"
},
"require-dev": {
"ircmaxell/php-yacc": "^0.0.7",
"phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0"
},
"bin": [
"bin/php-parse"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.9-dev"
}
},
"autoload": {
"psr-4": {
"PhpParser\\": "lib/PhpParser"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Nikita Popov"
}
],
"description": "A PHP parser written in PHP",
"keywords": [
"parser",
"php"
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v4.13.2"
},
"time": "2021-11-30T19:35:32+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.25.0",
@ -304,7 +567,10 @@
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform": {
"ext-mysqli": "*",
"ext-gettext": "*"
},
"platform-dev": [],
"plugin-api-version": "2.2.0"
"plugin-api-version": "2.3.0"
}

7
scripts/cron.php Normal file
View File

@ -0,0 +1,7 @@
<?php
require_once __dir__ . '/../Router.php';
// Deletes logins which are older than 24 hours.
DB::query("DELETE FROM user_login WHERE expire = 0 AND ctime < ?", time() - 24 * 60 * 60);

View File

@ -49,9 +49,9 @@
</ul>
<div class="navbar-nav" id="navLogin">
{% if loggedIn %}
<a class='nav-item nav-link' href='{{ pr }}/logout.php'>Log out</a>
<a class='nav-item nav-link' href='{{ pr }}/logout.php'>{{ 'Log out'|t }}</a>
{% else %}
<a class="nav-item nav-link" id='login' href='{{ pr }}/login.php'>Login</a>
<a class="nav-item nav-link" id='login' href='{{ pr }}/login.php'>{{ 'Login'|t }}</a>
{% endif %}
</div>
</div>
@ -71,10 +71,11 @@
{% endif %}
{% block content %}{% endblock %}
</div>
<footer>
Any prices you find on this website is added by its users and are only a guideline.<br>
{{ 'Any prices you find on this website is added by its users and are only a guideline.'|t }}<br>
<strong>PaperBag</strong> is a <a href="//svagard.no/projects/paperbag" target="_blank" rel="nofollow">Svagård</a> project<br>
{#
@ -88,5 +89,7 @@
</footer>
</div>
{% block endContent %}{% endblock %}
</body>
</html>

View File

@ -0,0 +1,12 @@
{% extends "assets/base.html.twig" %}
{% block content %}
<h1 class="headline text-center"><img src='paperbag_small.svg' style="height: 4rem;" alt='Logo' /> PaperBag</h1>
<div class="container-sm" style="padding-top: 5px; text-align: center; background: #fffe; border-radius: 10px;">
<h1>500 - An unexpected error occured</h1>
<h3>{{ errormessage }}</h3>
<pre style="text-align: left;">{{ errorDescription }}</pre>
</div>
{% endblock %}

View File

@ -9,7 +9,7 @@
<div class="form-group row mb-1">
<label for="loginEmail" class="col-sm-2 col-form-label">Email</label>
<div class="col-sm-10">
<input type="email" class="form-control" name="loginEmail" id="loginEmail">
<input type="email" class="form-control" name="loginEmail" id="loginEmail" value="{{ data.loginEmail }}">
</div>
</div>
<div class="form-group row">
@ -19,7 +19,7 @@
</div>
</div>
<div class="form-check" style="margin: 3px 0;">
<input class="form-check-input" type="checkbox" id="gridCheck">
<input class="form-check-input" type="checkbox" id="gridCheck" name='cookieAccept' value="1" {% if data.cookieAccept %}checked="checked"{% endif %}">
<label class="form-check-label" for="gridCheck">
I agree to save a temporary cookie in my browser for the logged in functions to work.
</label>

View File

@ -0,0 +1,36 @@
{% extends "assets/base.html.twig" %}
{% block content %}
<div class='container-sm' style="max-width: 540px;">
<h1 class="headline text-center">Register</h1>
<form action="register.php" method="POST" class="row">
<input type="hidden" name="referrerPage" value="<?=$returnToPage;?>">
<div class="col-md-6">
<label for="newEmail" class="form-label">Email*</label>
<input type="email" class="form-control" id="newEmail" name="newEmail" maxlength="220" required value="{{ data.newEmail }}">
</div>
<div class="col-md-6">
<label for="newPassword" class="form-label">Password*</label>
<input type="password" class="form-control" id="newPassword" name="newPassword" minlength="6" maxlength="30" required>
</div>
<div class="col-12">
<label for="newName" class="form-label">Full name</label>
<input type="text" class="form-control" id="newName" name="newName" maxlength="200" value="{{ data.newName }}">
</div>
<div class="col-12" style="margin: 3px 0;">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="gridCheck" required>
<label class="form-check-label" for="gridCheck">
I will read the Terms and Conditions when they are published.
</label>
</div>
</div>
<div class="col-12 mb-3">
<button type="submit" class="btn btn-primary w-100">Sign up</button>
</div>
</form>
<p>Already have an account? <a href="login.php">Login</a></p>
</div>
{% endblock %}

View File

@ -4,7 +4,7 @@ require_once '../Router.php';
class LoginPage extends WebPage {
public $pagekey = "login";
public $title = "PaperBag - Plan & Execute Your Shopping";
public $title = "Login - PaperBag - Plan & Execute Your Shopping";
public $returnToPage = "/";
function load(){
@ -17,13 +17,9 @@ class LoginPage extends WebPage {
if(isset($_GET['thank'])){
$this->msg[] = "Thank you for registering. Please log in to continue!";
}
if(isset($_POST) && !empty($_POST)){
$this->doPost();
}
}
private function findReturnPage(){
protected function findReturnPage(){
if(isset($_POST['referrerPage'])){
$this->returnToPage = $_POST['referrerPage'];
}
@ -39,23 +35,22 @@ class LoginPage extends WebPage {
}
}
private function doPost(){
$data = [];
foreach($_POST as $key => $value){
if(($data[$key] = Utils::filter($value)) === false){
echo "Failed to sanitize: `".$key."`: ".$value." \t-\t type: ".gettype($value)."\n";
}
}
function doPost(){
$stayLoggedIn = isset($_POST['stayLoggedIn']);
$err = Auth::loginWithCredentials($data['loginEmail'], $data['loginPwd'], $stayLoggedIn);
$err = Auth::loginWithCredentials($this->data['loginEmail'], $this->data['loginPwd'], $stayLoggedIn);
if($err === true){
header("Location: ".$this->returnToPage);
die();
}
// Do not send the password back to the client...
$this->data['loginPwd'] = null;
$this->err = $err;
}
}
$a = new LoginPage();
// For use when class is extended upon
if(isExecutingPage(__file__)){
$a = new LoginPage();
}

View File

@ -1,7 +1,6 @@
<?php
require 'webdata/init.php';
require_once '../Router.php';
logoutUser();
header("Location: ".getConfig('projectRoot')."/");
Auth::logout();
header("Location: ".Config::get('system', 'projectRoot', '')."/");

View File

@ -1,120 +1,32 @@
<?php
require 'webdata/init.php';
$returnToPage = "./";
if(isset($_POST['referrerPage'])){
$returnToPage = $_POST['referrerPage'];
}
else if(isset($_SERVER['HTTP_REFERER'])){
$returnToPage = $_SERVER['HTTP_REFERER'];
}
if(stristr($returnToPage, "login.php") || stristr($returnToPage, "register.php")){
$returnToPage = "./";
}
require_once '../Router.php';
require_once 'login.php';
if(isset($_POST) && !empty($_POST)) {
$db = database();
$data = [];
class RegisterPage extends LoginPage {
public $pagekey = "register";
public $title = "Register - PaperBag - Plan & Execute Your Shopping";
public $returnToPage = "/";
foreach ($_POST as $key => $value) {
if (($data[$key] = filter($value)) === false) {
print_r($value);
echo "Failed to sanitize: `" . $key . "`: " . $value . " \t-\t type: " . gettype($value) . "\n";
function load(){
$this->findReturnPage();
if(Auth::checkLogin(true)){
header("Location: ".$this->returnToPage);
}
}
$userEmail = $data['newEmail'];
$userPass = $data['newPassword'];
$userName = $data['newName'] ?? '';
if(strlen($userPass) < 6){
$err[] = "Password is too short. Password needs to be at least 6 characters!";
}
else if(strlen($userPass) > 30){
$err[] = "Password is too long. Max length is set to 30 characters. If you believe it should be higher please contact the developers.";
}
if(strlen($userEmail) > 220){
$err[] = "Your email-address is too long. Can you please register with another email?";
}
if(strlen($userName) > 200){
$err[] = "Your name seems to be too large to fit this system. Maybe you can short it somehow?";
}
if(empty($err)){
// HASH PASSWORD
$newPass = PwdGen($userPass, true);
$createUserSQL = "INSERT INTO user SET full_name = '$userName', user_email = '$userEmail', pwd = '$newPass';";
if($db->query($createUserSQL)){
$newID = $db->insert_id;
$updateUserSQL = "UPDATE user SET md5_id = '".md5($newID)."' WHERE user_id = '$newID';";
$db->query($updateUserSQL);
function doPost(){
$err = Auth::register($this->data['newEmail'], $this->data['newPassword'], $this->data);
if($err === true){
header("Location: login.php?thank");
die();
}
else {
$err[] = "Something went wrong:<br>".$db->error;
}
$this->err = $err;
// Do not send the password back to the client...
$this->data['newPassword'] = null;
}
}
?><!DOCTYPE html>
<html lang="en">
<head>
<?=getHtmlHeaders();?>
<title>Register - PaperBag</title>
</head>
<body id='plan'>
<div id="page-container">
<div id="page-wrapper">
<?php include 'webdata/navbar.php'; ?>
<div class='container-md' style="max-width: 720px;">
<h1 class="headline text-center">Register</h1>
<br>
<?php if(!empty($err)){
foreach($err as $e){
echo "<div class='alert alert-danger' role='alert'>$e</div>";
}
echo "<br>";
}
?>
<form action="register.php" method="POST" class="row">
<input type="hidden" name="referrerPage" value="<?=$returnToPage;?>">
<div class="col-md-6">
<label for="newEmail" class="form-label">Email*</label>
<input type="email" class="form-control" id="newEmail" name="newEmail" maxlength="220" required value="<?= $data['newEmail'] ?? '';?>">
</div>
<div class="col-md-6">
<label for="newPassword" class="form-label">Password*</label>
<input type="password" class="form-control" id="newPassword" name="newPassword" minlength="6" maxlength="30" required>
</div>
<div class="col-12">
<label for="newName" class="form-label">Full name</label>
<input type="text" class="form-control" id="newName" name="newName" maxlength="200" value="<?= $data['newName'] ?? '';?>">
</div>
<div class="col-12" style="margin: 3px 0;">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="gridCheck" required>
<label class="form-check-label" for="gridCheck">
I will read the Terms and Conditions when they are published.
</label>
</div>
</div>
<div class="col-12 mb-3">
<button type="submit" class="btn btn-primary w-100">Sign up</button>
</div>
</form>
<p>Already have an account? <a href="login.php">Login</a></p>
</div>
</div>
<?php include 'webdata/footer.html'; ?>
</div>
</body>
</html>
$a = new RegisterPage();