Added Twig and started templating the pages.
parent
ccd8557362
commit
2f394e530f
|
@ -3,3 +3,6 @@ www/webdata/config.php
|
|||
www/temp/
|
||||
www/css/archive/*
|
||||
www/js/archive/*
|
||||
vendor
|
||||
config/config.ini
|
||||
tmp/
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
const PRODUCTION = false;
|
||||
const DEBUG = true;
|
||||
|
||||
if(DEBUG){
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
}
|
||||
|
||||
|
||||
// Composer autoload
|
||||
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';
|
||||
}
|
||||
elseif(file_exists(__DIR__."/models/".$class_name.'.php')) {
|
||||
include __DIR__."/models/".$class_name.'.php';
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Define Twig
|
||||
$twigSettings = PRODUCTION?['cache' => __dir__.'/tmp/']:[];
|
||||
|
||||
$loader = new Twig\Loader\FilesystemLoader(__dir__ . '/templates/'.Config::get('system', 'template', 'default')."/");
|
||||
$twig = new Twig\Environment($loader, $twigSettings);
|
||||
|
||||
|
||||
class Config {
|
||||
private static $instance = null;
|
||||
private $config;
|
||||
|
||||
private function __construct($file = 'config/config.ini'){
|
||||
$this->config = parse_ini_file($file, true);
|
||||
}
|
||||
|
||||
public static function get(string $category, string $config, $default = false){
|
||||
// Singleton method to avoid reading the same file (too) many times
|
||||
if (self::$instance == null){
|
||||
self::$instance = new Config();
|
||||
}
|
||||
|
||||
$conf = self::$instance;
|
||||
|
||||
if(!empty($conf->config[$category]) && !empty($conf->config[$category][$config])){
|
||||
return $conf->config[$category][$config];
|
||||
}
|
||||
return $default;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
<?php
|
||||
|
||||
class Auth {
|
||||
private static $instance = null;
|
||||
private $isLoggedIn = false;
|
||||
|
||||
private function __construct(){
|
||||
session_start();
|
||||
}
|
||||
|
||||
public static function getInstance(): Auth {
|
||||
if(self::$instance == null){
|
||||
self::$instance = new Auth();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public static function loginWithCredentials($email, $pass, bool $stayLoggedIn = false){
|
||||
$Auth = new Auth();
|
||||
|
||||
$err = [];
|
||||
|
||||
// get user from database
|
||||
$getUserSQL = "SELECT pwd, user_id, user_email FROM user WHERE user_email = ? LIMIT 1;";
|
||||
$getUserRes = DB::query($getUserSQL, $email);
|
||||
|
||||
$dbPass = "missingPassword";
|
||||
$dbUserId = 0;
|
||||
$dbUserName = "Unknown";
|
||||
|
||||
if($getUserRes->num_rows == 1){
|
||||
// Verify password
|
||||
list($dbPass, $dbUserId, $dbUserName) = $getUserRes->fetch_row();
|
||||
}
|
||||
|
||||
if(password_verify(PwdGen($pass), $dbPass)){
|
||||
session_regenerate_id();
|
||||
|
||||
$md5agent = md5($_SERVER['HTTP_USER_AGENT']);
|
||||
$userKey = GenKey();
|
||||
$shaUserKey = sha1($userKey);
|
||||
$expire = $stayLoggedIn?30:0; // if "stay logged in", stay logged in for 30 days.
|
||||
|
||||
$updateUserSQL = "INSERT INTO user_login (user_id, ckey, ctime, expire, agent) VALUES (?, ?, ?, ?, ?);";
|
||||
if(DB::query($updateUserSQL, $dbUserId, $userKey, time(), $expire, $md5agent)){
|
||||
$_SESSION['user_id'] = $dbUserId;
|
||||
$_SESSION['user_name'] = $dbUserName;
|
||||
$_SESSION['user_agent'] = $md5agent;
|
||||
$_SESSION['user_key'] = $shaUserKey;
|
||||
|
||||
if($stayLoggedIn){
|
||||
setcookie("auth", $shaUserKey.".".$dbUserId, time() + (30 * 24*60*60), "/", $_SERVER['HTTP_HOST'], isset($_SERVER['HTTPS']), true );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
$err[] = "Failed to login.\n".$db->error;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$err[] = "Username and/or Password is wrong.";
|
||||
}
|
||||
|
||||
return $err;
|
||||
}
|
||||
|
||||
|
||||
public static function loginFromAuthKey(): bool {
|
||||
if(isset($_COOKIE['auth'])){
|
||||
$md5agent = md5($_SERVER['HTTP_USER_AGENT']);
|
||||
|
||||
$cookieSplode = explode('.', $_COOKIE['auth']);
|
||||
if(count($cookieSplode) == 2){
|
||||
$tkey = $cookieSplode[0];
|
||||
$tuid = $cookieSplode[1];
|
||||
$verifyLoginCookieRes = DB::query("
|
||||
SELECT ul.user_id, ul.ckey, ul.ctime, ul.expire, ul.agent, u.full_name
|
||||
FROM user_login ul
|
||||
INNER JOIN user u ON ul.user_id = u.user_id
|
||||
WHERE ul.user_id = ?
|
||||
ORDER BY ctime DESC", $tuid);
|
||||
while($row = $verifyLoginCookieRes->fetch_assoc()){
|
||||
if($row['expire'] == 0){ $row['expire'] = 1; }
|
||||
if(
|
||||
sha1($row['ckey']) == $tkey &&
|
||||
$row['agent'] == $md5agent &&
|
||||
($row['ctime']+($row['expire']*24*60*60) > time())
|
||||
){
|
||||
session_regenerate_id();
|
||||
$_SESSION['user_id'] = $row['user_id'];
|
||||
$_SESSION['user_name'] = $row['full_name'];
|
||||
$_SESSION['user_agent'] = $md5agent;
|
||||
$_SESSION['user_key'] = sha1($row['ckey']);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function logout(){}
|
||||
|
||||
|
||||
public static function checkLogin($strict = false): bool {
|
||||
$Auth = Auth::getInstance();
|
||||
|
||||
if(
|
||||
($Auth->isLoggedIn && !$strict) ||
|
||||
$Auth->checkSessionLogin() ||
|
||||
$Auth->loginFromAuthKey()
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
unset($_SESSION['user_id']);
|
||||
return false;
|
||||
}
|
||||
|
||||
private function checkSessionLogin(): bool {
|
||||
if(isset($_SESSION['user_id'])){
|
||||
$md5agent = md5($_SERVER['HTTP_USER_AGENT']);
|
||||
if($md5agent == @$_SESSION['user_agent']){
|
||||
$verifyLoginSessionRes = DB::query("
|
||||
SELECT expire, ctime, ckey
|
||||
FROM `user_login`
|
||||
WHERE user_id = ?
|
||||
AND agent = ?",
|
||||
$_SESSION['user_id'], $md5agent);
|
||||
|
||||
while ($row = $verifyLoginSessionRes->fetch_assoc()){
|
||||
if(
|
||||
(sha1($row['ckey']) == $_SESSION['user_key']) &&
|
||||
($row['expire'] == 0 || $row['ctime']+($row['expire']*24*60*60) > time())
|
||||
){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function PwdGen($pass, $returnHashed = false): string {
|
||||
$pepper = Config::get('security', 'pepper', 'IAmBadAtSecurity');
|
||||
$pwd_peppered = hash_hmac("sha256", $pass, $pepper);
|
||||
if(!$returnHashed){
|
||||
return $pwd_peppered;
|
||||
}
|
||||
|
||||
return password_hash($pwd_peppered, PASSWORD_ARGON2ID, ['threads' => 2]);
|
||||
}
|
||||
|
||||
function GenKey($length = 21): string{
|
||||
$password = "";
|
||||
$possible = "0123456789abcdefghijkmnopqrstuvwxyz";
|
||||
|
||||
$i = 0;
|
||||
|
||||
while($i < $length){
|
||||
$char = substr($possible, mt_rand(0, strlen($possible)-1), 1);
|
||||
|
||||
if(!strstr($password, $char)){
|
||||
$password .= $char;
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
return $password;
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
class DB {
|
||||
private static $instance = null;
|
||||
private $db;
|
||||
|
||||
private function __construct(){
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
public static function getInstance(): DB {
|
||||
if(self::$instance == null){
|
||||
self::$instance = new DB();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public static function query($sql, ...$params){
|
||||
return self::doQuery(0, $sql, ...$params);
|
||||
}
|
||||
|
||||
public static function queryTest($sql, ...$params){
|
||||
return self::doQuery(1, $sql, ...$params);
|
||||
}
|
||||
|
||||
public static function queryPreview($sql, ...$params){
|
||||
return self::doQuery(2, $sql, ...$params);
|
||||
}
|
||||
|
||||
private static function doQuery($mode, $sql, ...$params){
|
||||
/*
|
||||
Modes:
|
||||
0: Execute normally
|
||||
1: Execute, but print query
|
||||
2: Only print query
|
||||
*/
|
||||
|
||||
// Check if query parameters comes as an array, or as individual arguments
|
||||
if(count($params) == 1 && is_array($params[0])){
|
||||
$params = $params[0];
|
||||
}
|
||||
|
||||
// Create type-specification for query
|
||||
$types = "";
|
||||
foreach($params as &$value){
|
||||
if(is_double($value)){
|
||||
$types .= "d";
|
||||
}
|
||||
elseif(is_integer($value)){
|
||||
$types .= "i";
|
||||
}
|
||||
else {
|
||||
$types .= "s";
|
||||
}
|
||||
}
|
||||
|
||||
$db = DB::getInstance();
|
||||
|
||||
if($mode >= 1){
|
||||
printf(str_replace('?', "'%s'", $sql)."\n", ...$params);
|
||||
}
|
||||
if($mode >= 2) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$stmt = $db->db->prepare($sql);
|
||||
$stmt->bind_param($types, ...$params);
|
||||
$stmt->execute();
|
||||
|
||||
return $stmt->get_result();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
interface RequireAuth {}
|
||||
|
||||
class WebPage {
|
||||
protected $httpStatus = 200;
|
||||
protected $activePage;
|
||||
public $status;
|
||||
public $template;
|
||||
public $title = "PaperBag";
|
||||
public $lang = "EN";
|
||||
public $navbar;
|
||||
public $loggedIn = false;
|
||||
public $pr = ""; // project root
|
||||
|
||||
|
||||
public function __construct(){
|
||||
$this->pr = Config::get('system', 'projectroot', '');
|
||||
$this->loggedIn = Auth::checkLogin();
|
||||
|
||||
if($this instanceof RequireAuth && !$this->loggedIn) {
|
||||
$returnTo = str_ireplace('index.php', '', $_SERVER['PHP_SELF']);
|
||||
$_SESSION['pre-auth'] = $returnTo;
|
||||
header("Location: ".$this->pr."/login.php");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->prepareNavbar();
|
||||
$this->load();
|
||||
|
||||
if(!$this->template){
|
||||
$this->template = str_replace('.php', '.html', $_SERVER['SCRIPT_NAME']);
|
||||
}
|
||||
|
||||
if($this->httpStatus == 200){
|
||||
global $twig;
|
||||
|
||||
if(DEBUG){
|
||||
header("X-Template: ".$this->template);
|
||||
header("X-Script-name: ".$_SERVER['SCRIPT_NAME']);
|
||||
}
|
||||
|
||||
http_response_code(200);
|
||||
|
||||
echo $twig->render($this->template, $this->vars());
|
||||
}
|
||||
else {
|
||||
http_response_code($this->httpStatus);
|
||||
print_r( $this->vars() );
|
||||
}
|
||||
}
|
||||
catch (\Twig\Error\LoaderError $e){
|
||||
http_response_code(500);
|
||||
if( $e->getCode() == 0){
|
||||
print_r($e->getMessage());
|
||||
}
|
||||
else {
|
||||
echo $e;
|
||||
}
|
||||
}
|
||||
catch(\Twig\Error\RuntimeError | \Twig\Error\SyntaxError | Exception $e) {
|
||||
http_response_code(500);
|
||||
echo $e;
|
||||
print_r( $this->vars() );
|
||||
}
|
||||
}
|
||||
|
||||
function load(){ throw new Exception("Incomplete implementation"); }
|
||||
|
||||
function vars(): array {
|
||||
return (array) $this;
|
||||
}
|
||||
|
||||
private function prepareNavbar(){
|
||||
$this->activePage = str_replace('index.php', '', $_SERVER['REQUEST_URI']);
|
||||
|
||||
$this->navbar['links'] = array_merge([
|
||||
["href"=>"/", "name"=>"Home", "active"=>$this->activePage=="/"?'active':''],
|
||||
["href"=>"/plan/", "name"=>"Plan", "active"=>$this->activePage=="/plan/"?'active':''],
|
||||
["href"=>"/review/", "name"=>"Review", "active"=>$this->activePage=="/review/"?'active':'']
|
||||
], $this->navbar ?? []);
|
||||
}
|
||||
}
|
||||
|
||||
class WebPageAuth extends WebPage {
|
||||
public function __construct(){
|
||||
if(Auth::checklogin()){
|
||||
parent::__construct();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"require": {
|
||||
"twbs/bootstrap": "5.1.3",
|
||||
"twig/twig": "^3.3",
|
||||
"ext-mysqli": "*"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,310 @@
|
|||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "0daa3afffc94d70bd593d310cef916f8",
|
||||
"packages": [
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.25.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||
"reference": "30885182c981ab175d4d034db0f6f469898070ab"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab",
|
||||
"reference": "30885182c981ab175d4d034db0f6f469898070ab",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"provide": {
|
||||
"ext-ctype": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-ctype": "For best performance"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.23-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Ctype\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Gert de Pagter",
|
||||
"email": "BackEndTea@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for ctype functions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"ctype",
|
||||
"polyfill",
|
||||
"portable"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-10-20T20:35:02+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.25.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
"reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825",
|
||||
"reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"provide": {
|
||||
"ext-mbstring": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-mbstring": "For best performance"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.23-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Mbstring\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for the Mbstring extension",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"mbstring",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.25.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-11-30T18:21:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "twbs/bootstrap",
|
||||
"version": "v5.1.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/twbs/bootstrap.git",
|
||||
"reference": "1a6fdfae6be09b09eaced8f0e442ca6f7680a61e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/twbs/bootstrap/zipball/1a6fdfae6be09b09eaced8f0e442ca6f7680a61e",
|
||||
"reference": "1a6fdfae6be09b09eaced8f0e442ca6f7680a61e",
|
||||
"shasum": ""
|
||||
},
|
||||
"replace": {
|
||||
"twitter/bootstrap": "self.version"
|
||||
},
|
||||
"type": "library",
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mark Otto",
|
||||
"email": "markdotto@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Jacob Thornton",
|
||||
"email": "jacobthornton@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "The most popular front-end framework for developing responsive, mobile first projects on the web.",
|
||||
"homepage": "https://getbootstrap.com/",
|
||||
"keywords": [
|
||||
"JS",
|
||||
"css",
|
||||
"framework",
|
||||
"front-end",
|
||||
"mobile-first",
|
||||
"responsive",
|
||||
"sass",
|
||||
"web"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/twbs/bootstrap/issues",
|
||||
"source": "https://github.com/twbs/bootstrap/tree/v5.1.3"
|
||||
},
|
||||
"time": "2021-10-09T06:43:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "twig/twig",
|
||||
"version": "v3.3.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/twigphp/Twig.git",
|
||||
"reference": "8442df056c51b706793adf80a9fd363406dd3674"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/8442df056c51b706793adf80a9fd363406dd3674",
|
||||
"reference": "8442df056c51b706793adf80a9fd363406dd3674",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"symfony/polyfill-ctype": "^1.8",
|
||||
"symfony/polyfill-mbstring": "^1.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"psr/container": "^1.0",
|
||||
"symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.3-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Twig\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com",
|
||||
"homepage": "http://fabien.potencier.org",
|
||||
"role": "Lead Developer"
|
||||
},
|
||||
{
|
||||
"name": "Twig Team",
|
||||
"role": "Contributors"
|
||||
},
|
||||
{
|
||||
"name": "Armin Ronacher",
|
||||
"email": "armin.ronacher@active-4.com",
|
||||
"role": "Project Founder"
|
||||
}
|
||||
],
|
||||
"description": "Twig, the flexible, fast, and secure template language for PHP",
|
||||
"homepage": "https://twig.symfony.com",
|
||||
"keywords": [
|
||||
"templating"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/twigphp/Twig/issues",
|
||||
"source": "https://github.com/twigphp/Twig/tree/v3.3.10"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/twig/twig",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-04-06T06:47:41+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.2.0"
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace models;
|
||||
|
||||
abstract class Model {
|
||||
|
||||
|
||||
// public function set($value, )
|
||||
|
||||
public function save(){
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace models;
|
||||
|
||||
class Recipe extends Model {
|
||||
|
||||
public static function getRecipes() : array {
|
||||
$db = $db ?? database();
|
||||
|
||||
$recipes = [];
|
||||
|
||||
$result = $db->query("SELECT * FROM recipe");
|
||||
while($row = $result->fetch_assoc()){
|
||||
$recipe = new Recipe();
|
||||
$recipe->
|
||||
|
||||
$recipes[] = $recipe;
|
||||
}
|
||||
|
||||
return $recipes;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
|
||||
require_once $_SERVER['DOCUMENT_ROOT'].'/webdata/init.php';
|
||||
|
||||
include $_SERVER['DOCUMENT_ROOT']."/api/v1/products.php";
|
|
@ -0,0 +1,92 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="{{ language|default('en') }}">
|
||||
<head>
|
||||
<meta charset='UTF-8'>
|
||||
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
|
||||
<title>{{ title|default('PaperBag') }}</title>
|
||||
|
||||
<link rel='stylesheet' href='{{ pr }}/css/bootstrap.min.css' type='text/css' />
|
||||
<link rel='stylesheet' href='{{ pr }}/css/index.css' type='text/css' />
|
||||
|
||||
<link rel='apple-touch-icon' sizes='180x180' href='{{ pr }}/apple-touch-icon.png'>
|
||||
<link rel='icon' type='image/png' sizes='32x32' href='{{ pr }}/favicon-32x32.png'>
|
||||
<link rel='icon' type='image/png' sizes='16x16' href='{{ pr }}/favicon-16x16.png'>
|
||||
<link rel='manifest' href='{{ pr }}/site.webmanifest'>
|
||||
<link rel='mask-icon' href='{{ pr }}/favicon/safari-pinned-tab.svg' color='#5bbad5'>
|
||||
<meta name='msapplication-TileColor' content='#da532c'>
|
||||
<meta name='theme-color' content='#ffffff'>
|
||||
|
||||
<script src='{{ pr }}/js/jquery-3.5.1.min.js'></script>
|
||||
<script src='{{ pr }}/js/popper.min.js'></script>
|
||||
<script src='{{ pr }}/js/bootstrap.min.js'></script>
|
||||
<!--<script src='//cdn.jsdelivr.net/npm/marked/marked.min.js'></script>-->
|
||||
<script src='{{ pr }}/js/hammer.js'></script>
|
||||
<script src='{{ pr }}/js/hammer.jquery.js'></script>
|
||||
|
||||
{% block head %}{% endblock %}
|
||||
|
||||
</head>
|
||||
<body id="{{ pagekey|default('') }}">
|
||||
|
||||
<div id="page-container">
|
||||
<div id="page-wrapper">
|
||||
<nav class="navbar navbar-expand-sm navbar-light bg-light" style="z-index: 2;">
|
||||
<div class="container-fluid" style="max-width: 900px;">
|
||||
<a class="navbar-brand" href="{{ pr }}/"><img src='{{ pr }}/paperbag_small.svg' style="height: 24px;" alt='Logo' /> PaperBag</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
|
||||
data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false"
|
||||
aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-sm-0">
|
||||
{% for link in navbar.links %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ link.active }}" aria-current="page" href="{{ link.href }}">{{ link.name }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<div class="navbar-nav" id="navLogin">
|
||||
{% if loggedIn %}
|
||||
<a class='nav-item nav-link' href='{{ pr }}/logout.php'>Log out</a>
|
||||
{% else %}
|
||||
<a class="nav-item nav-link" id='login' href='{{ pr }}/login.php'>Login</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{% if err or msg %}
|
||||
<div class='container-sm' style="max-width: 540px;">
|
||||
{% for e in err %}
|
||||
<div class='alert alert-danger' role='alert'>{{ e }}</div>
|
||||
{% endfor %}
|
||||
|
||||
{% for m in msg %}
|
||||
<div class='alert alert-success' role='alert'>{{ m }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
Any prices you find on this website is added by its users and are only a guideline.<br>
|
||||
<strong>PaperBag</strong> is a <a href="//svagard.no/projects/paperbag" target="_blank" rel="nofollow">Svagård</a> project<br>
|
||||
|
||||
{#
|
||||
<!--<a href="https://useg.it/eirik/GroceryAssist" target="_blank" rel="nofollow">View source</a> |
|
||||
<a href="/cookies" rel="nofollow">Cookies</a> |
|
||||
<a href="/tos" rel="nofollow">Terms of Use</a> |
|
||||
<a href="/privacy" rel="nofollow">Privacy Policy</a>-->
|
||||
#}
|
||||
{% block footer %}{% endblock %}
|
||||
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,40 @@
|
|||
{% extends "assets/base.html.twig" %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="headline text-center"><img src='paperbag_small.svg' style="width: 4rem;" alt='Logo' /> PaperBag</h1>
|
||||
<div class="container-sm" style="padding-top: 5px;">
|
||||
|
||||
<div class="card-group" style="text-align: center;">
|
||||
|
||||
<div class="card h-100" style="">
|
||||
<a href='./plan'>
|
||||
<!-- <img src="#" class="card-img-top" alt="Plan">-->
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Plan shopping</h5>
|
||||
<p class="card-text"></p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="card h-100" style="">
|
||||
<!-- <img src="#" class="card-img-top" alt="Review">-->
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Review</h5>
|
||||
<p class="card-text">Coming soon</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card-group" style="text-align: center;">
|
||||
<div class="card" style="">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Recent changes</h5>
|
||||
<pre class="card-text" style="text-align: left; width: min-content; max-width: 100%; margin: auto; max-height: 150px; overflow-y: auto; padding: 0 15px;">{{ changes }}</pre>
|
||||
</div>
|
||||
<div class="card-footer">For more details see the <a href="https://useg.it/eirik/PaperBag/commits/branch/master">commits</a></div>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -0,0 +1,37 @@
|
|||
{% extends "assets/base.html.twig" %}
|
||||
|
||||
{% block content %}
|
||||
<div class='container-sm' style="max-width: 540px;">
|
||||
|
||||
<h1 class="headline text-center">Login</h1>
|
||||
|
||||
<form action="login.php" method="POST">
|
||||
<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">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="loginPwd" class="col-sm-2 col-form-label">Password</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" class="form-control" name="loginPwd" id="loginPwd">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-check" style="margin: 3px 0;">
|
||||
<input class="form-check-input" type="checkbox" id="gridCheck">
|
||||
<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>
|
||||
</div>
|
||||
<div class="form-check" style="margin: 3px 0;">
|
||||
<input class="form-check-input" type="checkbox" id="rememberCheck" name="stayLoggedIn">
|
||||
<label class="form-check-label" for="rememberCheck">
|
||||
Remember me (for 30 days)
|
||||
</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary mb-3 w-100">Login</button>
|
||||
</form>
|
||||
<p>Do you not have an account? <a href="register.php">Register</a> </p>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -0,0 +1,82 @@
|
|||
{% extends "assets/base.html.twig" %}
|
||||
|
||||
{% block head %}
|
||||
<!-- jsDelivr :: Sortable :: Latest (https://www.jsdelivr.com/package/npm/sortablejs) -->
|
||||
<script src="/js/sortable/Sortable.min.js"></script> <!-- CDN: https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js -->
|
||||
<script src="/js/sortable/jquery-sortable.js"></script> <!-- CDN: https://cdn.jsdelivr.net/npm/jquery-sortablejs@latest/jquery-sortable.js -->
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="headline text-center">PaperBag</h1>
|
||||
<div class="container" style="padding-top: 5px; padding-bottom: 15px; text-align: center;">
|
||||
|
||||
<div id="spaceSelectWrapper" style="max-width: 900px; margin: auto;">
|
||||
<div class="input-group" style="width: 200px;">
|
||||
<label class="input-group-text" for="spaceSelect">Space:</label>
|
||||
<select class="form-select" id='spaceSelect' aria-label="Select space">
|
||||
<option>...</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-columns" id="stores" style="text-align: center;">
|
||||
<noscript>
|
||||
<hr>
|
||||
<h5>Please enable javascript for this page to work</h5>
|
||||
<hr>
|
||||
</noscript>
|
||||
</div>
|
||||
|
||||
<div id='totalPriceWrapper'>Space subtotal: <span id="totalPrice" class="priceWrapper price">00.00</span></div>
|
||||
<br>
|
||||
<button class="btn btn-primary mb-2" id="addStore">Add store</button><br>
|
||||
<button class="btn btn-secondary" id="refreshAll">Refresh all</button>
|
||||
|
||||
</div>
|
||||
|
||||
<script src='htmlElements.js'></script>
|
||||
<script src='plan.js'></script>
|
||||
<script src='recipe.js'></script>
|
||||
<script src='product.js'></script>
|
||||
|
||||
<div class="modal" tabindex="-1" id="addStoreModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Add store</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body align-content-center">
|
||||
<button class="btn btn-primary" id="addEmptyStore">Empty store</button>
|
||||
<hr>
|
||||
<div class="accordion mb-2" id="addStoreRecipe"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let addStoreModal = new bootstrap.Modal( document.getElementById('addStoreModal') );
|
||||
|
||||
$("#addStore").on('click', ev=>{
|
||||
ev.stopPropagation();
|
||||
addStoreModal.show();
|
||||
});
|
||||
|
||||
$("#addStoreModal").on('show.bs.modal', ev=> {
|
||||
$("#addStoreRecipe").html('');
|
||||
|
||||
$.getJSON('/api/v1/recipe', {}, resp => {
|
||||
let recipes = new Recipe(resp);
|
||||
recipes.getAccordionHtml('#addStoreRecipe');
|
||||
});
|
||||
});
|
||||
|
||||
$("#addEmptyStore").on('click', ev => {
|
||||
stores.push(new Store());
|
||||
addStoreModal.hide();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
|
@ -1,56 +1,17 @@
|
|||
<?php require 'webdata/init.php'; ?><!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<?=getHtmlHeaders();?>
|
||||
<title>PaperBag - Plan & Execute Your Shopping</title>
|
||||
</head>
|
||||
<body id="home">
|
||||
<div id="page-container">
|
||||
<div id="page-wrapper">
|
||||
<?php
|
||||
require_once '../Router.php';
|
||||
|
||||
<?php include 'webdata/navbar.php'; ?>
|
||||
class HomePage extends WebPage {
|
||||
public $pagekey = "home";
|
||||
public $title = "PaperBag - Plan & Execute Your Shopping";
|
||||
|
||||
<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;">
|
||||
function load(){
|
||||
|
||||
<div class="card-group" style="text-align: center;">
|
||||
if (file_exists('changes.txt')) {
|
||||
$this->changes = file_get_contents('changes.txt');
|
||||
}
|
||||
|
||||
<div class="card h-100" style="">
|
||||
<a href='./plan'>
|
||||
<!-- <img src="#" class="card-img-top" alt="Plan">-->
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Plan shopping</h5>
|
||||
<p class="card-text"></p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
<div class="card h-100" style="">
|
||||
<!-- <img src="#" class="card-img-top" alt="Review">-->
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Review</h5>
|
||||
<p class="card-text">Coming soon</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<?php if(file_exists('changes.txt')){
|
||||
$changes = file_get_contents('changes.txt');
|
||||
?>
|
||||
<div class="card-group" style="text-align: center;">
|
||||
<div class="card" style="">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Recent changes</h5>
|
||||
<pre class="card-text" style="text-align: left; width: min-content; max-width: 100%; margin: auto; max-height: 150px; overflow-y: auto; padding: 0 15px;"><?=$changes;?></pre>
|
||||
</div>
|
||||
<div class="card-footer">For more details see the <a href="https://useg.it/eirik/PaperBag/commits/branch/master">commits</a></div>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include 'webdata/footer.html'; ?>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
$a = new HomePage();
|
||||
|
|
148
www/login.php
148
www/login.php
|
@ -1,7 +1,55 @@
|
|||
<?php
|
||||
require 'webdata/init.php';
|
||||
|
||||
$returnToPage = "./";
|
||||
require_once '../Router.php';
|
||||
|
||||
class LoginPage extends WebPage {
|
||||
public $pagekey = "login";
|
||||
public $title = "PaperBag - Plan & Execute Your Shopping";
|
||||
|
||||
function load(){
|
||||
$returnToPage = $_POST['referrerPage'] ?? $_SESSION['pre-auth'] ?? explode($_SERVER['HTTP_HOST'], $_SERVER['HTTP_REFERER'])[1];
|
||||
|
||||
// TODO: check if returntopage is an auth-changing page (e.g: login / register, and if so don't redirect there!
|
||||
|
||||
$this->msg[] = $returnToPage;
|
||||
|
||||
if(Auth::checkLogin(true)){
|
||||
header("Location: ".$returnToPage);
|
||||
}
|
||||
|
||||
if(isset($_GET['thank'])){
|
||||
$this->msg[] = "Thank you for registering. Please log in to continue!";
|
||||
}
|
||||
|
||||
if(isset($_POST) && !empty($_POST)){
|
||||
$data = [];
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
$stayLoggedIn = isset($_POST['stayLoggedIn']);
|
||||
$err = Auth::loginWithCredentials($data['loginEmail'], $data['loginPwd'], $stayLoggedIn);
|
||||
if($err === true){
|
||||
header("Location: ".$returnToPage);
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$a = new LoginPage();
|
||||
|
||||
|
||||
|
||||
|
||||
//require 'webdata/init.php';
|
||||
|
||||
/*$returnToPage = "./";
|
||||
if(isset($_GET['return'])){
|
||||
$returnToPage = $_GET['return'];
|
||||
}
|
||||
|
@ -10,103 +58,17 @@ elseif(isset($_POST['referrerPage'])){
|
|||
}
|
||||
elseif(isset($_SERVER['HTTP_REFERER'])){
|
||||
$returnToPage = explode($_SERVER['HTTP_HOST'], $_SERVER['HTTP_REFERER'])[1];
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
if(stristr($returnToPage, "login.php") || stristr($returnToPage, "register.php")){
|
||||
/*if(stristr($returnToPage, "login.php") || stristr($returnToPage, "register.php")){
|
||||
$returnToPage = "./";
|
||||
} else {
|
||||
$returnToPage = str_ireplace('index.php', '', $returnToPage);
|
||||
}
|
||||
}*/
|
||||
|
||||
if( checkLogin() ){
|
||||
/*if( checkLogin() ){
|
||||
header("Location: ".$returnToPage);
|
||||
}
|
||||
|
||||
$msg = [];
|
||||
if(isset($_GET['thank'])){
|
||||
$msg[] = "Thank you for registering. Please log in to continue!";
|
||||
}
|
||||
|
||||
if(isset($_POST) && !empty($_POST)){
|
||||
$db = database();
|
||||
$data = [];
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
$stayLoggedIn = isset($_POST['stayLoggedIn']);
|
||||
$err = loginUser($data['loginEmail'], $data['loginPwd'], $stayLoggedIn);
|
||||
if($err === true){
|
||||
header("Location: ".$returnToPage);
|
||||
die();
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
?><!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<?=getHtmlHeaders();?>
|
||||
<title>Login - PaperBag</title>
|
||||
</head>
|
||||
<body id='plan'>
|
||||
<div id="page-container">
|
||||
<div id="page-wrapper">
|
||||
<?php include 'webdata/navbar.php'; ?>
|
||||
|
||||
<div class='container-sm' style="max-width: 540px;">
|
||||
|
||||
<h1 class="headline text-center">Login</h1>
|
||||
|
||||
<?php if(!empty($err)){
|
||||
foreach($err as $e){
|
||||
echo "<div class='alert alert-danger' role='alert'>$e</div>";
|
||||
}
|
||||
}
|
||||
if(!empty($msg)){
|
||||
foreach($msg as $m){
|
||||
echo "<div class='alert alert-success' role='alert'>$m</div>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<form action="login.php" method="POST">
|
||||
<input type="hidden" name="referrerPage" value="<?=$returnToPage;?>">
|
||||
<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">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="loginPwd" class="col-sm-2 col-form-label">Password</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" class="form-control" name="loginPwd" id="loginPwd">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-check" style="margin: 3px 0;">
|
||||
<input class="form-check-input" type="checkbox" id="gridCheck">
|
||||
<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>
|
||||
</div>
|
||||
<div class="form-check" style="margin: 3px 0;">
|
||||
<input class="form-check-input" type="checkbox" id="rememberCheck" name="stayLoggedIn">
|
||||
<label class="form-check-label" for="rememberCheck">
|
||||
Remember me (for 30 days)
|
||||
</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary mb-3 w-100">Login</button>
|
||||
</form>
|
||||
<p>Do you not have an account? <a href="register.php">Register</a> </p>
|
||||
</div>
|
||||
</div>
|
||||
<?php include 'webdata/footer.html'; ?>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,88 +1,12 @@
|
|||
<?php $rPath = "../"; require $rPath.'webdata/init.php'; requireLogin(); ?><!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<?=getHtmlHeaders($rPath);?>
|
||||
<title>Plan - PaperBag - Plan Your Shopping</title>
|
||||
<?php
|
||||
require_once '../../Router.php';
|
||||
|
||||
<!-- jsDelivr :: Sortable :: Latest (https://www.jsdelivr.com/package/npm/sortablejs) -->
|
||||
<script src="/js/sortable/Sortable.min.js"></script> <!-- CDN: https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js -->
|
||||
<script src="/js/sortable/jquery-sortable.js"></script> <!-- CDN: https://cdn.jsdelivr.net/npm/jquery-sortablejs@latest/jquery-sortable.js -->
|
||||
</head>
|
||||
<body id='plan'>
|
||||
<div id="page-container">
|
||||
<div id="page-wrapper">
|
||||
<?php include $rPath.'webdata/navbar.php'; ?>
|
||||
<h1 class="headline text-center">PaperBag</h1>
|
||||
<div class="container" style="padding-top: 5px; padding-bottom: 15px; text-align: center;">
|
||||
class PlanPage extends WebPage implements RequireAuth {
|
||||
public $pagekey = "plan";
|
||||
public $title = "Plan - PaperBag - Plan & Execute Your Shopping";
|
||||
|
||||
<div id="spaceSelectWrapper" style="max-width: 900px; margin: auto;">
|
||||
<div class="input-group" style="width: 200px;">
|
||||
<label class="input-group-text" for="spaceSelect">Space:</label>
|
||||
<select class="form-select" id='spaceSelect' aria-label="Select space">
|
||||
<option>...</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
function load(){
|
||||
}
|
||||
}
|
||||
|
||||
<div class="card-columns" id="stores" style="text-align: center;">
|
||||
<hr>
|
||||
<h5>Please enable javascript for this page to work</h5>
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
<div id='totalPriceWrapper'>Space subtotal: <span id="totalPrice" class="priceWrapper price">00.00</span></div>
|
||||
<br>
|
||||
<button class="btn btn-primary mb-2" id="addStore">Add store</button><br>
|
||||
<button class="btn btn-secondary" id="refreshAll">Refresh all</button>
|
||||
|
||||
</div>
|
||||
|
||||
<script src='htmlElements.js'></script>
|
||||
<script src='plan.js'></script>
|
||||
<script src='recipe.js'></script>
|
||||
<script src='product.js'></script>
|
||||
<!-- <script src='draggingClass.js'></script>-->
|
||||
</div>
|
||||
<?php include $rPath.'webdata/footer.html'; ?>
|
||||
</div>
|
||||
|
||||
<div class="modal" tabindex="-1" id="addStoreModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Add store</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body align-content-center">
|
||||
<button class="btn btn-primary" id="addEmptyStore">Empty store</button>
|
||||
<hr>
|
||||
<div class="accordion mb-2" id="addStoreRecipe"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
let addStoreModal = new bootstrap.Modal( document.getElementById('addStoreModal') );
|
||||
|
||||
$("#addStore").on('click', ev=>{
|
||||
ev.stopPropagation();
|
||||
addStoreModal.show();
|
||||
});
|
||||
|
||||
$("#addStoreModal").on('show.bs.modal', ev=> {
|
||||
$("#addStoreRecipe").html('');
|
||||
|
||||
$.getJSON('/api/v1/recipe', {}, resp => {
|
||||
let recipes = new Recipe(resp);
|
||||
recipes.getAccordionHtml('#addStoreRecipe');
|
||||
});
|
||||
});
|
||||
|
||||
$("#addEmptyStore").on('click', ev => {
|
||||
stores.push(new Store());
|
||||
addStoreModal.hide();
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
$a = new PlanPage();
|
||||
|
|
|
@ -186,7 +186,7 @@ function logoutUser($everywhere = false){
|
|||
session_destroy();
|
||||
}
|
||||
|
||||
function PwdGen($pass, $returnHashed = false): string {
|
||||
/*function PwdGen($pass, $returnHashed = false): string {
|
||||
global $config;
|
||||
|
||||
$pepper = $config['general']['pepper'] ?? 'IAmBadAtSecurity';
|
||||
|
@ -196,7 +196,7 @@ function PwdGen($pass, $returnHashed = false): string {
|
|||
}
|
||||
|
||||
return password_hash($pwd_peppered, PASSWORD_ARGON2ID, ['threads' => 2]);
|
||||
}
|
||||
}* /
|
||||
|
||||
function GenKey($length = 21): string{
|
||||
$password = "";
|
||||
|
@ -215,7 +215,7 @@ function GenKey($length = 21): string{
|
|||
}
|
||||
|
||||
return $password;
|
||||
}
|
||||
}*/
|
||||
|
||||
function checkLogin(): bool {
|
||||
global $db, $_SESSION, $_COOKIE;
|
||||
|
|
Loading…
Reference in New Issue