Added first draft
This commit is contained in:
parent
cae31b68fd
commit
f58e976373
13 changed files with 495 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
juggl/config/config.txt
|
||||||
|
juggl/config/config.path
|
1
juggl/config/config.path.sample
Normal file
1
juggl/config/config.path.sample
Normal file
|
@ -0,0 +1 @@
|
||||||
|
relativepath=config/config.txt
|
4
juggl/config/config.txt.sample
Normal file
4
juggl/config/config.txt.sample
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
host=HOST
|
||||||
|
dbname=DBNAME
|
||||||
|
username=USERNAME
|
||||||
|
password=PASSWORD
|
38
juggl/services/apiBranch.inc.php
Normal file
38
juggl/services/apiBranch.inc.php
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
require_once(__DIR__."/authenticator.inc.php");
|
||||||
|
require_once(__DIR__."/responses.inc.php");
|
||||||
|
require_once(__DIR__."/requestTypes.inc.php");
|
||||||
|
require_once(__DIR__."/dbOperations.inc.php");
|
||||||
|
require_once(__DIR__."/paramCleaner.inc.php");
|
||||||
|
|
||||||
|
abstract class ApiBranch {
|
||||||
|
function get (ParamCleaner $params) {}
|
||||||
|
function post (ParamCleaner $params) {}
|
||||||
|
function authenticationMissing (ParamCleaner $params) {
|
||||||
|
respondStatus(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
function execute ($authenticationRequired = true) {
|
||||||
|
if ($authenticationRequired) {
|
||||||
|
$auth = new Authenticator();
|
||||||
|
if (!$auth->isAuthenticated($_SESSION, $_REQUEST)) {
|
||||||
|
$this->authenticationMissing($this->getParams());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$currentType = currentRequestType();
|
||||||
|
if($currentType === RequestType::GET) {
|
||||||
|
$this->get($this->getParams());
|
||||||
|
} else if ($currentType === RequestType::POST) {
|
||||||
|
$this->post($this->getParams());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getParams() {
|
||||||
|
$content = json_decode(file_get_contents('php://input'), true);
|
||||||
|
$contentArray = array("json" => $content);
|
||||||
|
return new ParamCleaner(array_merge($contentArray, $_REQUEST, $_SESSION, $_FILES));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
43
juggl/services/authenticator.inc.php
Normal file
43
juggl/services/authenticator.inc.php
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
require(__DIR__."/dbOperations.inc.php");
|
||||||
|
|
||||||
|
class Authenticator {
|
||||||
|
function isApiKeyAuthenticated($key) {
|
||||||
|
$db = new DbOperations();
|
||||||
|
$db->select("api_keys", array("quota_max", "client_key"));
|
||||||
|
$db->where("client_key", Comparison::EQUAL, $key);
|
||||||
|
|
||||||
|
$result = $db->execute();
|
||||||
|
|
||||||
|
if (count($result) == 1 && $result[0]['quota_max'] > 0)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isSessionAuthenticated($session) {
|
||||||
|
if (isset($session['stayloggedin'])) {
|
||||||
|
if($session["stayloggedin"]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($session['until'])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($session['until'] > time() || $session['until'] == 0)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAuthenticated($session, $request) {
|
||||||
|
if (isset($request['key'])) {
|
||||||
|
return $this->isApiKeyAuthenticated($request['key']);
|
||||||
|
} else {
|
||||||
|
return $this->isSessionAuthenticated($session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
33
juggl/services/basicEnum.inc.php
Normal file
33
juggl/services/basicEnum.inc.php
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
abstract class BasicEnum {
|
||||||
|
private static $constCacheArray = NULL;
|
||||||
|
|
||||||
|
private static function getConstants() {
|
||||||
|
if (self::$constCacheArray == NULL) {
|
||||||
|
self::$constCacheArray = [];
|
||||||
|
}
|
||||||
|
$calledClass = get_called_class();
|
||||||
|
if (!array_key_exists($calledClass, self::$constCacheArray)) {
|
||||||
|
$reflect = new ReflectionClass($calledClass);
|
||||||
|
self::$constCacheArray[$calledClass] = $reflect->getConstants();
|
||||||
|
}
|
||||||
|
return self::$constCacheArray[$calledClass];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function isValidName($name, $strict = false) {
|
||||||
|
$constants = self::getConstants();
|
||||||
|
|
||||||
|
if ($strict) {
|
||||||
|
return array_key_exists($name, $constants);
|
||||||
|
}
|
||||||
|
|
||||||
|
$keys = array_map('strtolower', array_keys($constants));
|
||||||
|
return in_array(strtolower($name), $keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function isValidValue($value, $strict = true) {
|
||||||
|
$values = array_values(self::getConstants());
|
||||||
|
return in_array($value, $values, $strict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
45
juggl/services/configReader.inc.php
Normal file
45
juggl/services/configReader.inc.php
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class ConfigReader {
|
||||||
|
private const VALUE_SEPARATOR = '=';
|
||||||
|
|
||||||
|
function readFile ($path) {
|
||||||
|
if (file_exists($path) == false)
|
||||||
|
return;
|
||||||
|
|
||||||
|
$this->configuration = array();
|
||||||
|
foreach (file($path) as $line) {
|
||||||
|
$valuePair = $this->convertLine(trim($line));
|
||||||
|
|
||||||
|
if ($valuePair === false)
|
||||||
|
continue;
|
||||||
|
$this->configuration[$valuePair['key']] = $valuePair['value'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSetting ($key) {
|
||||||
|
if (array_key_exists($key, $this->configuration) == false)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return $this->configuration[$key];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function convertLine($line) {
|
||||||
|
if ($line == "")
|
||||||
|
return False;
|
||||||
|
|
||||||
|
$splitted = explode (self::VALUE_SEPARATOR, $line, 2);
|
||||||
|
$key = $splitted[0];
|
||||||
|
|
||||||
|
$value = "";
|
||||||
|
for ($i = 1; $i < sizeof($splitted); $i++) {
|
||||||
|
if ($i > 1) {
|
||||||
|
$value .= "=";
|
||||||
|
}
|
||||||
|
$value .= $splitted[$i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['key' => $key, 'value' => $value];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
190
juggl/services/dbOperations.inc.php
Normal file
190
juggl/services/dbOperations.inc.php
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
<?php
|
||||||
|
require(__DIR__."/configReader.inc.php");
|
||||||
|
require(__DIR__."/basicEnum.inc.php");
|
||||||
|
|
||||||
|
class DbOperations {
|
||||||
|
function __construct () {
|
||||||
|
$this->resetQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetQuery() {
|
||||||
|
$this->query = "";
|
||||||
|
$this->data = array();
|
||||||
|
$this->table = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private function openConnection () {
|
||||||
|
$config = new ConfigReader();
|
||||||
|
$config->readFile(__DIR__."/../config/config.path");
|
||||||
|
$config->readFile(__DIR__."/../".$config->getSetting("relativepath"));
|
||||||
|
|
||||||
|
$host = $config->getSetting('host');
|
||||||
|
$dbname = $config->getSetting('dbname');
|
||||||
|
$dsn = "mysql:host=$host;dbname=$dbname";
|
||||||
|
|
||||||
|
$options = array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$this->pdo = new PDO($dsn, $config->getSetting('username'), $config->getSetting('password'), $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
function select (string $table, array $attributes = array()) {
|
||||||
|
$this->table = $table;
|
||||||
|
if (count($attributes) == 0)
|
||||||
|
$formattedAttributes = "*";
|
||||||
|
else {
|
||||||
|
for($i = 0; $i < count($attributes); $i++){
|
||||||
|
$a = $attributes[$i];
|
||||||
|
if (strpos($a, ".") === false) {
|
||||||
|
$attributes[$i] = "$this->table.$a";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$formattedAttributes = implode(', ', $attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->addToQuery("SELECT $formattedAttributes FROM $table");
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function orderBy (string $attribute, string $order = Order::ASC) {
|
||||||
|
$this->addToQuery("ORDER BY $attribute $order");
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function getLatestIdInTable (string $table, string $attribute = "id") {
|
||||||
|
$db = new DbOperations();
|
||||||
|
$db->select($table, array($attribute));
|
||||||
|
$db->orderBy($attribute, Order::DESC);
|
||||||
|
|
||||||
|
return $db->execute()[0][$attribute];
|
||||||
|
}
|
||||||
|
|
||||||
|
function insert (string $table, array $data) {
|
||||||
|
$this->table = $table;
|
||||||
|
|
||||||
|
$attributes = implode(", ", array_keys($data));
|
||||||
|
$valuesIds = array();
|
||||||
|
foreach($data as $attribute => $value) {
|
||||||
|
$valuesIds[] = $this->addData($value, $attribute);
|
||||||
|
}
|
||||||
|
$values = implode(" , ", $valuesIds);
|
||||||
|
|
||||||
|
$this->addToQuery("INSERT INTO $table ( $attributes ) VALUES ( $values )");
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
function update (string $table, array $data) {
|
||||||
|
$this->table = $table;
|
||||||
|
|
||||||
|
$sets = array();
|
||||||
|
foreach($data as $attribute => $value) {
|
||||||
|
$valueId = $this->addData($value, $attribute);
|
||||||
|
$sets[] = "$attribute = $valueId";
|
||||||
|
}
|
||||||
|
$setString = implode(", ", $sets);
|
||||||
|
|
||||||
|
$this->addToQuery("UPDATE $table SET $setString");
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
function delete (string $table) {
|
||||||
|
$this->table = $table;
|
||||||
|
|
||||||
|
$this->addToQuery("DELETE FROM $table");
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addToQuery (string $phrase) {
|
||||||
|
$delimeter = " ";
|
||||||
|
$this->query = implode($delimeter, array($this->query, $phrase));
|
||||||
|
}
|
||||||
|
|
||||||
|
function where (string $attribute, string $comparison, $value, string $connector = Combination::AND) {
|
||||||
|
if (Comparison::isValidValue($comparison) == false)
|
||||||
|
return;
|
||||||
|
|
||||||
|
$keyWord = "WHERE";
|
||||||
|
if (!(strpos($this->query, $keyWord) === false))
|
||||||
|
$keyWord = $connector;
|
||||||
|
|
||||||
|
$valueId = $this->addData($value, $attribute);
|
||||||
|
$this->addToQuery("$keyWord $attribute $comparison $valueId");
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
function innerJoin (string $table, string $externAttribute, string $internAttribute, string $internTable = "") {
|
||||||
|
if ($internTable === "") {
|
||||||
|
$internTable = $this->table;
|
||||||
|
}
|
||||||
|
|
||||||
|
$innerJoin = "INNER JOIN $table ON $table.$externAttribute = $internTable.$internAttribute";
|
||||||
|
|
||||||
|
$this->addToQuery($innerJoin);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addData($data, $attribute) {
|
||||||
|
$name = str_replace(".", "", $attribute);
|
||||||
|
|
||||||
|
$this->data[$name] = $data;
|
||||||
|
return ":".$name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function execute() {
|
||||||
|
try{
|
||||||
|
$this->openConnection();
|
||||||
|
|
||||||
|
$pdoQuery = $this->pdo->prepare($this->query);
|
||||||
|
$pdoQuery->execute($this->data);
|
||||||
|
|
||||||
|
$results = array();
|
||||||
|
while($row = $pdoQuery->fetch()) {
|
||||||
|
$results[] = $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->resetQuery();
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
// TODO: Hide errors from user and log them
|
||||||
|
print($e);
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sql(string $sqlStatement, array $data) {
|
||||||
|
$this->query = $sqlStatement;
|
||||||
|
|
||||||
|
foreach($data as $attribute => $value) {
|
||||||
|
$this->addData($value, $attribute);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Comparison extends BasicEnum {
|
||||||
|
const EQUAL = "=";
|
||||||
|
const GREATER_THAN = ">";
|
||||||
|
const GREATER_THAN_OR_EQUAL = ">=";
|
||||||
|
const LESS_THAN = "<";
|
||||||
|
const LESS_THAN_OR_EQUAL = "<=";
|
||||||
|
const UNEQUAL = "!=";
|
||||||
|
const LIKE = "LIKE";
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Combination extends BasicEnum {
|
||||||
|
const AND = "AND";
|
||||||
|
const OR = "OR";
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Order extends BasicEnum {
|
||||||
|
const ASC = "ASC";
|
||||||
|
const DESC = "DESC";
|
||||||
|
}
|
||||||
|
?>
|
37
juggl/services/jsonBuilder.inc.php
Normal file
37
juggl/services/jsonBuilder.inc.php
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
class JsonBuilder {
|
||||||
|
function __construct () {
|
||||||
|
$this->jsonData = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getJson () {
|
||||||
|
return json_encode($this->jsonData, JSON_FORCE_OBJECT);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getArray () {
|
||||||
|
return $this->jsonData;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addCategories (array $categories) {
|
||||||
|
$columns = array("id" => "",
|
||||||
|
"name" => "");
|
||||||
|
|
||||||
|
foreach ($categories as $category) {
|
||||||
|
$this->jsonData['categories'] = array();
|
||||||
|
$this->jsonData['categories'][] = $this->createJsonArray($category, $columns);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createJsonArray (array $data, array $columns) {
|
||||||
|
$jsonArray = array();
|
||||||
|
foreach ($columns as $key => $column) {
|
||||||
|
if ($column === "") {
|
||||||
|
$column = $key;
|
||||||
|
}
|
||||||
|
$jsonArray[$key] = $data[$column];
|
||||||
|
}
|
||||||
|
return $jsonArray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
41
juggl/services/paramCleaner.inc.php
Normal file
41
juggl/services/paramCleaner.inc.php
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?php
|
||||||
|
class ParamCleaner {
|
||||||
|
function __construct (array $params) {
|
||||||
|
$this->sourceParams = $params;
|
||||||
|
$this->selectedParams = $params;
|
||||||
|
$this->errorCount = 0;
|
||||||
|
$this->errorMessage = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function select (string $prop = "") {
|
||||||
|
if ($prop == "") {
|
||||||
|
$this->selectedParams = $this->sourceParams;
|
||||||
|
} else {
|
||||||
|
$this->selectedParams = $this->selectedParams[$prop];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function get (string $prop) {
|
||||||
|
if(isset($this->selectedParams[$prop])) {
|
||||||
|
return $this->selectedParams[$prop];
|
||||||
|
} else {
|
||||||
|
$this->errorCount += 1;
|
||||||
|
$this->errorMessage .= "Property \"{$prop}\" missing. ";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasErrorOccurred () {
|
||||||
|
return $this->errorCount > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getErrorMessage () {
|
||||||
|
return $this->errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetErrors () {
|
||||||
|
$this->errorMessage = "";
|
||||||
|
$this->errorCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
19
juggl/services/requestTypes.inc.php
Normal file
19
juggl/services/requestTypes.inc.php
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
require_once(__DIR__.'/basicEnum.inc.php');
|
||||||
|
|
||||||
|
abstract class RequestType extends BasicEnum {
|
||||||
|
const GET = "GET";
|
||||||
|
const POST = "POST";
|
||||||
|
const PUT = "PUT";
|
||||||
|
const DELETE = "DELETE";
|
||||||
|
}
|
||||||
|
|
||||||
|
function currentRequestType () {
|
||||||
|
$requestType = $_SERVER['REQUEST_METHOD'];
|
||||||
|
if (RequestType::isValidValue($requestType)) {
|
||||||
|
return $requestType;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
25
juggl/services/responses.inc.php
Normal file
25
juggl/services/responses.inc.php
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
require_once(__DIR__."/jsonBuilder.inc.php");
|
||||||
|
|
||||||
|
function respondJson(JsonBuilder $builder) {
|
||||||
|
header('Content-type: application/json');
|
||||||
|
echo($builder->getJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
function respondHtml(string $html) {
|
||||||
|
print($html);
|
||||||
|
}
|
||||||
|
|
||||||
|
function respondStatus(int $statusCode, string $message = "") {
|
||||||
|
http_response_code($statusCode);
|
||||||
|
die($message);
|
||||||
|
}
|
||||||
|
|
||||||
|
function redirectBack() {
|
||||||
|
header("Location: {$_SERVER['HTTP_REFERER']}");
|
||||||
|
}
|
||||||
|
|
||||||
|
function redirectTo($url) {
|
||||||
|
header("Location: {$url}");
|
||||||
|
}
|
||||||
|
?>
|
17
juggl/timetracking.php
Normal file
17
juggl/timetracking.php
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
require_once(__DIR__."/services/apiBranch.inc.php");
|
||||||
|
require_once(__DIR__."/services/jsonBuilder.inc.php");
|
||||||
|
require_once(__DIR__."/services/responses.inc.php");
|
||||||
|
|
||||||
|
class TimeTrackingBranch extends ApiBranch {
|
||||||
|
function get (ParamCleaner $params) {
|
||||||
|
respondStatus(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
function post (ParamCleaner $params) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$branch = new TimeTrackingBranch();
|
||||||
|
$branch->execute();
|
||||||
|
?>
|
Loading…
Reference in a new issue