Browse Source

Initial drop

master
Bernhard Fröhlich 2 years ago
parent
commit
beefe3cfab

+ 1
- 1
LICENSE View File

@@ -1,6 +1,6 @@
BSD 2-Clause License

Copyright (c) 2017, Bernhard Fröhlich
Copyright (c) 2017, Bernhard Foehlich <decke@bluelife.at>
All rights reserved.

Redistribution and use in source and binary forms, with or without

+ 35
- 0
README.md View File

@@ -1,2 +1,37 @@
# nodeman
FunkFeuer Node Manager

## Setup

Nodeman is using composer for the PHP dependencies so they
need to be downloaded and installed first.

<code>
composer install
</code>

Then we also need to create the SQLite Database tables and
add some configuration.

<code>
sqlite share/nodeman.db
> .read share/schema.sql
> .q
</code>


For testing and development you can use the builtin php
webserver but beware it's not safe because anybody could
access your sqlite databse.

<code>
php -S localhost:80
</code>

For a production system please use nginx and the example
config in `share/nodeman.conf`.


Then you can access nodeman via `http://localhost/` and login
as user `admin` with password `admin`.


+ 18
- 0
composer.json View File

@@ -0,0 +1,18 @@
{
"name": "ffgraz/nodeman",
"description": "FunkFeuer Node Manager",
"homepage": "https://github.com/decke/nodeman",
"license": "BSD-2-Clause",
"require": {
"slim/slim": "^3.0",
"slim/flash": "^0.2.0",
"slim/php-view": "~2.0",
"php": ">=5.5.0"
},
"autoload": {
"psr-0": {
"FunkFeuer": "lib"
},
"files": ["lib/functions.php"]
}
}

+ 411
- 0
composer.lock View File

