fontawesome und jeeede menge reworks
Build and push / Pulling repo on server (push) Successful in 20s
Build and push / Pulling repo on server (push) Successful in 20s
This commit is contained in:
1 parent
fc9de5b15c
commit
004e38b3bb
72 files changed
+53087
-83
No files matched your search
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
/*!
|
||||
* Font Awesome Pro 5.15.4 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license (Commercial License)
|
||||
*/
|
||||
.fa,.fab,.fad,.fal,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:.08em solid #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fab.fa-pull-left,.fal.fa-pull-left,.far.fa-pull-left,.fas.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fab.fa-pull-right,.fal.fa-pull-right,.far.fa-pull-right,.fas.fa-pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-webkit-transform:scaleY(-1);transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical,.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{-webkit-transform:scale(-1);transform:scale(-1)}:root .fa-flip-both,:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{-webkit-filter:none;filter:none}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-500px:before{content:"\f26e"}.fa-abacus:before{content:"\f640"}.fa-accessible-icon:before{content:"\f368"}.fa-accusoft:before{content:"\f369"}.fa-acorn:before{content:"\f6ae"}.fa-acquisitions-incorporated:before{content:"\f6af"}.fa-ad:before{content:"\f641"}.fa-address-book:before{content:"\f2b9"}.fa-address-card:before{content:"\f2bb"}.fa-adjust:before{content:"\f042"}.fa-adn:before{content:"\f170"}.fa-adversal:before{content:"\f36a"}.fa-affiliatetheme:before{content:"\f36b"}.fa-air-conditioner:before{content:"\f8f4"}.fa-air-freshener:before{content:"\f5d0"}.fa-airbnb:before{content:"\f834"}.fa-alarm-clock:before{content:"\f34e"}.fa-alarm-exclamation:before{content:"\f843"}.fa-alarm-plus:before{content:"\f844"}.fa-alarm-snooze:before{content:"\f845"}.fa-album:before{content:"\f89f"}.fa-album-collection:before{content:"\f8a0"}.fa-algolia:before{content:"\f36c"}.fa-alicorn:before{content:"\f6b0"}.fa-alien:before{content:"\f8f5"}.fa-alien-monster:before{content:"\f8f6"}.fa-align-center:before{content:"\f037"}.fa-align-justify:before{content:"\f039"}.fa-align-left:before{content:"\f036"}.fa-align-right:before{content:"\f038"}.fa-align-slash:before{content:"\f846"}.fa-alipay:before{content:"\f642"}.fa-allergies:before{content:"\f461"}.fa-amazon:before{content:"\f270"}.fa-amazon-pay:before{content:"\f42c"}.fa-ambulance:before{content:"\f0f9"}.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-amilia:before{content:"\f36d"}.fa-amp-guitar:before{content:"\f8a1"}.fa-analytics:before{content:"\f643"}.fa-anchor:before{content:"\f13d"}.fa-android:before{content:"\f17b"}.fa-angel:before{content:"\f779"}.fa-angellist:before{content:"\f209"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-down:before{content:"\f107"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angry:before{content:"\f556"}.fa-angrycreative:before{content:"\f36e"}.fa-angular:before{content:"\f420"}.fa-ankh:befLine truncated
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
+54
-1
@@ -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));
|
||||
}
|
||||
+7
-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),'/')));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;}
|
||||
}
|
||||
@@ -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;}
|
||||
|
||||
+26
-46
@@ -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>
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
Binary file not shown.
File renamed without changes.
File diff suppressed because it is too large.
Load diff
|
After Width: | Height: | Size: 730 KiB |
Whitespace-only changes.
Binary file not shown.
Whitespace-only changes.
Binary file not shown.
Binary file not shown.
Whitespace-only changes.
Whitespace-only changes.
Binary file not shown.
Whitespace-only changes.
File diff suppressed because it is too large.
Load diff
|
After Width: | Height: | Size: 2.5 MiB |
Whitespace-only changes.
Binary file not shown.
Whitespace-only changes.
Binary file not shown.
Binary file not shown.
Whitespace-only changes.
Whitespace-only changes.
Binary file not shown.
Whitespace-only changes.
File diff suppressed because it is too large.
Load diff
|
After Width: | Height: | Size: 2.3 MiB |
Whitespace-only changes.
Binary file not shown.
Whitespace-only changes.
Binary file not shown.
Binary file not shown.
Whitespace-only changes.
Whitespace-only changes.
Binary file not shown.
Whitespace-only changes.
File diff suppressed because it is too large.
Load diff
|
After Width: | Height: | Size: 2.1 MiB |
Whitespace-only changes.
Binary file not shown.
Whitespace-only changes.
Binary file not shown.
Binary file not shown.
Whitespace-only changes.
Whitespace-only changes.
Binary file not shown.
Whitespace-only changes.
File diff suppressed because it is too large.
Load diff
|
After Width: | Height: | Size: 1.7 MiB |
Whitespace-only changes.
Binary file not shown.
Whitespace-only changes.
Binary file not shown.
Binary file not shown.
Whitespace-only changes.
Whitespace-only changes.
Reference in new issue
Block a user