Google reCaptcha
This commit is contained in:
parent
9442a120ab
commit
cd6c28c9b3
115
core/Api/ContactAPI.class.php
Normal file
115
core/Api/ContactAPI.class.php
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Api {
|
||||||
|
class ContactAPI extends Request {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Api\Contact {
|
||||||
|
|
||||||
|
use Api\ContactAPI;
|
||||||
|
use Api\Parameter\Parameter;
|
||||||
|
use Api\Parameter\StringType;
|
||||||
|
use Api\VerifyCaptcha;
|
||||||
|
use Objects\User;
|
||||||
|
|
||||||
|
class Request extends ContactAPI {
|
||||||
|
|
||||||
|
private int $notificationId;
|
||||||
|
private int $contactRequestId;
|
||||||
|
|
||||||
|
public function __construct(User $user, bool $externalCall = false) {
|
||||||
|
$parameters = array(
|
||||||
|
'fromName' => new StringType('fromName', 32),
|
||||||
|
'fromEmail' => new Parameter('fromEmail', Parameter::TYPE_EMAIL),
|
||||||
|
'message' => new StringType('message', 512),
|
||||||
|
);
|
||||||
|
|
||||||
|
$settings = $user->getConfiguration()->getSettings();
|
||||||
|
if ($settings->isRecaptchaEnabled()) {
|
||||||
|
$parameters["captcha"] = new StringType("captcha");
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::__construct($user, $externalCall, $parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute($values = array()) {
|
||||||
|
if (!parent::execute($values)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$settings = $this->user->getConfiguration()->getSettings();
|
||||||
|
if ($settings->isRecaptchaEnabled()) {
|
||||||
|
$captcha = $this->getParam("captcha");
|
||||||
|
$req = new VerifyCaptcha($this->user);
|
||||||
|
if (!$req->execute(array("captcha" => $captcha, "action" => "contact"))) {
|
||||||
|
return $this->createError($req->getLastError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->insertContactRequest()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->createNotification();
|
||||||
|
|
||||||
|
if (!$this->success) {
|
||||||
|
return $this->createError("The contact request was saved, but the server was unable to create a notification.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function insertContactRequest() {
|
||||||
|
$sql = $this->user->getSQL();
|
||||||
|
$name = $this->getParam("fromName");
|
||||||
|
$email = $this->getParam("fromEmail");
|
||||||
|
$message = $this->getParam("message");
|
||||||
|
|
||||||
|
$res = $sql->insert("ContactRequest", array("from_name", "from_email", "message"))
|
||||||
|
->addRow($name, $email, $message)
|
||||||
|
->returning("uid")
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$this->success = ($res !== FALSE);
|
||||||
|
$this->lastError = $sql->getLastError();
|
||||||
|
|
||||||
|
if ($this->success) {
|
||||||
|
$this->contactRequestId = $sql->getLastInsertId();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createNotification() {
|
||||||
|
$sql = $this->user->getSQL();
|
||||||
|
$name = $this->getParam("fromName");
|
||||||
|
$email = $this->getParam("fromEmail");
|
||||||
|
$message = $this->getParam("message");
|
||||||
|
|
||||||
|
$res = $sql->insert("Notification", array("title", "message", "type"))
|
||||||
|
->addRow("New Contact Request from: $name", "$name ($email) wrote:\n$message", "message")
|
||||||
|
->returning("uid")
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$this->success = ($res !== FALSE);
|
||||||
|
$this->lastError = $sql->getLastError();
|
||||||
|
|
||||||
|
if ($this->success) {
|
||||||
|
$this->notificationId = $sql->getLastInsertId();
|
||||||
|
|
||||||
|
$res = $sql->insert("GroupNotification", array("group_id", "notification_id"))
|
||||||
|
->addRow(USER_GROUP_ADMIN, $this->notificationId)
|
||||||
|
->addRow(USER_GROUP_SUPPORT, $this->notificationId)
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$this->success = ($res !== FALSE);
|
||||||
|
$this->lastError = $sql->getLastError();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,11 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Api {
|
namespace Api {
|
||||||
|
|
||||||
class NotificationsAPI extends Request {
|
class NotificationsAPI extends Request {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Api\Notifications {
|
namespace Api\Notifications {
|
||||||
@ -14,6 +12,9 @@ namespace Api\Notifications {
|
|||||||
use Api\Parameter\Parameter;
|
use Api\Parameter\Parameter;
|
||||||
use Api\Parameter\StringType;
|
use Api\Parameter\StringType;
|
||||||
use Driver\SQL\Condition\Compare;
|
use Driver\SQL\Condition\Compare;
|
||||||
|
use Driver\SQL\Condition\CondIn;
|
||||||
|
use Driver\SQL\Query\Select;
|
||||||
|
use Objects\User;
|
||||||
|
|
||||||
class Create extends NotificationsAPI {
|
class Create extends NotificationsAPI {
|
||||||
|
|
||||||
@ -144,67 +145,67 @@ namespace Api\Notifications {
|
|||||||
class Fetch extends NotificationsAPI {
|
class Fetch extends NotificationsAPI {
|
||||||
|
|
||||||
private array $notifications;
|
private array $notifications;
|
||||||
|
private array $notificationids;
|
||||||
|
|
||||||
public function __construct($user, $externalCall = false) {
|
public function __construct($user, $externalCall = false) {
|
||||||
parent::__construct($user, $externalCall, array());
|
parent::__construct($user, $externalCall, array(
|
||||||
|
'new' => new Parameter('new', Parameter::TYPE_BOOLEAN, true, true)
|
||||||
|
));
|
||||||
$this->loginRequired = true;
|
$this->loginRequired = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function fetchUserNotifications() {
|
private function fetchUserNotifications() {
|
||||||
|
$onlyNew = $this->getParam('new');
|
||||||
$userId = $this->user->getId();
|
$userId = $this->user->getId();
|
||||||
$sql = $this->user->getSQL();
|
$sql = $this->user->getSQL();
|
||||||
$res = $sql->select($sql->distinct("Notification.uid"), "created_at", "title", "message")
|
$query = $sql->select($sql->distinct("Notification.uid"), "created_at", "title", "message", "type")
|
||||||
->from("Notification")
|
->from("Notification")
|
||||||
->innerJoin("UserNotification", "UserNotification.notification_id", "Notification.uid")
|
->innerJoin("UserNotification", "UserNotification.notification_id", "Notification.uid")
|
||||||
->where(new Compare("UserNotification.user_id", $userId))
|
->where(new Compare("UserNotification.user_id", $userId))
|
||||||
->where(new Compare("UserNotification.seen", false))
|
->orderBy("created_at")->descending();
|
||||||
->orderBy("created_at")->descending()
|
|
||||||
->execute();
|
|
||||||
|
|
||||||
$this->success = ($res !== FALSE);
|
if ($onlyNew) {
|
||||||
$this->lastError = $sql->getLastError();
|
$query->where(new Compare("UserNotification.seen", false));
|
||||||
|
|
||||||
if ($this->success) {
|
|
||||||
foreach($res as $row) {
|
|
||||||
$id = $row["uid"];
|
|
||||||
if (!isset($this->notifications[$id])) {
|
|
||||||
$this->notifications[$id] = array(
|
|
||||||
"uid" => $id,
|
|
||||||
"title" => $row["title"],
|
|
||||||
"message" => $row["message"],
|
|
||||||
"created_at" => $row["created_at"],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->success;
|
return $this->fetchNotifications($query);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function fetchGroupNotifications() {
|
private function fetchGroupNotifications() {
|
||||||
|
$onlyNew = $this->getParam('new');
|
||||||
$userId = $this->user->getId();
|
$userId = $this->user->getId();
|
||||||
$sql = $this->user->getSQL();
|
$sql = $this->user->getSQL();
|
||||||
$res = $sql->select($sql->distinct("Notification.uid"), "created_at", "title", "message")
|
$query = $sql->select($sql->distinct("Notification.uid"), "created_at", "title", "message", "type")
|
||||||
->from("Notification")
|
->from("Notification")
|
||||||
->innerJoin("GroupNotification", "GroupNotification.notification_id", "Notification.uid")
|
->innerJoin("GroupNotification", "GroupNotification.notification_id", "Notification.uid")
|
||||||
->innerJoin("UserGroup", "GroupNotification.group_id", "UserGroup.group_id")
|
->innerJoin("UserGroup", "GroupNotification.group_id", "UserGroup.group_id")
|
||||||
->where(new Compare("UserGroup.user_id", $userId))
|
->where(new Compare("UserGroup.user_id", $userId))
|
||||||
->where(new Compare("GroupNotification.seen", false))
|
->orderBy("created_at")->descending();
|
||||||
->orderBy("created_at")->descending()
|
|
||||||
->execute();
|
|
||||||
|
|
||||||
|
if ($onlyNew) {
|
||||||
|
$query->where(new Compare("GroupNotification.seen", false));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->fetchNotifications($query);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function fetchNotifications(Select $query) {
|
||||||
|
$sql = $this->user->getSQL();
|
||||||
|
$res = $query->execute();
|
||||||
$this->success = ($res !== FALSE);
|
$this->success = ($res !== FALSE);
|
||||||
$this->lastError = $sql->getLastError();
|
$this->lastError = $sql->getLastError();
|
||||||
|
|
||||||
if ($this->success) {
|
if ($this->success) {
|
||||||
foreach($res as $row) {
|
foreach($res as $row) {
|
||||||
$id = $row["uid"];
|
$id = $row["uid"];
|
||||||
if (!isset($this->notifications[$id])) {
|
if (!in_array($id, $this->notificationids)) {
|
||||||
|
$this->notificationids[] = $id;
|
||||||
$this->notifications[] = array(
|
$this->notifications[] = array(
|
||||||
"uid" => $id,
|
"uid" => $id,
|
||||||
"title" => $row["title"],
|
"title" => $row["title"],
|
||||||
"message" => $row["message"],
|
"message" => $row["message"],
|
||||||
"created_at" => $row["created_at"],
|
"created_at" => $row["created_at"],
|
||||||
|
"type" => $row["type"]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -219,6 +220,7 @@ namespace Api\Notifications {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->notifications = array();
|
$this->notifications = array();
|
||||||
|
$this->notificationids = array();
|
||||||
if ($this->fetchUserNotifications() && $this->fetchGroupNotifications()) {
|
if ($this->fetchUserNotifications() && $this->fetchGroupNotifications()) {
|
||||||
$this->result["notifications"] = $this->notifications;
|
$this->result["notifications"] = $this->notifications;
|
||||||
}
|
}
|
||||||
@ -227,4 +229,41 @@ namespace Api\Notifications {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Seen extends NotificationsAPI {
|
||||||
|
|
||||||
|
public function __construct(User $user, bool $externalCall = false) {
|
||||||
|
parent::__construct($user, $externalCall, array());
|
||||||
|
$this->loginRequired = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute($values = array()) {
|
||||||
|
if (!parent::execute($values)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = $this->user->getSQL();
|
||||||
|
$res = $sql->update("UserNotification")
|
||||||
|
->set("seen", true)
|
||||||
|
->where(new Compare("user_id", $this->user->getId()))
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$this->success = ($res !== FALSE);
|
||||||
|
$this->lastError = $sql->getLastError();
|
||||||
|
|
||||||
|
if ($this->success) {
|
||||||
|
$res = $sql->update("GroupNotification")
|
||||||
|
->set("seen", true)
|
||||||
|
->where(new CondIn("group_id",
|
||||||
|
$sql->select("group_id")
|
||||||
|
->from("UserGroup")
|
||||||
|
->where(new Compare("user_id", $this->user->getId()))))
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$this->success = ($res !== FALSE);
|
||||||
|
$this->lastError = $sql->getLastError();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->success;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -48,15 +48,17 @@ class Request {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function parseParams($values) {
|
public function parseParams($values) {
|
||||||
|
|
||||||
foreach($this->params as $name => $param) {
|
foreach($this->params as $name => $param) {
|
||||||
$value = $values[$name] ?? NULL;
|
$value = $values[$name] ?? NULL;
|
||||||
|
|
||||||
if(!$param->optional && (is_null($value) || empty($value))) {
|
$isEmpty = (is_string($value) || is_array($value)) && empty($value);
|
||||||
|
if(!$param->optional && (is_null($value) || $isEmpty)) {
|
||||||
$this->lastError = 'Missing parameter: ' . $name;
|
$this->lastError = 'Missing parameter: ' . $name;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!is_null($value) && !empty($value)) {
|
if(!is_null($value) && !$isEmpty) {
|
||||||
if(!$param->parseParam($value)) {
|
if(!$param->parseParam($value)) {
|
||||||
$value = print_r($value, true);
|
$value = print_r($value, true);
|
||||||
$this->lastError = "Invalid Type for parameter: $name '$value' (Required: " . $param->getTypeName() . ")";
|
$this->lastError = "Invalid Type for parameter: $name '$value' (Required: " . $param->getTypeName() . ")";
|
||||||
|
@ -120,6 +120,7 @@ namespace Api\User {
|
|||||||
use Api\Parameter\StringType;
|
use Api\Parameter\StringType;
|
||||||
use Api\SendMail;
|
use Api\SendMail;
|
||||||
use Api\UserAPI;
|
use Api\UserAPI;
|
||||||
|
use Api\VerifyCaptcha;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
use Driver\SQL\Condition\Compare;
|
use Driver\SQL\Condition\Compare;
|
||||||
use Driver\SQL\Condition\CondIn;
|
use Driver\SQL\Condition\CondIn;
|
||||||
@ -531,13 +532,20 @@ namespace Api\User {
|
|||||||
private ?int $userId;
|
private ?int $userId;
|
||||||
private string $token;
|
private string $token;
|
||||||
|
|
||||||
public function __construct($user, $externalCall = false) {
|
public function __construct(User $user, bool $externalCall = false) {
|
||||||
parent::__construct($user, $externalCall, array(
|
$parameters = array(
|
||||||
"username" => new StringType("username", 32),
|
"username" => new StringType("username", 32),
|
||||||
'email' => new Parameter('email', Parameter::TYPE_EMAIL),
|
'email' => new Parameter('email', Parameter::TYPE_EMAIL),
|
||||||
"password" => new StringType("password"),
|
"password" => new StringType("password"),
|
||||||
"confirmPassword" => new StringType("confirmPassword"),
|
"confirmPassword" => new StringType("confirmPassword"),
|
||||||
));
|
);
|
||||||
|
|
||||||
|
$settings = $user->getConfiguration()->getSettings();
|
||||||
|
if ($settings->isRecaptchaEnabled()) {
|
||||||
|
$parameters["captcha"] = new StringType("captcha");
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::__construct($user, $externalCall, $parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function insertToken() {
|
private function insertToken() {
|
||||||
@ -582,6 +590,15 @@ namespace Api\User {
|
|||||||
return $this->createError("User Registration is not enabled.");
|
return $this->createError("User Registration is not enabled.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$settings = $this->user->getConfiguration()->getSettings();
|
||||||
|
if ($settings->isRecaptchaEnabled()) {
|
||||||
|
$captcha = $this->getParam("captcha");
|
||||||
|
$req = new VerifyCaptcha($this->user);
|
||||||
|
if (!$req->execute(array("captcha" => $captcha, "action" => "register"))) {
|
||||||
|
return $this->createError($req->getLastError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$username = $this->getParam("username");
|
$username = $this->getParam("username");
|
||||||
$email = $this->getParam('email');
|
$email = $this->getParam('email');
|
||||||
if (!$this->userExists($username, $email)) {
|
if (!$this->userExists($username, $email)) {
|
||||||
|
67
core/Api/verifyCaptcha.class.php
Normal file
67
core/Api/verifyCaptcha.class.php
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Api;
|
||||||
|
|
||||||
|
use Api\Parameter\StringType;
|
||||||
|
use Objects\User;
|
||||||
|
|
||||||
|
class VerifyCaptcha extends Request {
|
||||||
|
|
||||||
|
public function __construct(User $user, bool $externalCall = false) {
|
||||||
|
parent::__construct($user, $externalCall, array(
|
||||||
|
"captcha" => new StringType("captcha"),
|
||||||
|
"action" => new StringType("action"),
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->isPublic = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute($values = array()) {
|
||||||
|
if(!parent::execute($values)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$settings = $this->user->getConfiguration()->getSettings();
|
||||||
|
if (!$settings->isRecaptchaEnabled()) {
|
||||||
|
return $this->createError("Google reCaptcha is not enabled.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$url = "https://www.google.com/recaptcha/api/siteverify";
|
||||||
|
$secret = $settings->getRecaptchaSecretKey();
|
||||||
|
$captcha = $this->getParam("captcha");
|
||||||
|
$action = $this->getParam("action");
|
||||||
|
|
||||||
|
$params = array(
|
||||||
|
"secret" => $secret,
|
||||||
|
"response" => $captcha
|
||||||
|
);
|
||||||
|
|
||||||
|
$ch = curl_init();
|
||||||
|
curl_setopt($ch, CURLOPT_URL,$url);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, 1);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
$response = @json_decode(curl_exec($ch), true);
|
||||||
|
curl_close ($ch);
|
||||||
|
|
||||||
|
$this->success = false;
|
||||||
|
$this->lastError = "Could not verify captcha: No response from google received.";
|
||||||
|
|
||||||
|
if($response) {
|
||||||
|
$this->success = $response["success"];
|
||||||
|
if(!$this->success) {
|
||||||
|
$this->lastError = "Could not verify captcha: " . implode(";", $response["error-codes"]);
|
||||||
|
} else {
|
||||||
|
$score = $response["score"];
|
||||||
|
if($action !== $response["action"]) {
|
||||||
|
$this->createError("Could not verify captcha: Action does not match");
|
||||||
|
}
|
||||||
|
else if($score < 0.7) {
|
||||||
|
$this->createError("Could not verify captcha: Google ReCaptcha Score < 0.7 (Your score: $score), you are likely a bot");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->success;
|
||||||
|
}
|
||||||
|
}
|
@ -88,6 +88,7 @@ class CreateDatabase {
|
|||||||
|
|
||||||
$queries[] = $sql->createTable("Notification")
|
$queries[] = $sql->createTable("Notification")
|
||||||
->addSerial("uid")
|
->addSerial("uid")
|
||||||
|
->addEnum("type", array("default","message","warning"), false, "default")
|
||||||
->addDateTime("created_at", false, $sql->currentTimestamp())
|
->addDateTime("created_at", false, $sql->currentTimestamp())
|
||||||
->addString("title", 32)
|
->addString("title", 32)
|
||||||
->addString("message", 256)
|
->addString("message", 256)
|
||||||
@ -96,7 +97,7 @@ class CreateDatabase {
|
|||||||
$queries[] = $sql->createTable("UserNotification")
|
$queries[] = $sql->createTable("UserNotification")
|
||||||
->addInt("user_id")
|
->addInt("user_id")
|
||||||
->addInt("notification_id")
|
->addInt("notification_id")
|
||||||
->addBool("seen")
|
->addBool("seen", false)
|
||||||
->foreignKey("user_id", "User", "uid")
|
->foreignKey("user_id", "User", "uid")
|
||||||
->foreignKey("notification_id", "Notification", "uid")
|
->foreignKey("notification_id", "Notification", "uid")
|
||||||
->unique("user_id", "notification_id");
|
->unique("user_id", "notification_id");
|
||||||
@ -104,7 +105,7 @@ class CreateDatabase {
|
|||||||
$queries[] = $sql->createTable("GroupNotification")
|
$queries[] = $sql->createTable("GroupNotification")
|
||||||
->addInt("group_id")
|
->addInt("group_id")
|
||||||
->addInt("notification_id")
|
->addInt("notification_id")
|
||||||
->addBool("seen")
|
->addBool("seen", false)
|
||||||
->foreignKey("group_id", "Group", "uid")
|
->foreignKey("group_id", "Group", "uid")
|
||||||
->foreignKey("notification_id", "Notification", "uid")
|
->foreignKey("notification_id", "Notification", "uid")
|
||||||
->unique("group_id", "notification_id");
|
->unique("group_id", "notification_id");
|
||||||
@ -144,8 +145,8 @@ class CreateDatabase {
|
|||||||
$queries[] = $sql->createTable("Settings")
|
$queries[] = $sql->createTable("Settings")
|
||||||
->addString("name", 32)
|
->addString("name", 32)
|
||||||
->addString("value", 1024, true)
|
->addString("value", 1024, true)
|
||||||
->addBool("private", false)
|
->addBool("private", false) // these values are not returned from '/api/settings/get', but can be changed
|
||||||
->addBool("readonly", false)
|
->addBool("readonly", false) // these values are neither returned, nor can be changed from outside
|
||||||
->primaryKey("name");
|
->primaryKey("name");
|
||||||
|
|
||||||
$settingsQuery = $sql->insert("Settings", array("name", "value", "private", "readonly"))
|
$settingsQuery = $sql->insert("Settings", array("name", "value", "private", "readonly"))
|
||||||
@ -162,6 +163,14 @@ class CreateDatabase {
|
|||||||
(Settings::loadDefaults())->addRows($settingsQuery);
|
(Settings::loadDefaults())->addRows($settingsQuery);
|
||||||
$queries[] = $settingsQuery;
|
$queries[] = $settingsQuery;
|
||||||
|
|
||||||
|
$queries[] = $sql->createTable("ContactRequest")
|
||||||
|
->addSerial("uid")
|
||||||
|
->addString("from_name", 32)
|
||||||
|
->addString("from_email", 64)
|
||||||
|
->addString("message", 512)
|
||||||
|
->addDateTime("created_at", false, $sql->currentTimestamp())
|
||||||
|
->primaryKey("uid");
|
||||||
|
|
||||||
return $queries;
|
return $queries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,9 @@ class Settings {
|
|||||||
private string $jwtSecret;
|
private string $jwtSecret;
|
||||||
private bool $installationComplete;
|
private bool $installationComplete;
|
||||||
private bool $registrationAllowed;
|
private bool $registrationAllowed;
|
||||||
|
private bool $recaptchaEnabled;
|
||||||
|
private string $recaptchaPublicKey;
|
||||||
|
private string $recaptchaPrivateKey;
|
||||||
|
|
||||||
public function getJwtSecret(): string {
|
public function getJwtSecret(): string {
|
||||||
return $this->jwtSecret;
|
return $this->jwtSecret;
|
||||||
@ -36,6 +39,9 @@ class Settings {
|
|||||||
$settings->jwtSecret = $jwt;
|
$settings->jwtSecret = $jwt;
|
||||||
$settings->installationComplete = false;
|
$settings->installationComplete = false;
|
||||||
$settings->registrationAllowed = false;
|
$settings->registrationAllowed = false;
|
||||||
|
$settings->recaptchaPublicKey = "";
|
||||||
|
$settings->recaptchaPrivateKey = "";
|
||||||
|
$settings->recaptchaEnabled = false;
|
||||||
return $settings;
|
return $settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,6 +55,9 @@ class Settings {
|
|||||||
$this->registrationAllowed = $result["user_registration_enabled"] ?? $this->registrationAllowed;
|
$this->registrationAllowed = $result["user_registration_enabled"] ?? $this->registrationAllowed;
|
||||||
$this->installationComplete = $result["installation_completed"] ?? $this->installationComplete;
|
$this->installationComplete = $result["installation_completed"] ?? $this->installationComplete;
|
||||||
$this->jwtSecret = $result["jwt_secret"] ?? $this->jwtSecret;
|
$this->jwtSecret = $result["jwt_secret"] ?? $this->jwtSecret;
|
||||||
|
$this->recaptchaEnabled = $result["recaptcha_enabled"] ?? $this->recaptchaEnabled;
|
||||||
|
$this->recaptchaPublicKey = $result["recaptcha_public_key"] ?? $this->recaptchaPublicKey;
|
||||||
|
$this->recaptchaPrivateKey = $result["recaptcha_private_key"] ?? $this->recaptchaPrivateKey;
|
||||||
|
|
||||||
if (!isset($result["jwt_secret"])) {
|
if (!isset($result["jwt_secret"])) {
|
||||||
$req = new \Api\Settings\Set($user);
|
$req = new \Api\Settings\Set($user);
|
||||||
@ -66,7 +75,10 @@ class Settings {
|
|||||||
->addRow("base_url", $this->baseUrl, false, false)
|
->addRow("base_url", $this->baseUrl, false, false)
|
||||||
->addRow("user_registration_enabled", $this->registrationAllowed ? "1" : "0", false, false)
|
->addRow("user_registration_enabled", $this->registrationAllowed ? "1" : "0", false, false)
|
||||||
->addRow("installation_completed", $this->installationComplete ? "1" : "0", true, true)
|
->addRow("installation_completed", $this->installationComplete ? "1" : "0", true, true)
|
||||||
->addRow("jwt_secret", $this->jwtSecret, true, true);
|
->addRow("jwt_secret", $this->jwtSecret, true, true)
|
||||||
|
->addRow("recaptcha_enabled", $this->recaptchaEnabled ? "1" : "0", false, false)
|
||||||
|
->addRow("recaptcha_public_key", $this->recaptchaPublicKey, false, false)
|
||||||
|
->addRow("recaptcha_private_key", $this->recaptchaPrivateKey, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSiteName() {
|
public function getSiteName() {
|
||||||
@ -76,4 +88,16 @@ class Settings {
|
|||||||
public function getBaseUrl() {
|
public function getBaseUrl() {
|
||||||
return $this->baseUrl;
|
return $this->baseUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isRecaptchaEnabled() {
|
||||||
|
return $this->recaptchaEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRecaptchaSiteKey() {
|
||||||
|
return $this->recaptchaPublicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRecaptchaSecretKey() {
|
||||||
|
return $this->recaptchaPrivateKey;
|
||||||
|
}
|
||||||
}
|
}
|
@ -5,13 +5,13 @@ namespace Driver\SQL\Condition;
|
|||||||
class CondIn extends Condition {
|
class CondIn extends Condition {
|
||||||
|
|
||||||
private string $column;
|
private string $column;
|
||||||
private array $values;
|
private $expression;
|
||||||
|
|
||||||
public function __construct(string $column, array $values) {
|
public function __construct(string $column, $expression) {
|
||||||
$this->column = $column;
|
$this->column = $column;
|
||||||
$this->values = $values;
|
$this->expression = $expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getColumn() { return $this->column; }
|
public function getColumn() { return $this->column; }
|
||||||
public function getValues() { return $this->values; }
|
public function getExpression() { return $this->expression; }
|
||||||
}
|
}
|
@ -10,7 +10,6 @@ use Driver\SQL\Condition\Condition;
|
|||||||
use Driver\SQL\Condition\CondKeyword;
|
use Driver\SQL\Condition\CondKeyword;
|
||||||
use Driver\SQL\Condition\CondNot;
|
use Driver\SQL\Condition\CondNot;
|
||||||
use Driver\SQL\Condition\CondOr;
|
use Driver\SQL\Condition\CondOr;
|
||||||
use Driver\SQL\Condition\CondRegex;
|
|
||||||
use Driver\SQL\Constraint\Constraint;
|
use Driver\SQL\Constraint\Constraint;
|
||||||
use \Driver\SQL\Constraint\Unique;
|
use \Driver\SQL\Constraint\Unique;
|
||||||
use \Driver\SQL\Constraint\PrimaryKey;
|
use \Driver\SQL\Constraint\PrimaryKey;
|
||||||
@ -18,6 +17,7 @@ use \Driver\SQL\Constraint\ForeignKey;
|
|||||||
use Driver\SQL\Query\CreateTable;
|
use Driver\SQL\Query\CreateTable;
|
||||||
use Driver\SQL\Query\Delete;
|
use Driver\SQL\Query\Delete;
|
||||||
use Driver\SQL\Query\Insert;
|
use Driver\SQL\Query\Insert;
|
||||||
|
use Driver\SQL\Query\Query;
|
||||||
use Driver\SQL\Query\Select;
|
use Driver\SQL\Query\Select;
|
||||||
use Driver\SQL\Query\Truncate;
|
use Driver\SQL\Query\Truncate;
|
||||||
use Driver\SQL\Query\Update;
|
use Driver\SQL\Query\Update;
|
||||||
@ -50,27 +50,27 @@ abstract class SQL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function createTable($tableName) {
|
public function createTable($tableName) {
|
||||||
return new Query\CreateTable($this, $tableName);
|
return new CreateTable($this, $tableName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function insert($tableName, $columns=array()) {
|
public function insert($tableName, $columns=array()) {
|
||||||
return new Query\Insert($this, $tableName, $columns);
|
return new Insert($this, $tableName, $columns);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function select(...$columNames) {
|
public function select(...$columNames) {
|
||||||
return new Query\Select($this, $columNames);
|
return new Select($this, $columNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function truncate($table) {
|
public function truncate($table) {
|
||||||
return new Query\Truncate($this, $table);
|
return new Truncate($this, $table);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete($table) {
|
public function delete($table) {
|
||||||
return new Query\Delete($this, $table);
|
return new Delete($this, $table);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update($table) {
|
public function update($table) {
|
||||||
return new Query\Update($this, $table);
|
return new Update($this, $table);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ####################
|
// ####################
|
||||||
@ -86,6 +86,53 @@ abstract class SQL {
|
|||||||
public abstract function disconnect();
|
public abstract function disconnect();
|
||||||
|
|
||||||
// Querybuilder
|
// Querybuilder
|
||||||
|
protected function buildQuery(Query $query, array &$params) {
|
||||||
|
if ($query instanceof Select) {
|
||||||
|
$select = $query;
|
||||||
|
$columns = $this->columnName($select->getColumns());
|
||||||
|
$tables = $select->getTables();
|
||||||
|
|
||||||
|
if (!$tables) {
|
||||||
|
return $this->execute("SELECT $columns", $params, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$tables = $this->tableName($tables);
|
||||||
|
$where = $this->getWhereClause($select->getConditions(), $params);
|
||||||
|
|
||||||
|
$joinStr = "";
|
||||||
|
$joins = $select->getJoins();
|
||||||
|
if (!empty($joins)) {
|
||||||
|
foreach($joins as $join) {
|
||||||
|
$type = $join->getType();
|
||||||
|
$joinTable = $this->tableName($join->getTable());
|
||||||
|
$columnA = $this->columnName($join->getColumnA());
|
||||||
|
$columnB = $this->columnName($join->getColumnB());
|
||||||
|
$joinStr .= " $type JOIN $joinTable ON $columnA=$columnB";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$groupBy = "";
|
||||||
|
$groupColumns = $select->getGroupBy();
|
||||||
|
if (!empty($groupColumns)) {
|
||||||
|
$groupBy = " GROUP BY " . $this->columnName($groupColumns);
|
||||||
|
}
|
||||||
|
|
||||||
|
$orderBy = "";
|
||||||
|
$orderColumns = $select->getOrderBy();
|
||||||
|
if (!empty($orderColumns)) {
|
||||||
|
$orderBy = " ORDER BY " . $this->columnName($orderColumns);
|
||||||
|
$orderBy .= ($select->isOrderedAscending() ? " ASC" : " DESC");
|
||||||
|
}
|
||||||
|
|
||||||
|
$limit = ($select->getLimit() > 0 ? (" LIMIT " . $select->getLimit()) : "");
|
||||||
|
$offset = ($select->getOffset() > 0 ? (" OFFSET " . $select->getOffset()) : "");
|
||||||
|
return "SELECT $columns FROM $tables$joinStr$where$groupBy$orderBy$limit$offset";
|
||||||
|
} else {
|
||||||
|
$this->lastError = "buildQuery() not implemented for type: " . get_class($query);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function executeCreateTable(CreateTable $createTable) {
|
public function executeCreateTable(CreateTable $createTable) {
|
||||||
$tableName = $this->tableName($createTable->getTableName());
|
$tableName = $this->tableName($createTable->getTableName());
|
||||||
$ifNotExists = $createTable->ifNotExists() ? " IF NOT EXISTS": "";
|
$ifNotExists = $createTable->ifNotExists() ? " IF NOT EXISTS": "";
|
||||||
@ -161,46 +208,8 @@ abstract class SQL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function executeSelect(Select $select) {
|
public function executeSelect(Select $select) {
|
||||||
|
|
||||||
$columns = $this->columnName($select->getColumns());
|
|
||||||
$tables = $select->getTables();
|
|
||||||
$params = array();
|
$params = array();
|
||||||
|
$query = $this->buildQuery($select, $params);
|
||||||
if (!$tables) {
|
|
||||||
return $this->execute("SELECT $columns", $params, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
$tables = $this->tableName($tables);
|
|
||||||
$where = $this->getWhereClause($select->getConditions(), $params);
|
|
||||||
|
|
||||||
$joinStr = "";
|
|
||||||
$joins = $select->getJoins();
|
|
||||||
if (!empty($joins)) {
|
|
||||||
foreach($joins as $join) {
|
|
||||||
$type = $join->getType();
|
|
||||||
$joinTable = $this->tableName($join->getTable());
|
|
||||||
$columnA = $this->columnName($join->getColumnA());
|
|
||||||
$columnB = $this->columnName($join->getColumnB());
|
|
||||||
$joinStr .= " $type JOIN $joinTable ON $columnA=$columnB";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$groupBy = "";
|
|
||||||
$groupColumns = $select->getGroupBy();
|
|
||||||
if (!empty($groupColumns)) {
|
|
||||||
$groupBy = " GROUP BY " . $this->columnName($groupColumns);
|
|
||||||
}
|
|
||||||
|
|
||||||
$orderBy = "";
|
|
||||||
$orderColumns = $select->getOrderBy();
|
|
||||||
if (!empty($orderColumns)) {
|
|
||||||
$orderBy = " ORDER BY " . $this->columnName($orderColumns);
|
|
||||||
$orderBy .= ($select->isOrderedAscending() ? " ASC" : " DESC");
|
|
||||||
}
|
|
||||||
|
|
||||||
$limit = ($select->getLimit() > 0 ? (" LIMIT " . $select->getLimit()) : "");
|
|
||||||
$offset = ($select->getOffset() > 0 ? (" OFFSET " . $select->getOffset()) : "");
|
|
||||||
$query = "SELECT $columns FROM $tables$joinStr$where$groupBy$orderBy$limit$offset";
|
|
||||||
if($select->dump) { var_dump($query); var_dump($params); }
|
if($select->dump) { var_dump($query); var_dump($params); }
|
||||||
return $this->execute($query, $params, true);
|
return $this->execute($query, $params, true);
|
||||||
}
|
}
|
||||||
@ -342,14 +351,21 @@ abstract class SQL {
|
|||||||
}
|
}
|
||||||
} else if($condition instanceof CondIn) {
|
} else if($condition instanceof CondIn) {
|
||||||
|
|
||||||
$value = $condition->getValues();
|
$expression = $condition->getExpression();
|
||||||
|
if (is_array($expression)) {
|
||||||
|
$values = array();
|
||||||
|
foreach ($expression as $value) {
|
||||||
|
$values[] = $this->addValue($value, $params);
|
||||||
|
}
|
||||||
|
|
||||||
$values = array();
|
$values = implode(",", $values);
|
||||||
foreach ($condition->getValues() as $value) {
|
} else if($expression instanceof Select) {
|
||||||
$values[] = $this->addValue($value, $params);
|
$values = $this->buildQuery($expression, $params);
|
||||||
|
} else {
|
||||||
|
$this->lastError = "Unsupported in-expression value: " . get_class($condition);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$values = implode(",", $values);
|
|
||||||
return $this->columnName($condition->getColumn()) . " IN ($values)";
|
return $this->columnName($condition->getColumn()) . " IN ($values)";
|
||||||
} else if($condition instanceof CondKeyword) {
|
} else if($condition instanceof CondKeyword) {
|
||||||
$left = $condition->getLeftExp();
|
$left = $condition->getLeftExp();
|
||||||
|
@ -54,7 +54,7 @@ if(isset($_GET["api"]) && is_string($_GET["api"])) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
$file = getClassPath($parentClass);
|
$file = getClassPath($parentClass);
|
||||||
if(!file_exists($file)) {
|
if(!file_exists($file) || !class_exists($parentClass) || !class_exists($apiClass)) {
|
||||||
header("404 Not Found");
|
header("404 Not Found");
|
||||||
$response = createError("Not found");
|
$response = createError("Not found");
|
||||||
} else {
|
} else {
|
||||||
|
10
js/admin.min.js
vendored
10
js/admin.min.js
vendored
File diff suppressed because one or more lines are too long
@ -43,8 +43,12 @@ export default class API {
|
|||||||
return this.apiCall("user/logout");
|
return this.apiCall("user/logout");
|
||||||
}
|
}
|
||||||
|
|
||||||
async getNotifications() {
|
async getNotifications(onlyNew = true) {
|
||||||
return this.apiCall("notifications/fetch");
|
return this.apiCall("notifications/fetch", { new: onlyNew });
|
||||||
|
}
|
||||||
|
|
||||||
|
async markNotificationsSeen() {
|
||||||
|
return this.apiCall("notifications/seen");
|
||||||
}
|
}
|
||||||
|
|
||||||
async getUser(id) {
|
async getUser(id) {
|
||||||
@ -101,6 +105,5 @@ export default class API {
|
|||||||
|
|
||||||
async sendTestMail(receiver) {
|
async sendTestMail(receiver) {
|
||||||
return this.apiCall("sendTestMail", { receiver: receiver });
|
return this.apiCall("sendTestMail", { receiver: receiver });
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
@ -28,7 +28,7 @@ class AdminDashboard extends React.Component {
|
|||||||
this.state = {
|
this.state = {
|
||||||
loaded: false,
|
loaded: false,
|
||||||
dialog: { onClose: () => this.hideDialog() },
|
dialog: { onClose: () => this.hideDialog() },
|
||||||
notifications: { }
|
notifications: [ ]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,6 +75,7 @@ class AdminDashboard extends React.Component {
|
|||||||
|
|
||||||
this.controlObj = {
|
this.controlObj = {
|
||||||
showDialog: this.showDialog.bind(this),
|
showDialog: this.showDialog.bind(this),
|
||||||
|
fetchNotifications: this.fetchNotifications.bind(this),
|
||||||
api: this.api
|
api: this.api
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -92,7 +93,7 @@ class AdminDashboard extends React.Component {
|
|||||||
return <EditUser {...newProps} />
|
return <EditUser {...newProps} />
|
||||||
}}/>
|
}}/>
|
||||||
<Route path={"/admin/group/add"}><CreateGroup {...this.controlObj} /></Route>
|
<Route path={"/admin/group/add"}><CreateGroup {...this.controlObj} /></Route>
|
||||||
<Route path={"/admin/logs"}><Logs {...this.controlObj} /></Route>
|
<Route path={"/admin/logs"}><Logs {...this.controlObj} notifications={this.state.notifications} /></Route>
|
||||||
<Route path={"/admin/settings"}><Settings {...this.controlObj} /></Route>
|
<Route path={"/admin/settings"}><Settings {...this.controlObj} /></Route>
|
||||||
<Route path={"/admin/pages"}><PageOverview {...this.controlObj} /></Route>
|
<Route path={"/admin/pages"}><PageOverview {...this.controlObj} /></Route>
|
||||||
<Route path={"/admin/help"}><HelpPage {...this.controlObj} /></Route>
|
<Route path={"/admin/help"}><HelpPage {...this.controlObj} /></Route>
|
||||||
|
@ -32,8 +32,8 @@ export default function HelpPage() {
|
|||||||
<div className="col-12 col-md-8 col-lg-4">
|
<div className="col-12 col-md-8 col-lg-4">
|
||||||
<p>
|
<p>
|
||||||
WebBase is a php framework to simplify user management, pages and routing.
|
WebBase is a php framework to simplify user management, pages and routing.
|
||||||
It can easily be modified and extended by writing document classes or
|
It can easily be modified and extended by writing document classes and the database
|
||||||
access the database with the available abstracted scheme. It also includes
|
can be accessed through the available abstracted scheme. It also includes
|
||||||
a REST API with access control, parameter type checking and more.
|
a REST API with access control, parameter type checking and more.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,13 +1,100 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {Link} from "react-router-dom";
|
import {Link} from "react-router-dom";
|
||||||
|
import Icon from "../elements/icon";
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
export default class Logs extends React.Component {
|
export default class Logs extends React.Component {
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
alerts: [],
|
||||||
|
notifications: []
|
||||||
|
};
|
||||||
|
|
||||||
|
this.parent = {
|
||||||
|
api : props.api,
|
||||||
|
fetchNotifications: props.fetchNotifications,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeError(i) {
|
||||||
|
if (i >= 0 && i < this.state.alerts.length) {
|
||||||
|
let alerts = this.state.alerts.slice();
|
||||||
|
alerts.splice(i, 1);
|
||||||
|
this.setState({...this.state, alerts: alerts});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.parent.api.getNotifications(false).then((res) => {
|
||||||
|
if (!res.success) {
|
||||||
|
let alerts = this.state.alerts.slice();
|
||||||
|
alerts.push({ message: res.msg, title: "Error fetching Notifications" });
|
||||||
|
this.setState({ ...this.state, alerts: alerts });
|
||||||
|
} else {
|
||||||
|
this.setState({ ...this.state, notifications: res.notifications });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.parent.api.markNotificationsSeen().then((res) => {
|
||||||
|
if (!res.success) {
|
||||||
|
let alerts = this.state.alerts.slice();
|
||||||
|
alerts.push({ message: res.msg, title: "Error fetching Notifications" });
|
||||||
|
this.setState({ ...this.state, alerts: alerts });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.parent.fetchNotifications();
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
||||||
|
const colors = ["red", "green", "blue", "purple", "maroon"];
|
||||||
|
|
||||||
|
let dates = { };
|
||||||
|
for (let notification of this.state.notifications) {
|
||||||
|
let day = moment(notification["created_at"]).format('ll');
|
||||||
|
if (!dates.hasOwnProperty(day)) {
|
||||||
|
dates[day] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
let icon = "bell";
|
||||||
|
if (notification.type === "message") {
|
||||||
|
icon = "envelope";
|
||||||
|
} else if(notification.type === "warning") {
|
||||||
|
icon = "exclamation-triangle";
|
||||||
|
}
|
||||||
|
|
||||||
|
dates[day].push({ ...notification, icon: icon, timestamp: notification["created_at"] });
|
||||||
|
}
|
||||||
|
|
||||||
|
let elements = [];
|
||||||
|
for (let date in dates) {
|
||||||
|
let color = colors[Math.floor(Math.random() * colors.length)];
|
||||||
|
|
||||||
|
elements.push(
|
||||||
|
<div className={"time-label"} key={"time-label-" + date}>
|
||||||
|
<span className={"bg-" + color}>{date}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
for (let event of dates[date]) {
|
||||||
|
let timeString = moment(event.timestamp).fromNow();
|
||||||
|
elements.push(
|
||||||
|
<div>
|
||||||
|
<Icon icon={event.icon} className={"bg-" + color}/>
|
||||||
|
<div className="timeline-item">
|
||||||
|
<span className="time"><Icon icon={"clock"}/> {timeString}</span>
|
||||||
|
<h3 className="timeline-header">{event.title}</h3>
|
||||||
|
<div className="timeline-body">{event.message}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<div className="content-header">
|
<div className="content-header">
|
||||||
<div className="container-fluid">
|
<div className="container-fluid">
|
||||||
@ -25,13 +112,18 @@ export default class Logs extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={"content"}>
|
<div className={"content"}>
|
||||||
<div className={"content-fluid"}>
|
<div className={"container-fluid"}>
|
||||||
<div className={"row"}>
|
<div className={"row"}>
|
||||||
<div className={"col-lg-6"}>
|
<div className={"col-lg-8 col-12"}>
|
||||||
|
<div className="timeline">
|
||||||
</div>
|
<div className={"time-label"}>
|
||||||
<div className={"col-lg-6"}>
|
<span className={"bg-blue"}>Today</span>
|
||||||
|
</div>
|
||||||
|
{elements}
|
||||||
|
<div>
|
||||||
|
<Icon icon={"clock"} className={"bg-gray"}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -45,6 +45,13 @@ export default class Settings extends React.Component {
|
|||||||
isEditing: null,
|
isEditing: null,
|
||||||
keys: ["message_confirm_email", "message_accept_invite", "message_reset_password"]
|
keys: ["message_confirm_email", "message_accept_invite", "message_reset_password"]
|
||||||
},
|
},
|
||||||
|
recaptcha: {
|
||||||
|
alerts: [],
|
||||||
|
isOpen: true,
|
||||||
|
isSaving: false,
|
||||||
|
isResetting: false,
|
||||||
|
keys: ["recaptcha_enabled", "recaptcha_public_key", "recaptcha_private_key"]
|
||||||
|
},
|
||||||
uncategorised: {
|
uncategorised: {
|
||||||
alerts: [],
|
alerts: [],
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
@ -60,8 +67,9 @@ export default class Settings extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.hiddenKeys = [
|
this.hiddenKeys = [
|
||||||
"mail_password",
|
"recaptcha_private_key",
|
||||||
"jwt_secret"
|
"mail_password",
|
||||||
|
"jwt_secret"
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,7 +172,7 @@ export default class Settings extends React.Component {
|
|||||||
<div className={"card-header"} style={{cursor: "pointer"}}
|
<div className={"card-header"} style={{cursor: "pointer"}}
|
||||||
onClick={() => this.toggleCollapse(category)}>
|
onClick={() => this.toggleCollapse(category)}>
|
||||||
<h4 className={"card-title"}>
|
<h4 className={"card-title"}>
|
||||||
<Icon className={"mr-2"} icon={icon}/>
|
<Icon className={"mr-2"} icon={icon} type={icon==="google"?"fab":"fas"} />
|
||||||
{title}
|
{title}
|
||||||
</h4>
|
</h4>
|
||||||
<div className={"card-tools"}>
|
<div className={"card-tools"}>
|
||||||
@ -402,6 +410,49 @@ export default class Settings extends React.Component {
|
|||||||
return formGroups;
|
return formGroups;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getRecaptchaForm() {
|
||||||
|
return <>
|
||||||
|
<div className={"form-group mt-2"}>
|
||||||
|
<div className={"form-check"}>
|
||||||
|
<input type={"checkbox"} className={"form-check-input"}
|
||||||
|
name={"recaptcha_enabled"} id={"recaptcha_enabled"}
|
||||||
|
checked={(this.state.settings["recaptcha_enabled"] ?? "0") === "1"}
|
||||||
|
onChange={this.onChangeValue.bind(this)}/>
|
||||||
|
<label className={"form-check-label"} htmlFor={"recaptcha_enabled"}>
|
||||||
|
Enable Google's reCaptcha
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr className={"m-2"}/>
|
||||||
|
<label htmlFor={"recaptcha_public_key"} className={"mt-2"}>reCaptcha Site Key</label>
|
||||||
|
<div className={"input-group"}>
|
||||||
|
<div className={"input-group-prepend"}>
|
||||||
|
<span className={"input-group-text"}>
|
||||||
|
<Icon icon={"unlock"}/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<input type={"text"} className={"form-control"}
|
||||||
|
value={this.state.settings["recaptcha_public_key"] ?? ""}
|
||||||
|
placeholder={"Enter site key"} name={"recaptcha_public_key"}
|
||||||
|
id={"recaptcha_public_key"} onChange={this.onChangeValue.bind(this)}
|
||||||
|
disabled={(this.state.settings["recaptcha_enabled"] ?? "0") !== "1"}/>
|
||||||
|
</div>
|
||||||
|
<label htmlFor={"recaptcha_private_key"} className={"mt-2"}>reCaptcha Secret Key</label>
|
||||||
|
<div className={"input-group mb-3"}>
|
||||||
|
<div className={"input-group-prepend"}>
|
||||||
|
<span className={"input-group-text"}>
|
||||||
|
<Icon icon={"lock"}/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<input type={"password"} className={"form-control"}
|
||||||
|
value={this.state.settings["recaptcha_private_key"] ?? ""}
|
||||||
|
placeholder={"(unchanged)"} name={"recaptcha_private_key"}
|
||||||
|
id={"mail_password"} onChange={this.onChangeValue.bind(this)}
|
||||||
|
disabled={(this.state.settings["recaptcha_enabled"] ?? "0") !== "1"}/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
|
||||||
getUncategorizedForm() {
|
getUncategorizedForm() {
|
||||||
let tr = [];
|
let tr = [];
|
||||||
|
|
||||||
@ -463,6 +514,7 @@ export default class Settings extends React.Component {
|
|||||||
"general": {color: "primary", icon: "cogs", title: "General Settings", content: this.createGeneralForm()},
|
"general": {color: "primary", icon: "cogs", title: "General Settings", content: this.createGeneralForm()},
|
||||||
"mail": {color: "warning", icon: "envelope", title: "Mail Settings", content: this.createMailForm()},
|
"mail": {color: "warning", icon: "envelope", title: "Mail Settings", content: this.createMailForm()},
|
||||||
"messages": {color: "info", icon: "copy", title: "Message Templates", content: this.getMessagesForm()},
|
"messages": {color: "info", icon: "copy", title: "Message Templates", content: this.getMessagesForm()},
|
||||||
|
"recaptcha": {color: "danger", icon: "google", title: "Google reCaptcha", content: this.getRecaptchaForm()},
|
||||||
"uncategorised": {color: "secondary", icon: "stream", title: "Uncategorised", content: this.getUncategorizedForm()},
|
"uncategorised": {color: "secondary", icon: "stream", title: "Uncategorised", content: this.getUncategorizedForm()},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user