@@ -0,0 +1,411 @@
{
"_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#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"content-hash": "bf8b1548b18b7e6051fd4442624c8ea8",
"packages": [
{
"name": "container-interop/container-interop",
"version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/container-interop/container-interop.git",
"reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/container-interop/container-interop/zipball/79cbf1341c22ec75643d841642dd5d6acd83bdb8",
"reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8",
"shasum": ""
},
"require": {
"psr/container": "^1.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Interop\\Container\\": "src/Interop/Container/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Promoting the interoperability of container objects (DIC, SL, etc.)",
"homepage": "https://github.com/container-interop/container-interop",
"time": "2017-02-14T19:40:03+00:00"
},
{
"name": "nikic/fast-route",
"version": "v1.2.0",
"source": {
"type": "git",
"url": "https://github.com/nikic/FastRoute.git",
"reference": "b5f95749071c82a8e0f58586987627054400cdf6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/FastRoute/zipball/b5f95749071c82a8e0f58586987627054400cdf6",
"reference": "b5f95749071c82a8e0f58586987627054400cdf6",
"shasum": ""
},
"require": {
"php": ">=5.4.0"
},
"type": "library",
"autoload": {
"psr-4": {
"FastRoute\\": "src/"
},
"files": [
"src/functions.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Nikita Popov",
"email": "nikic@php.net"
}
],
"description": "Fast request router for PHP",
"keywords": [
"router",
"routing"
],
"time": "2017-01-19T11:35:12+00:00"
},
{
"name": "pimple/pimple",
"version": "v3.2.2",
"source": {
"type": "git",
"url": "https://github.com/silexphp/Pimple.git",
"reference": "4d45fb62d96418396ec58ba76e6f065bca16e10a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/silexphp/Pimple/zipball/4d45fb62d96418396ec58ba76e6f065bca16e10a",
"reference": "4d45fb62d96418396ec58ba76e6f065bca16e10a",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"psr/container": "^1.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^3.2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.2.x-dev"
}
},
"autoload": {
"psr-0": {
"Pimple": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Pimple, a simple Dependency Injection Container",
"homepage": "http://pimple.sensiolabs.org",
"keywords": [
"container",
"dependency injection"
],
"time": "2017-07-23T07:32:15+00:00"
},
{
"name": "psr/container",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/container.git",
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Container\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common Container Interface (PHP FIG PSR-11)",
"homepage": "https://github.com/php-fig/container",
"keywords": [
"PSR-11",
"container",
"container-interface",
"container-interop",
"psr"
],
"time": "2017-02-14T16:28:37+00:00"
},
{
"name": "psr/http-message",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for HTTP messages",
"homepage": "https://github.com/php-fig/http-message",
"keywords": [
"http",
"http-message",
"psr",
"psr-7",
"request",
"response"
],
"time": "2016-08-06T14:39:51+00:00"
},
{
"name": "slim/flash",
"version": "0.2.0",
"source": {
"type": "git",
"url": "https://github.com/slimphp/Slim-Flash.git",
"reference": "3c9a26b3163820acc48080336c504d0a3cac6f30"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/slimphp/Slim-Flash/zipball/3c9a26b3163820acc48080336c504d0a3cac6f30",
"reference": "3c9a26b3163820acc48080336c504d0a3cac6f30",
"shasum": ""
},
"require": {
"php": ">=5.5.0"
},
"require-dev": {
"phpunit/phpunit": "^4.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Slim\\Flash\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Josh Lockhart",
"email": "hello@joshlockhart.com",
"homepage": "http://joshlockhart.com"
}
],
"description": "Slim Framework Flash message service provider",
"homepage": "http://slimframework.com",
"keywords": [
"flash",
"framework",
"message",
"provider",
"slim"
],
"time": "2016-11-11T16:29:19+00:00"
},
{
"name": "slim/php-view",
"version": "2.2.0",
"source": {
"type": "git",
"url": "https://github.com/slimphp/PHP-View.git",
"reference": "122ed121a8d9cf91a94020814d2a3ee6c836754f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/slimphp/PHP-View/zipball/122ed121a8d9cf91a94020814d2a3ee6c836754f",
"reference": "122ed121a8d9cf91a94020814d2a3ee6c836754f",
"shasum": ""
},
"require": {
"psr/http-message": "^1.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8",
"slim/slim": "^3.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Slim\\Views\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Glenn Eggleton",
"email": "geggleto@gmail.com"
}
],
"description": "Render PHP view scripts into a PSR-7 Response object.",
"keywords": [
"framework",
"php",
"phtml",
"renderer",
"slim",
"template",
"view"
],
"time": "2016-10-11T07:43:08+00:00"
},
{
"name": "slim/slim",
"version": "3.8.1",
"source": {
"type": "git",
"url": "https://github.com/slimphp/Slim.git",
"reference": "5385302707530b2bccee1769613ad769859b826d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/slimphp/Slim/zipball/5385302707530b2bccee1769613ad769859b826d",
"reference": "5385302707530b2bccee1769613ad769859b826d",
"shasum": ""
},
"require": {
"container-interop/container-interop": "^1.2",
"nikic/fast-route": "^1.0",
"php": ">=5.5.0",
"pimple/pimple": "^3.0",
"psr/container": "^1.0",
"psr/http-message": "^1.0"
},
"provide": {
"psr/http-message-implementation": "1.0"
},
"require-dev": {
"phpunit/phpunit": "^4.0",
"squizlabs/php_codesniffer": "^2.5"
},
"type": "library",
"autoload": {
"psr-4": {
"Slim\\": "Slim"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Rob Allen",
"email": "rob@akrabat.com",
"homepage": "http://akrabat.com"
},
{
"name": "Josh Lockhart",
"email": "hello@joshlockhart.com",
"homepage": "https://joshlockhart.com"
},
{
"name": "Gabriel Manricks",
"email": "gmanricks@me.com",
"homepage": "http://gabrielmanricks.com"
},
{
"name": "Andrew Smith",
"email": "a.smith@silentworks.co.uk",
"homepage": "http://silentworks.co.uk"
}
],
"description": "Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs",
"homepage": "https://slimframework.com",
"keywords": [
"api",
"framework",
"micro",
"router"
],
"time": "2017-03-19T17:55:20+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=5.5.0"
},
"platform-dev": []
}

+ 7
- 0
css/bootstrap.min.css
File diff suppressed because it is too large
View File


+ 4
- 0
css/custom.css View File

@@ -0,0 +1,4 @@
body {
padding-top: 5rem;
}


+ 59
- 0
index.php View File

@@ -0,0 +1,59 @@
<?php

/**
* FunkFeuer Node Manager
*
* @author Bernhard Froehlich <decke@bluelife.at>
* @copyright 2017 Bernhard Froehlich
* @license BSD License (2 Clause)
*
* @link https://github.com/decke/nodeman
*/
namespace FunkFeuer\Nodeman;

