fontawesome und jeeede menge reworks
All checks were successful
Build and push / Pulling repo on server (push) Successful in 20s
All checks were successful
Build and push / Pulling repo on server (push) Successful in 20s
This commit is contained in:
parent
fc9de5b15c
commit
004e38b3bb
5
web/css/fontawesome.min.css
vendored
Normal file
5
web/css/fontawesome.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
194
web/inc/classes/Model.class.php
Normal file
194
web/inc/classes/Model.class.php
Normal file
@ -0,0 +1,194 @@
|
||||
<?php
|
||||
|
||||
class Model {
|
||||
public $data;
|
||||
public $id = false;
|
||||
public $redis;
|
||||
|
||||
function __construct()
|
||||
{
|
||||
//redis
|
||||
if ($GLOBALS['redis'])
|
||||
$this->redis = $GLOBALS['redis'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic getter function
|
||||
*
|
||||
* @param $name Variable name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
if (isset($this->data[$name]))
|
||||
return $this->data[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic setter function
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __set($name, $value)
|
||||
{
|
||||
$this->data[$name] = $value;
|
||||
}
|
||||
|
||||
public function getDBFields()
|
||||
{
|
||||
return $this->dbFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all data fields but removes filtered datapoints like passwords, etc
|
||||
*/
|
||||
public function getDataFiltered()
|
||||
{
|
||||
$data = $this->data;
|
||||
if(is_array($this->hidden))
|
||||
foreach($this->hidden as $secretfield)
|
||||
unset($data[$secretfield]);
|
||||
foreach($this->dbFields as $field=>$options)
|
||||
if($options['type']=='csv')
|
||||
$data[$field] = ($data[$field]?explode(';',$data[$field]):[]);
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
if (!$this->validate())
|
||||
return false;
|
||||
foreach($this->dbFields as $field=>$options)
|
||||
{
|
||||
if(isset($this->data[$field]))
|
||||
$this->redis->hset($this->dbTable.':'.$this->id,$field,$this->data[$field]);
|
||||
}
|
||||
}
|
||||
|
||||
public function load($id)
|
||||
{
|
||||
$this->id = $id;
|
||||
if(!$this->redis->exists($this->dbTable.':'.$this->id))
|
||||
return false;
|
||||
$keys = array_keys($this->dbFields);
|
||||
|
||||
foreach($keys as $key)
|
||||
{
|
||||
$value = $this->redis->hget($this->dbTable.':'.$this->id,$key);
|
||||
|
||||
if($value!==NULL) //we'll leave null values
|
||||
switch($this->dbFields[$key]['type'])
|
||||
{
|
||||
case 'int': $value = intval($value);break;
|
||||
case 'bool': $value = boolval($value);break;
|
||||
case 'float': $value = floatval($value);break;
|
||||
case 'double': $value = doubleval($value);break;
|
||||
}
|
||||
$this->data[$key] = $value;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*/
|
||||
private function validate()
|
||||
{
|
||||
if (!$this->dbFields)
|
||||
return true;
|
||||
|
||||
$data = $this->data;
|
||||
|
||||
foreach ($this->dbFields as $key => $options) {
|
||||
$type = null;
|
||||
$required = false;
|
||||
|
||||
if(in_array('autoupdate',$options))
|
||||
$this->data[$key] = NULL;
|
||||
|
||||
if (isset($data[$key]))
|
||||
$value = $data[$key];
|
||||
else
|
||||
$value = null;
|
||||
|
||||
if (is_array($value))
|
||||
continue;
|
||||
|
||||
if (isset($desc[0]))
|
||||
$type = $desc[0];
|
||||
if (in_array('required',$options))
|
||||
$required = true;
|
||||
|
||||
if($value===null && $options['autoValMethod'])
|
||||
{
|
||||
if(method_exists($this,$options['autoValMethod']))
|
||||
$value = $this->{$options['autoValMethod']}();
|
||||
else if(function_exists($options['autoValMethod']))
|
||||
$value = $options['autoValMethod']();
|
||||
else
|
||||
$value = null;
|
||||
$this->data[$key] = $value;
|
||||
}
|
||||
|
||||
if($options['default']!==null && $value===null)
|
||||
$value = $options['default'];
|
||||
|
||||
if ($required && strlen($value) == 0) {
|
||||
throw new Exception($this->dbTable . "." . $key . " is required");
|
||||
}
|
||||
if ($value == null)
|
||||
continue;
|
||||
|
||||
switch ($type) {
|
||||
case 'email':
|
||||
$regexp = null;
|
||||
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
|
||||
throw new Exception("$type validation failed");
|
||||
}
|
||||
break;
|
||||
case 'csv':
|
||||
case "text":
|
||||
$regexp = null;
|
||||
break;
|
||||
case "int":
|
||||
$regexp = "/^[0-9]*$/";
|
||||
break;
|
||||
case "double":
|
||||
$regexp = "/^[0-9\.]*$/";
|
||||
break;
|
||||
case "bool":
|
||||
$regexp = '/^(yes|no|0|1|true|false)$/i';
|
||||
break;
|
||||
case "datetime":
|
||||
$regexp = "/^[0-9a-zA-Z -:]*$/";
|
||||
break;
|
||||
default:
|
||||
$regexp = $type;
|
||||
break;
|
||||
}
|
||||
if (!$regexp)
|
||||
continue;
|
||||
|
||||
if (!preg_match($regexp, $value)) {
|
||||
throw new Exception("$type validation failed");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function delete()
|
||||
{
|
||||
return $this->redis->del($this->dbTable.':'.$this->id);
|
||||
}
|
||||
|
||||
function gen_shorthash(){
|
||||
return substr(uniqid(),-7);
|
||||
}
|
||||
|
||||
function getDateTime($time=false)
|
||||
{
|
||||
return date('Y-m-d H:i:s',($time?$time:time()));
|
||||
}
|
||||
}
|
@ -48,7 +48,8 @@ class Page
|
||||
|
||||
function redirect($url)
|
||||
{
|
||||
header("Location: $url");
|
||||
//header("Location: $url");
|
||||
header("HX-Redirect: ". $url);
|
||||
exit();
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,8 @@ function autoload($className)
|
||||
//one of the global classes?
|
||||
if (file_exists(ROOT . DS . 'inc'. DS. 'classes' . DS . $className . '.class.php'))
|
||||
require_once(ROOT . DS . 'inc'. DS. 'classes' . DS . $className . '.class.php');
|
||||
else if (file_exists(ROOT . DS . 'models' . DS . $className . '.model.php'))
|
||||
require_once(ROOT . DS . 'models' . DS . $className . '.model.php');
|
||||
else if (file_exists(ROOT . DS . 'pages' . DS . strtolower($className) . DS . 'controller.php'))
|
||||
require_once(ROOT . DS . 'pages' . DS . strtolower($className) . DS . 'controller.php');
|
||||
}
|
||||
@ -25,7 +27,22 @@ function includeManagement()
|
||||
|
||||
//DB
|
||||
if(defined('REDIS_SERVER') && REDIS_SERVER !='')
|
||||
$GLOBALS['redis'] = new Redis();
|
||||
{
|
||||
$redis = new Redis();
|
||||
try{
|
||||
$redis->pconnect(REDIS_SERVER, REDIS_PORT);
|
||||
if (defined('REDIS_PASS') && REDIS_PASS)
|
||||
$redis->auth(REDIS_PASS);
|
||||
if (defined('REDIS_PREFIX') && REDIS_PREFIX)
|
||||
$redis->setOption(Redis::OPT_PREFIX, REDIS_PREFIX);
|
||||
if (defined('REDIS_DB') && REDIS_DB)
|
||||
$redis->select(REDIS_DB);
|
||||
$GLOBALS['redis'] = $redis;
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$GLOBALS['redis'] = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
$GLOBALS['redis'] = false;
|
||||
}
|
||||
@ -78,4 +95,40 @@ function callHook($url)
|
||||
return $response;
|
||||
else
|
||||
return $dispatch->renderPagecontent();
|
||||
}
|
||||
|
||||
function getMenu()
|
||||
{
|
||||
//first let's find all possible menu items
|
||||
$arr = array();
|
||||
if ($handle = opendir(ROOT . DS . 'pages')) {
|
||||
while (false !== ($file = readdir($handle))) {
|
||||
if (file_exists(ROOT . DS . 'pages' . DS . $file.DS.'controller.php') && class_exists($file)) {
|
||||
|
||||
$instance = new $file($file, 'index', false);
|
||||
$instance->setMenu();
|
||||
|
||||
if($instance->maySeeThisPage()===true)
|
||||
{
|
||||
$menu_text = $instance->menu_text;
|
||||
$menu_priority = $instance->menu_priority;
|
||||
|
||||
if($menu_text)
|
||||
{
|
||||
while($arr[$menu_priority])
|
||||
$menu_priority++;
|
||||
$arr[$menu_priority] = array('text'=>$menu_text,'image'=>$instance->menu_image, 'url'=>$file,'menu_class'=>$instance->menu_class);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir($handle);
|
||||
}
|
||||
|
||||
//sort the menu
|
||||
ksort($arr);
|
||||
|
||||
$arr = array_values($arr);
|
||||
|
||||
return $arr;
|
||||
}
|
@ -3,5 +3,6 @@
|
||||
define('REDIS_SERVER', 'ingress');
|
||||
define('REDIS_PORT', 6379);
|
||||
define('REDIS_DB', 1);
|
||||
define('REDIS_PREFIX', 'dogstats:');
|
||||
|
||||
define('DEV',true);
|
@ -195,4 +195,18 @@ function ulid_to_timestamp($ulid)
|
||||
function escape($str)
|
||||
{
|
||||
return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
function uuid4($data = null) {
|
||||
// Generate 16 bytes (128 bits) of random data or use the data passed into the function.
|
||||
$data = $data ?? random_bytes(16);
|
||||
assert(strlen($data) == 16);
|
||||
|
||||
// Set version to 0100
|
||||
$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
|
||||
// Set bits 6-7 to 10
|
||||
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
|
||||
|
||||
// Output the 36 character UUID.
|
||||
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
|
||||
}
|
@ -1,11 +1,8 @@
|
||||
<?php
|
||||
|
||||
session_start();
|
||||
define('DS', DIRECTORY_SEPARATOR);
|
||||
define('ROOT', dirname(__FILE__));
|
||||
|
||||
if($_SERVER['HTTP_HX_REQUEST']!='true')
|
||||
exit(file_get_contents(ROOT.DS.'templates'.DS.'mainpage.html'));
|
||||
|
||||
// if(file_exists(ROOT.DS.'inc'.DS.'config.inc.php'))
|
||||
// include_once(ROOT.DS.'inc'.DS.'config.inc.php');
|
||||
// else die('ERR: no config file found :(');
|
||||
@ -13,6 +10,12 @@ if($_SERVER['HTTP_HX_REQUEST']!='true')
|
||||
include_once(ROOT.DS.'inc'.DS.'core.php');
|
||||
includeManagement();
|
||||
|
||||
if($_SERVER['HTTP_HX_REQUEST']!='true')
|
||||
{
|
||||
include(ROOT.DS.'templates'.DS.'mainpage.html');
|
||||
die();
|
||||
}
|
||||
|
||||
if($_GET['url'])
|
||||
$url = explode('/',ltrim(parse_url($_GET['url'], PHP_URL_PATH),'/'));
|
||||
else $url = array_filter(explode('/',ltrim(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH),'/')));
|
||||
|
57
web/models/User.model.php
Normal file
57
web/models/User.model.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
class User extends Model {
|
||||
protected $dbTable = "users";
|
||||
protected $dbFields = Array (
|
||||
'uuid' => ['type'=>'text','required','unique','autoValMethod'=>'gen_ulid'],
|
||||
'password' => ['type'=>'text'],
|
||||
'registered' => ['type'=>'datetime','required','unique','autoValMethod'=>'getDateTime'],
|
||||
'email' => ['type'=>'email','unique'],
|
||||
'firstname' => ['type'=>'text'],
|
||||
'lastname' => ['type'=>'text'],
|
||||
'last_login' => ['type'=>'datetime','required','unique','autoValMethod'=>'getDateTime'],
|
||||
'token' => ['type'=>'text','required','unique','autoValMethod'=>'uuid4'],
|
||||
'timezone' => ['type'=>'int'],
|
||||
'active' => ['type'=>'int','default'=>0]
|
||||
);
|
||||
protected $hidden = ['password','token'];
|
||||
|
||||
function login()
|
||||
{
|
||||
if(!$this->id) return false;
|
||||
$this->last_login = $this->getDateTime();
|
||||
$this->save();
|
||||
|
||||
$_SESSION['user'] = $this;
|
||||
$_SESSION['userid'] = $this->id;
|
||||
}
|
||||
|
||||
function logout()
|
||||
{
|
||||
if(!$this->id) return false;
|
||||
unset($_SESSION['user']);
|
||||
}
|
||||
|
||||
function exists($id)
|
||||
{
|
||||
return $this->redis->exists($this->dbTable.':'.$id);
|
||||
}
|
||||
|
||||
function getAll($filtered = true)
|
||||
{
|
||||
$keys = $this->redis->keys($this->dbTable.':*');
|
||||
$users = [];
|
||||
foreach($keys as $key)
|
||||
{
|
||||
$id = end(explode(':',$key));
|
||||
$u = new User();
|
||||
$u->load($id);
|
||||
if($filtered===true)
|
||||
$users[] = $u->getDataFiltered();
|
||||
else
|
||||
$users[] = $u->data;
|
||||
}
|
||||
return $users;
|
||||
}
|
||||
|
||||
}
|
74
web/pages/admin/controller.php
Normal file
74
web/pages/admin/controller.php
Normal file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
class Admin extends Page {
|
||||
|
||||
function loginas()
|
||||
{
|
||||
$user = $_REQUEST['email'];
|
||||
|
||||
$u = new User();
|
||||
if($u->load($user))
|
||||
{
|
||||
$u->login();
|
||||
$this->redirect('/');
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->set('message', 'User '.escape($user).' not found');
|
||||
$this->set('template', 'notfound.html');
|
||||
}
|
||||
}
|
||||
|
||||
function edituser()
|
||||
{
|
||||
$user = $_REQUEST['email'];
|
||||
|
||||
$u = new User();
|
||||
if($u->load($user))
|
||||
{
|
||||
$data = $u->data;
|
||||
$this->set('userdata', $data);
|
||||
$this->set('userid', $user);
|
||||
$this->set('template', 'edituser.html');
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->set('message', 'User '.escape($user).' not found');
|
||||
$this->set('template', 'notfound.html');
|
||||
}
|
||||
}
|
||||
|
||||
function edituserdata()
|
||||
{
|
||||
$user = $_REQUEST['email'];
|
||||
|
||||
$u = new User();
|
||||
if(!$u->load($user))
|
||||
{
|
||||
$this->set('message', 'User '.escape($user).' not found');
|
||||
$this->set('template', '/templates/notfound.html');
|
||||
return;
|
||||
}
|
||||
|
||||
foreach($_REQUEST as $key => $value)
|
||||
{
|
||||
if($key == 'email') continue;
|
||||
$u->$key = $value;
|
||||
}
|
||||
|
||||
try{
|
||||
$u->save();
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
$this->set('message', $e->getMessage());
|
||||
$this->set('template', '/templates/error.html');
|
||||
return;
|
||||
|
||||
}
|
||||
$this->set('message', 'Speichern erfolgreich');
|
||||
$this->set('template', '/templates/success.html');
|
||||
}
|
||||
|
||||
function maySeeThisPage(){return true;}
|
||||
}
|
37
web/pages/admin/edituser.html
Normal file
37
web/pages/admin/edituser.html
Normal file
@ -0,0 +1,37 @@
|
||||
<form hx-post="/admin/edituserdata" hx-target="#response">
|
||||
<div id="defaultModal" tabindex="-1" aria-hidden="true" class="z-50 w-full p-4 overflow-x-hidden overflow-y-auto md:inset-0 h-[calc(100%-1rem)] max-h-full">
|
||||
<div class="relative w-full max-w-2xl max-h-full">
|
||||
<!-- Modal content -->
|
||||
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700">
|
||||
<!-- Modal header -->
|
||||
<div class="flex items-start justify-between p-4 border-b rounded-t dark:border-gray-600">
|
||||
<h3 class="text-xl font-semibold text-gray-900 dark:text-white">
|
||||
Edit user <?= $userid ?>
|
||||
</h3>
|
||||
</div>
|
||||
<!-- Modal body -->
|
||||
<div class="p-6 space-y-6">
|
||||
<div class="flex flex-col">
|
||||
<label for="id" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">
|
||||
ID
|
||||
</label>
|
||||
<input type="text" name="id" disabled id="id" value="<?= $userid ?>" class="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
|
||||
</div>
|
||||
<?php foreach (array_keys($userdata) as $key) : ?>
|
||||
<div class="flex flex-col">
|
||||
<label for="<?= $key ?>" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">
|
||||
<?= $key ?>
|
||||
</label>
|
||||
<input type="text" name="<?= $key ?>" id="<?= $key ?>" value="<?= $userdata[$key] ?>" class="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<!-- Modal footer -->
|
||||
<div class="flex items-center p-6 space-x-2 border-t border-gray-200 rounded-b dark:border-gray-600">
|
||||
<button type="submit" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">Save</button>
|
||||
<div id="response"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
@ -4,15 +4,17 @@ class Home extends Page
|
||||
{
|
||||
function setMenu()
|
||||
{
|
||||
$this->menu_text = 'home';
|
||||
$this->menu_image = 'mdi-action-home';
|
||||
$this->menu_text = 'Home';
|
||||
$this->menu_image = 'far fa-home';
|
||||
$this->menu_priority = 0;
|
||||
}
|
||||
|
||||
function index()
|
||||
{
|
||||
$u = new User();
|
||||
$this->set('userdata', $u->getAll());
|
||||
$this->set('template', "home.html");
|
||||
return $this->renderPagecontent();
|
||||
//return $this->renderPagecontent();
|
||||
}
|
||||
|
||||
function maySeeThisPage(){return true;}
|
||||
|
@ -1,9 +1,6 @@
|
||||
<div class="hidden sm:mb-8 sm:flex sm:justify-center">
|
||||
<div
|
||||
class="relative rounded-full px-3 py-1 text-sm leading-6 text-gray-600 ring-1 ring-gray-900/10 hover:ring-gray-900/20">
|
||||
Announcing our next round of funding. <a href="#" class="font-semibold text-indigo-600"><span
|
||||
class="absolute inset-0" aria-hidden="true"></span>Read more <span
|
||||
aria-hidden="true">→</span></a>
|
||||
<div class="relative rounded-full px-3 py-1 text-sm leading-6 text-gray-600 ring-1 ring-gray-900/10 hover:ring-gray-900/20">
|
||||
Currently logged in as: <?= $_SESSION['userid']?:'nobody' ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
@ -11,14 +8,14 @@
|
||||
<p class="mt-6 text-lg leading-8 text-gray-600">Anim aute id magna aliqua ad ad non deserunt sunt.
|
||||
Qui irure qui lorem cupidatat commodo. Elit sunt amet fugiat veniam occaecat fugiat aliqua.</p>
|
||||
<div class="mt-10 flex items-center justify-center gap-x-6">
|
||||
<a href="#"
|
||||
class="rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">Get
|
||||
<a href="#" class="rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">Get
|
||||
started</a>
|
||||
<a href="#" class="text-sm font-semibold leading-6 text-gray-900">Learn more <span
|
||||
aria-hidden="true">→</span></a>
|
||||
<a href="#" class="text-sm font-semibold leading-6 text-gray-900">Learn more <span aria-hidden="true">→</span></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 class="text-2xl p-6 text-center">Admin stuff</h1>
|
||||
|
||||
<div class="container flex justify-center mx-auto">
|
||||
<div class="flex flex-col">
|
||||
<div class="w-full">
|
||||
@ -26,54 +23,37 @@
|
||||
<table class="divide-y divide-gray-300 ">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<?php foreach (array_keys($userdata[0]) as $key) : ?>
|
||||
<th class="px-6 py-2 text-xs text-gray-500">
|
||||
ID
|
||||
<?= $key ?>
|
||||
</th>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<th class="px-6 py-2 text-xs text-gray-500">
|
||||
Name
|
||||
</th>
|
||||
<th class="px-6 py-2 text-xs text-gray-500">
|
||||
Email
|
||||
</th>
|
||||
<th class="px-6 py-2 text-xs text-gray-500">
|
||||
Created_at
|
||||
Login
|
||||
</th>
|
||||
<th class="px-6 py-2 text-xs text-gray-500">
|
||||
Edit
|
||||
</th>
|
||||
<th class="px-6 py-2 text-xs text-gray-500">
|
||||
Delete
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-300">
|
||||
<?php foreach ([
|
||||
['id' => 1, 'name' => 'Max', 'email' => 'test@test.ts'],
|
||||
['id' => 2, 'name' => 'Roxie', 'email' => 'loxie@test.ts'],
|
||||
] as $user) : ?>
|
||||
<?php foreach ($userdata as $user) : ?>
|
||||
|
||||
<tr class="whitespace-nowrap">
|
||||
<td class="px-6 py-4 text-sm text-gray-500">
|
||||
<?= $user['id'] ?>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<div class="text-sm text-gray-900">
|
||||
<?= $user['name'] ?>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<div class="text-sm text-gray-500"><?= $user['email'] ?></div>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-gray-500">
|
||||
2021-1-12
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<a href="#" class="px-4 py-1 text-sm text-indigo-600 bg-indigo-200 rounded-full">Edit</a>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<a href="#" class="px-4 py-1 text-sm text-red-400 bg-red-200 rounded-full">Delete</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="whitespace-nowrap">
|
||||
<?php foreach (array_keys($user) as $key) : ?>
|
||||
<td class="px-6 py-4 text-sm text-gray-500">
|
||||
<?= $user[$key] ?>
|
||||
</td>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<td class="px-6 py-4">
|
||||
<button name="email" value="<?= $user['email'] ?>" hx-post="/admin/loginas/" class="px-4 py-1 text-sm text-indigo-600 bg-indigo-200 rounded-full">Login as this user</button>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<button name="email" value="<?= $user['email'] ?>" hx-post="/admin/edituser/" hx-target="#main" class="px-4 py-1 text-sm text-red-400 bg-red-200 rounded-full">Edit</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
|
@ -2,6 +2,16 @@
|
||||
|
||||
class Login extends Page {
|
||||
|
||||
function setMenu()
|
||||
{
|
||||
if($_SESSION['user'])
|
||||
$this->menu_text = $_SESSION['userid'];
|
||||
else
|
||||
$this->menu_text = 'Login';
|
||||
$this->menu_image = 'far fa-user';
|
||||
$this->menu_priority = 99;
|
||||
}
|
||||
|
||||
function index()
|
||||
{
|
||||
$this->set('hello','world');
|
||||
|
@ -31,7 +31,7 @@
|
||||
Sign in
|
||||
</button>
|
||||
<p class="text-sm font-light text-gray-500 dark:text-gray-400">
|
||||
Don’t have an account yet? <a href="#" class="font-medium text-primary-600 hover:underline dark:text-primary-500">Sign up</a>
|
||||
Don’t have an account yet? <a href="#" hx-get="/register" hx-push-url="/register" hx-target="#main" class="font-medium text-primary-600 hover:underline dark:text-primary-500">Sign up</a>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
|
91
web/pages/register/controller.php
Normal file
91
web/pages/register/controller.php
Normal file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
class Register extends Page {
|
||||
|
||||
function index()
|
||||
{
|
||||
$this->set('template', 'register.html');
|
||||
}
|
||||
|
||||
function validate()
|
||||
{
|
||||
$email = trim($_REQUEST['email']);
|
||||
$password = trim($_REQUEST['password']);
|
||||
$password2 = trim($_REQUEST['password2']);
|
||||
|
||||
$hash = password_hash($password, PASSWORD_DEFAULT);
|
||||
|
||||
|
||||
$u = new User();
|
||||
|
||||
$err = false;
|
||||
if(!$email)
|
||||
$err = "Email is required";
|
||||
else if(!filter_var($email, FILTER_VALIDATE_EMAIL))
|
||||
$err = "Email is invalid";
|
||||
else if(!$password)
|
||||
$err = "Password is required";
|
||||
else if($password != $password2)
|
||||
$err = "Passwords do not match";
|
||||
else if(strlen($password) < 8)
|
||||
$err = "Password must be at least 8 characters long";
|
||||
else if($u->exists($email))
|
||||
$err = "Email already exists";
|
||||
|
||||
if($err)
|
||||
{
|
||||
$this->set('template', '/templates/error.html');
|
||||
$this->set('title', 'Error');
|
||||
$this->set('message', $err);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
$u->id = $email;
|
||||
$u->email = $email;
|
||||
$u->password = $hash;
|
||||
$u->active = 0;
|
||||
try
|
||||
{
|
||||
$u->save();
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
$this->set('template', '/templates/error.html');
|
||||
$this->set('title', 'Error');
|
||||
$this->set('message', $e->getMessage());
|
||||
return;
|
||||
}
|
||||
//$this->redirect('/register/success');
|
||||
return;
|
||||
}
|
||||
|
||||
return print_r(['email'=>$email,'password'=>$password,'password2'=>$password2], true);
|
||||
}
|
||||
|
||||
function success()
|
||||
{
|
||||
$this->set('template', '/templates/success.html');
|
||||
$this->set('title', 'Success');
|
||||
$this->set('message', 'You have successfully registered. Activate your account from the Link in your email');
|
||||
}
|
||||
|
||||
function test()
|
||||
{
|
||||
$u = new User();
|
||||
$u->id = 'chris@chris.ch';
|
||||
$u->email = 'chris@chris.ch';
|
||||
$u->password = '123456';
|
||||
$u->save();
|
||||
|
||||
return nl2br(print_r($u, true));
|
||||
}
|
||||
|
||||
function test2()
|
||||
{
|
||||
$u = new User();
|
||||
$response = $u->load('chris@chasdris.ch');
|
||||
|
||||
return nl2br(print_r($response, true));
|
||||
}
|
||||
}
|
30
web/pages/register/register.html
Normal file
30
web/pages/register/register.html
Normal file
@ -0,0 +1,30 @@
|
||||
<div class="flex flex-col items-center justify-center px-6 py-8 mx-auto lg:py-0">
|
||||
<div class="w-full bg-white rounded-lg shadow dark:border md:mt-0 sm:max-w-md xl:p-0 dark:bg-gray-800 dark:border-gray-700">
|
||||
<div class="p-6 space-y-4 md:space-y-6 sm:p-8">
|
||||
<h1 class="text-xl font-bold leading-tight tracking-tight text-gray-900 md:text-2xl dark:text-white">
|
||||
Register
|
||||
</h1>
|
||||
<div id="response" class="text-gray-900 dark:text-white">
|
||||
<?= $hello ?>
|
||||
</div>
|
||||
<form class="space-y-4 md:space-y-6" hx-post="/register/validate" hx-target="#response">
|
||||
<div>
|
||||
<label for="email" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Your email</label>
|
||||
<input type="email" name="email" id="email" class="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="name@company.com" required="">
|
||||
</div>
|
||||
<div>
|
||||
<label for="password" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Password</label>
|
||||
<input type="password" name="password" id="password" placeholder="••••••••" class="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" required="">
|
||||
</div>
|
||||
<div>
|
||||
<label for="password2" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Repeat Password</label>
|
||||
<input type="password" name="password2" id="password2" placeholder="••••••••" class="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" required="">
|
||||
</div>
|
||||
|
||||
<button type="submit" class="bg-sky-500 hover:bg-sky-700 p-2 rounded">
|
||||
Account erstellen
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
4
web/templates/error.html
Normal file
4
web/templates/error.html
Normal file
@ -0,0 +1,4 @@
|
||||
<div class="bg-red-100 border-l-4 border-red-500 text-red-700 p-4" role="alert">
|
||||
<p class="font-bold"><?= $title ?></p>
|
||||
<p><?= $message ?></p>
|
||||
</div>
|
4
web/templates/info.html
Normal file
4
web/templates/info.html
Normal file
@ -0,0 +1,4 @@
|
||||
<div class="bg-blue-100 border-l-4 border-blue-500 text-blue-700 p-4" role="alert">
|
||||
<p class="font-bold"><?= $title ?></p>
|
||||
<p><?= $message ?></p>
|
||||
</div>
|
@ -6,40 +6,17 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Dogstats</title>
|
||||
<link href="/css/output.css" rel="stylesheet">
|
||||
<link href="/css/fontawesome.min.css" rel="stylesheet">
|
||||
<script src="/js/htmx.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="bg-white">
|
||||
|
||||
<header class="inset-x-0 top-0 z-50">
|
||||
<nav class="flex items-center justify-between p-6 lg:px-8" aria-label="Global">
|
||||
<div class="flex lg:flex-1">
|
||||
<a href="#" hx-get="/home" hx-push-url="/home" hx-target="#main" class="-m-1.5 p-1.5">
|
||||
<span class="sr-only">Dogstats</span>
|
||||
<img class="h-8 w-auto" src="/imgs/dogstats-50.png" alt="">
|
||||
</a>
|
||||
</div>
|
||||
<div class="flex lg:hidden">
|
||||
<button type="button" class="-m-2.5 inline-flex items-center justify-center rounded-md p-2.5 text-gray-700">
|
||||
<span class="sr-only">Open main menu</span>
|
||||
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="hidden lg:flex lg:gap-x-12">
|
||||
<a href="#" hx-get="/templates/product.html" hx-target="#main" class="text-sm font-semibold leading-6 text-gray-900">Product</a>
|
||||
<a href="#" hx-get="/features" hx-push-url="/features" hx-target="#main" class="text-sm font-semibold leading-6 text-gray-900">Features</a>
|
||||
</div>
|
||||
<div class="hidden lg:flex lg:flex-1 lg:justify-end">
|
||||
<a href="#" hx-get="/login" hx-target="#main" hx-push-url="/login" class="text-sm font-semibold leading-6 text-gray-900">Log in <span aria-hidden="true">→</span></a>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<div id="main" hx-get="/" hx-trigger="load">
|
||||
<?php include(ROOT."/templates/menu.html") ?>
|
||||
|
||||
<div id="main" hx-get="/" hx-trigger="load" hx-indicator="#spinner">
|
||||
<i id="spinner" class="fa-solid fa-spinner"></i>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
15
web/templates/menu.html
Normal file
15
web/templates/menu.html
Normal file
@ -0,0 +1,15 @@
|
||||
<header class="text-gray-600 body-font">
|
||||
<div class="container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center">
|
||||
<a class="flex title-font font-medium items-center text-gray-900 mb-4 md:mb-0">
|
||||
<img class="h-8 w-auto" src="/imgs/dogstats-50.png" alt="">
|
||||
<span class="ml-3 text-xl">Dogstats</span>
|
||||
</a>
|
||||
<nav class="md:ml-auto flex flex-wrap items-center text-base justify-center">
|
||||
<?php foreach(getMenu() as $item) : ?>
|
||||
<a href="/<?= $item['url'] ?>" hx-push-url="/<?= $item['url'] ?>" hx-get="/<?= $item['url'] ?>" hx-target="#main" class="mr-5 hover:text-gray-900"><i class="<?= $item['image'] ?>"></i> <?= $item['text'] ?></a>
|
||||
<?php endforeach; ?>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
</header>
|
||||
|
4
web/templates/notice.html
Normal file
4
web/templates/notice.html
Normal file
@ -0,0 +1,4 @@
|
||||
<div class="bg-orange-100 border-l-4 border-orange-500 text-orange-700 p-4" role="alert">
|
||||
<p class="font-bold"><?= $title ?></p>
|
||||
<p><?= $message ?></p>
|
||||
</div>
|
4
web/templates/success.html
Normal file
4
web/templates/success.html
Normal file
@ -0,0 +1,4 @@
|
||||
<div class="bg-green-100 border-l-4 border-green-500 text-green-700 p-4" role="alert">
|
||||
<p class="font-bold"><?= $title ?></p>
|
||||
<p><?= $message ?></p>
|
||||
</div>
|
BIN
web/webfonts/fa-brands-400.eot
Normal file
BIN
web/webfonts/fa-brands-400.eot
Normal file
Binary file not shown.
3717
web/webfonts/fa-brands-400.svg
Normal file
3717
web/webfonts/fa-brands-400.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 730 KiB |
0
web/webfonts/fa-brands-400.svg:Zone.Identifier
Normal file
0
web/webfonts/fa-brands-400.svg:Zone.Identifier
Normal file
BIN
web/webfonts/fa-brands-400.ttf
Normal file
BIN
web/webfonts/fa-brands-400.ttf
Normal file
Binary file not shown.
0
web/webfonts/fa-brands-400.ttf:Zone.Identifier
Normal file
0
web/webfonts/fa-brands-400.ttf:Zone.Identifier
Normal file
BIN
web/webfonts/fa-brands-400.woff
Normal file
BIN
web/webfonts/fa-brands-400.woff
Normal file
Binary file not shown.
BIN
web/webfonts/fa-brands-400.woff2
Normal file
BIN
web/webfonts/fa-brands-400.woff2
Normal file
Binary file not shown.
0
web/webfonts/fa-brands-400.woff2:Zone.Identifier
Normal file
0
web/webfonts/fa-brands-400.woff2:Zone.Identifier
Normal file
0
web/webfonts/fa-brands-400.woff:Zone.Identifier
Normal file
0
web/webfonts/fa-brands-400.woff:Zone.Identifier
Normal file
BIN
web/webfonts/fa-duotone-900.eot
Normal file
BIN
web/webfonts/fa-duotone-900.eot
Normal file
Binary file not shown.
0
web/webfonts/fa-duotone-900.eot:Zone.Identifier
Normal file
0
web/webfonts/fa-duotone-900.eot:Zone.Identifier
Normal file
15328
web/webfonts/fa-duotone-900.svg
Normal file
15328
web/webfonts/fa-duotone-900.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 2.5 MiB |
0
web/webfonts/fa-duotone-900.svg:Zone.Identifier
Normal file
0
web/webfonts/fa-duotone-900.svg:Zone.Identifier
Normal file
BIN
web/webfonts/fa-duotone-900.ttf
Normal file
BIN
web/webfonts/fa-duotone-900.ttf
Normal file
Binary file not shown.
0
web/webfonts/fa-duotone-900.ttf:Zone.Identifier
Normal file
0
web/webfonts/fa-duotone-900.ttf:Zone.Identifier
Normal file
BIN
web/webfonts/fa-duotone-900.woff
Normal file
BIN
web/webfonts/fa-duotone-900.woff
Normal file
Binary file not shown.
BIN
web/webfonts/fa-duotone-900.woff2
Normal file
BIN
web/webfonts/fa-duotone-900.woff2
Normal file
Binary file not shown.
0
web/webfonts/fa-duotone-900.woff2:Zone.Identifier
Normal file
0
web/webfonts/fa-duotone-900.woff2:Zone.Identifier
Normal file
0
web/webfonts/fa-duotone-900.woff:Zone.Identifier
Normal file
0
web/webfonts/fa-duotone-900.woff:Zone.Identifier
Normal file
BIN
web/webfonts/fa-light-300.eot
Normal file
BIN
web/webfonts/fa-light-300.eot
Normal file
Binary file not shown.
0
web/webfonts/fa-light-300.eot:Zone.Identifier
Normal file
0
web/webfonts/fa-light-300.eot:Zone.Identifier
Normal file
12423
web/webfonts/fa-light-300.svg
Normal file
12423
web/webfonts/fa-light-300.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 2.3 MiB |
0
web/webfonts/fa-light-300.svg:Zone.Identifier
Normal file
0
web/webfonts/fa-light-300.svg:Zone.Identifier
Normal file
BIN
web/webfonts/fa-light-300.ttf
Normal file
BIN
web/webfonts/fa-light-300.ttf
Normal file
Binary file not shown.
0
web/webfonts/fa-light-300.ttf:Zone.Identifier
Normal file
0
web/webfonts/fa-light-300.ttf:Zone.Identifier
Normal file
BIN
web/webfonts/fa-light-300.woff
Normal file
BIN
web/webfonts/fa-light-300.woff
Normal file
Binary file not shown.
BIN
web/webfonts/fa-light-300.woff2
Normal file
BIN
web/webfonts/fa-light-300.woff2
Normal file
Binary file not shown.
0
web/webfonts/fa-light-300.woff2:Zone.Identifier
Normal file
0
web/webfonts/fa-light-300.woff2:Zone.Identifier
Normal file
0
web/webfonts/fa-light-300.woff:Zone.Identifier
Normal file
0
web/webfonts/fa-light-300.woff:Zone.Identifier
Normal file
BIN
web/webfonts/fa-regular-400.eot
Normal file
BIN
web/webfonts/fa-regular-400.eot
Normal file
Binary file not shown.
0
web/webfonts/fa-regular-400.eot:Zone.Identifier
Normal file
0
web/webfonts/fa-regular-400.eot:Zone.Identifier
Normal file
11323
web/webfonts/fa-regular-400.svg
Normal file
11323
web/webfonts/fa-regular-400.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 2.1 MiB |
0
web/webfonts/fa-regular-400.svg:Zone.Identifier
Normal file
0
web/webfonts/fa-regular-400.svg:Zone.Identifier
Normal file
BIN
web/webfonts/fa-regular-400.ttf
Normal file
BIN
web/webfonts/fa-regular-400.ttf
Normal file
Binary file not shown.
0
web/webfonts/fa-regular-400.ttf:Zone.Identifier
Normal file
0
web/webfonts/fa-regular-400.ttf:Zone.Identifier
Normal file
BIN
web/webfonts/fa-regular-400.woff
Normal file
BIN
web/webfonts/fa-regular-400.woff
Normal file
Binary file not shown.
BIN
web/webfonts/fa-regular-400.woff2
Normal file
BIN
web/webfonts/fa-regular-400.woff2
Normal file
Binary file not shown.
0
web/webfonts/fa-regular-400.woff2:Zone.Identifier
Normal file
0
web/webfonts/fa-regular-400.woff2:Zone.Identifier
Normal file
0
web/webfonts/fa-regular-400.woff:Zone.Identifier
Normal file
0
web/webfonts/fa-regular-400.woff:Zone.Identifier
Normal file
BIN
web/webfonts/fa-solid-900.eot
Normal file
BIN
web/webfonts/fa-solid-900.eot
Normal file
Binary file not shown.
0
web/webfonts/fa-solid-900.eot:Zone.Identifier
Normal file
0
web/webfonts/fa-solid-900.eot:Zone.Identifier
Normal file
9653
web/webfonts/fa-solid-900.svg
Normal file
9653
web/webfonts/fa-solid-900.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 1.7 MiB |
0
web/webfonts/fa-solid-900.svg:Zone.Identifier
Normal file
0
web/webfonts/fa-solid-900.svg:Zone.Identifier
Normal file
BIN
web/webfonts/fa-solid-900.ttf
Normal file
BIN
web/webfonts/fa-solid-900.ttf
Normal file
Binary file not shown.
0
web/webfonts/fa-solid-900.ttf:Zone.Identifier
Normal file
0
web/webfonts/fa-solid-900.ttf:Zone.Identifier
Normal file
BIN
web/webfonts/fa-solid-900.woff
Normal file
BIN
web/webfonts/fa-solid-900.woff
Normal file
Binary file not shown.
BIN
web/webfonts/fa-solid-900.woff2
Normal file
BIN
web/webfonts/fa-solid-900.woff2
Normal file
Binary file not shown.
0
web/webfonts/fa-solid-900.woff2:Zone.Identifier
Normal file
0
web/webfonts/fa-solid-900.woff2:Zone.Identifier
Normal file
0
web/webfonts/fa-solid-900.woff:Zone.Identifier
Normal file
0
web/webfonts/fa-solid-900.woff:Zone.Identifier
Normal file
Reference in New Issue
Block a user