require_once __DIR__.'/vendor/autoload.php';

$session = new Session();

$app = new \Slim\App();

/* init php-view */
$container = $app->getContainer();
$container['view'] = function($container) use ($session) {
$renderer = new \Slim\Views\PhpRenderer(__DIR__.'/templates/');
$renderer->addAttribute('session', $session);
$renderer->addAttribute('config', new \FunkFeuer\Nodeman\Config());
$renderer->addAttribute('flash', new \Slim\Flash\Messages());

return $renderer;
};

/* init flash messages */
$container['flash'] = function() {
return new \Slim\Flash\Messages();
};


/* landing page */
$app->get('/', function($request, $response) {
return $this->view->render($response, 'index.html');
});

/* Authentication - Login */
$app->post('/login', function($request, $response) use ($session) {
if (!$request->getParam('username') || !$request->getParam('password')) {
$this->flash->addMessage('error', 'Authentication failed');
}
elseif (!$session->login($request->getParam('username'), $request->getParam('password'))) {
$this->flash->addMessage('error', 'Authentication failed');
}

return $response->withStatus(302)->withHeader('Location', '/');
});

$app->get('/logout', function($request, $response, $args) use ($session) {
$session->logout();
return $response->withStatus(302)->withHeader('Location', '/');
});

$app->run();

+ 6
- 0
js/bootstrap.min.js
File diff suppressed because it is too large
View File


+ 4
- 0
js/jquery.slim.min.js
File diff suppressed because it is too large
View File


+ 5
- 0
js/popper.min.js
File diff suppressed because it is too large
View File


+ 84
- 0
lib/FunkFeuer/Nodeman/Config.php View File

@@ -0,0 +1,84 @@
<?php

namespace FunkFeuer\Nodeman;

/**
* Configuration class to store various static settings.
*
* @author Bernhard Froehlich <decke@bluelife.at>
* @copyright 2017 Bernhard Froehlich
* @license BSD License (2 Clause)
*
* @link https://github.com/decke/nodeman
*/
class Config
{
protected static $datasource = 'sqlite:share/nodeman.db';
protected static $handle = null;

public static function getDataSource()
{
return self::$datasource;
}

public static function getDbHandle()
{
if(self::$handle === null)
self::$handle = new \PDO(self::$datasource);

return self::$handle;
}

public static function exists($property)
{
$handle = self::getDbHandle();

$stmt = $handle->prepare("SELECT name, value FROM config WHERE name = ?");
if(!$stmt) {
return false;
}

if(!$stmt->execute(array(strtolower($property)))) {
return false;
}

$row = $stmt->fetch(\PDO::FETCH_ASSOC);
if(!is_array($row))
return false;

return true;
}

public static function get($property)
{
$handle = self::getDbHandle();

$stmt = $handle->prepare("SELECT name, value FROM config WHERE name = ?");
if(!$stmt) {
return false;
}

if(!$stmt->execute(array(strtolower($property)))) {
throw new \Exception('Could not find config property '.$property);
}

$row = $stmt->fetch(\PDO::FETCH_ASSOC);
if(!is_array($row)) {
throw new \Exception('Could not find config property '.$property);
}

return $row['value'];
}

public static function set($property, $value)
{
$handle = self::getDbHandle();

if(!self::exists($property))
return false;

$stmt = $handle->prepare("UPDATE config SET value = ? WHERE name = ?");

return $stmt->execute(array($value, strtolower($property)));
}
}

+ 99
- 0
lib/FunkFeuer/Nodeman/Session.php View File

@@ -0,0 +1,99 @@
<?php

namespace FunkFeuer\Nodeman;

/**
* Manage PHP sessions and authenticate users.
*
* @author Bernhard Froehlich <decke@bluelife.at>
* @copyright 2017 Bernhard Froehlich
* @license BSD License (2 Clause)
*
* @link https://github.com/decke/nodeman
*/
class Session
{
public function __construct()
{
self::initialize();

session_start();
}

public static function initialize()
{
// do not expose Cookie value to JavaScript (enforced by browser)
ini_set('session.cookie_httponly', 1);

if (Config::get('security.https_only') === true) {
// only send cookie over https
ini_set('session.cookie_secure', 1);
}

// prevent caching by sending no-cache header
session_cache_limiter('nocache');

// rename session
session_name('SESSIONID');
}

public static function getSessionId()
{
return session_id();
}

public static function login($username, $password)
{
$user = new User($username);

if(!$user->checkPassword($password)) {
return false;
}

/* login assumed to be successfull */
$_SESSION['authenticated'] = true;
$_SESSION['username'] = $username;
$_SESSION['loginip'] = $_SERVER['REMOTE_ADDR'];

return true;
}

public static function getUsername()
{
if (isset($_SESSION['username'])) {
return $_SESSION['username'];
}

return false;
}

public static function getUser()
{
if(self::isAuthenticated()) {
return new User(self::getUsername());
}

return false;
}

public static function isAuthenticated()
{
return isset($_SESSION['authenticated']);
}

public static function logout()
{
$_SESSION = array();

/* also destroy session cookie on client */
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params['path'], $params['domain'],
$params['secure'], $params['httponly']
);

session_destroy();

return true;
}
}

+ 104
- 0
lib/FunkFeuer/Nodeman/User.php View File

@@ -0,0 +1,104 @@
<?php

namespace FunkFeuer\Nodeman;

/**
* User class for registered users
*
* @author Bernhard Froehlich <decke@bluelife.at>
* @copyright 2017 Bernhard Froehlich
* @license BSD License (2 Clause)
*
* @link https://github.com/decke/nodeman
*/
class User
{
private $_handle;
private $_data = array(
'userid' => null,
'username' => null,
'password' => null,
'email' => null,
'phone' => null
);

public function __construct($username = null)
{
$this->_handle = Config::getDbHandle();

if($username !== null) {
$this->load($username);
}
}

public function __get($name)
{
if(isset($this->_data[$name])) {
return $this->_data[$name];
}

throw new Exception('Undefined property '.$name.' in class User');
}

public function __set($name, $value)
{
if($name == 'password') {
return $this->setPassword($value);
}

if(isset($this->_data[$name])) {
$this->_data[$name] = $value;
return true;
}

throw new Exception('Undefined property '.$name.' in class User');
}

public function setPassword($password)
{
$this->_data['password'] = password_hash($password, PASSWORD_DEFAULT, array('cost' => 11));
return true;
}

public function checkPassword($password)
{
return password_verify($password, $this->password);
}

public function load($username)
{
$stmt = $this->_handle->prepare("SELECT userid, username, password, email, phone FROM users WHERE username = ?");
if(!$stmt->execute(array($username)))
return false;

while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
$this->_data = $row;

return true;
}

return false;
}

public function save()
{
if(!$this->userid)
{
$stmt = $this->_handle->prepare("INSERT INTO users (username, password, email, phone) VALUES (?, ?, ?, ?)");

if($stmt->execute(array($this->username, $this->password, $this->email, $this->phone)))
{
$this->userid = $this->_handle->lastInsertId();
return true;
}
}
else
{
$stmt = $this->_handle->prepare("UPDATE users SET username = ?, password = ?, email = ?, phone = ? WHERE userid = ?");

return $stmt->execute(array($this->username, $this->password, $this->email, $this->phone, $this->userid));
}

return false;
}
}

+ 42
- 0
lib/functions.php View File

@@ -0,0 +1,42 @@
<?php
/**
* Various functions.
*
* @author Bernhard Froehlich <decke@bluelife.at>
* @copyright 2017 Bernhard Froehlich
* @license BSD License (2 Clause)
*
* @link https://github.com/decke/nodeman
*/

function checkDbSchema($handle)
{
}

function jsonResponse($code, $data = array())
{
$app = \Slim\Slim::getInstance();
$app->response->setStatus($code);
$app->response->headers->set('Content-Type', 'application/json');
$app->response->write(json_encode($data));

if ($code != 200) {
$app->stop();
}

return true;
}

function textResponse($code, $data = '')
{
$app = \Slim\Slim::getInstance();
$app->response->setStatus($code);
$app->response->headers->set('Content-Type', 'text/plain');
$app->response->write($data);

if ($code != 200) {
$app->stop();
}

return true;
}

+ 45
- 0
share/nodeman.conf View File

@@ -0,0 +1,45 @@
#
# Example nodeman config for Nginx
#

server {
listen 80 default_server;
server_name _;
root /usr/local/www/nodeman;
try_files $uri /index.php?$query_string;

gzip on;
gzip_types text/plain;

# block access to our data
location ^~ /share/ {
return 403;
}

# block access to our code
location ^~ /lib/ {
return 403;
}
# block access to our code
location ^~ /templates/ {
return 403;
}
# block access to our dependencies
location ^~ /vendor/ {
return 403;
}
# this will only pass index.php to the fastcgi process which is
# generally safer but assumes the whole site is run via Slim.
location /index.php {
fastcgi_pass unix:/var/run/php-fpm.sock;
fastcgi_index index.php;
fastcgi_connect_timeout 10s;
fastcgi_read_timeout 120s;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}


+ 41
- 0
share/schema.sql View File

@@ -0,0 +1,41 @@
--- FunkFeuer nodeman database
---
--- Create the initial database:
--- sqlite3 nodeman.db
--- > .read schema.sql
--- > .q

BEGIN TRANSACTION;

CREATE TABLE config (
name CHAR(50) NOT NULL,
value CHAR(50) NOT NULL
);

CREATE INDEX config_idx1 ON config (name);

CREATE TABLE users (
userid INTEGER PRIMARY KEY NOT NULL,
username CHAR(50) NOT NULL,
password CHAR(255) NOT NULL,
email CHAR(255) NOT NULL,
phone CHAR(50) NOT NULL
);

CREATE INDEX users_idx1 ON users (username);


---
--- INITIAL DATA
---

INSERT INTO "config" VALUES('schemaversion','0');
INSERT INTO "config" VALUES('security.https_only','false');
INSERT INTO "config" VALUES('title','FunkFeuer Graz');
INSERT INTO "config" VALUES('title.url','https://graz.funkfeuer.at/');

--- default account
--- username: admin, password: admin
INSERT INTO "users" VALUES(1,'admin','$2y$11$mHyBgtw2Iu0JuUpAvr.ChekNkRZMsLzmoH0/rJJQUYxEJjii.CFjS','noreply@example.com','');

COMMIT;

+ 7
- 0
templates/_footer.html View File

@@ -0,0 +1,7 @@
<!-- optional JavaScript for Bootstrap -->
<script src="/js/jquery.slim.min.js"></script>
<script src="/js/popper.min.js"></script>
<script src="/js/bootstrap.min.js"></script>
</body>
</html>

+ 45
- 0
templates/_header.html View File

@@ -0,0 +1,45 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

<title><?=$config->get('title')?></title>

<!-- Bootstrap CSS -->
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/custom.css">
</head>

<body>

<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
<a class="navbar-brand" href="<?=$config->get('title.url')?>"><?=$config->get('title')?></a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarDefault" aria-controls="navbarDefault" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>

<div class="collapse navbar-collapse" id="navbarDefault">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="/">Home <span class="sr-only">(current)</span></a>
</li>
</ul>

<?php if($session->isAuthenticated()): ?>
<ul class="navbar-nav float-right">
<li class="nav-item">
<a class="btn btn-outline-success" href="/logout">Logout</a>
</li>
</ul>
<?php else: ?>
<form class="form-inline my-2 my-lg-0" action="/login" method="post">
<input type="text" name="username" placeholder="Username" class="form-control mr-sm-2">
<input type="password" name="password" placeholder="Password" class="form-control mr-sm-2">
<button type="submit" value="Login" class="btn btn-success my-2 my-sm-0">Sign in</button>
</form>
<?php endif; ?>

</div>
</nav>


+ 20
- 0
templates/index.html View File

@@ -0,0 +1,20 @@
<?php include("_header.html"); ?>

<div class="container">

<?php if($flash->hasMessage('error')): ?>
<?php foreach($flash->getMessage('error') as $msg): ?>
<div class="alert alert-danger" role="alert"><strong>Sorry</strong> <?=$msg?></div>
<?php endforeach; ?>
<?php endif; ?>

<?php if($flash->hasMessage('success')): ?>
<?php foreach($flash->getMessage('success') as $msg): ?>
<div class="alert alert-success" role="alert"><strong>Success</strong> <?=$msg?></div>
<?php endforeach; ?>
<?php endif; ?>

<h1><?=$config->get('title')?></h1>
</div>

<?php include("_footer.html"); ?>

Loading…
Cancel
Save