diff --git a/core/Api/ApiKey/Create.class.php b/core/Api/ApiKey/Create.class.php
deleted file mode 100644
index c5da1dd..0000000
--- a/core/Api/ApiKey/Create.class.php
+++ /dev/null
@@ -1,44 +0,0 @@
-apiKeyAllowed = false;
- $this->csrfTokenRequired = true;
- $this->loginRequired = true;
- }
-
- public function execute($values = array()) {
-
- if(!parent::execute($values)) {
- return false;
- }
-
- $apiKey = generateRandomString(64);
- $sql = $this->user->getSQL();
- $validUntil = (new \DateTime())->modify("+30 DAY");
-
- $this->success = $sql->insert("ApiKey", array("user_id", "api_key", "valid_until"))
- ->addRow($this->user->getId(), $apiKey, $validUntil)
- ->returning("uid")
- ->execute();
-
- $this->lastError = $sql->getLastError();
-
- if ($this->success) {
- $this->result["api_key"] = array(
- "api_key" => $apiKey,
- "valid_until" => $validUntil->getTimestamp(),
- "uid" => $sql->getLastInsertId(),
- );
- } else {
- $this->result["api_key"] = null;
- }
- return $this->success;
- }
-}
\ No newline at end of file
diff --git a/core/Api/ApiKey/Fetch.class.php b/core/Api/ApiKey/Fetch.class.php
deleted file mode 100644
index df231f2..0000000
--- a/core/Api/ApiKey/Fetch.class.php
+++ /dev/null
@@ -1,53 +0,0 @@
-loginRequired = true;
- $this->csrfTokenRequired = true;
- }
-
- public function execute($values = array()) {
- if(!parent::execute($values)) {
- return false;
- }
-
- $sql = $this->user->getSQL();
- $res = $sql->select("uid", "api_key", "valid_until")
- ->from("ApiKey")
- ->where(new Compare("user_id", $this->user->getId()))
- ->where(new Compare("valid_until", $sql->currentTimestamp(), ">"))
- ->where(new Compare("active", true))
- ->execute();
-
- $this->success = ($res !== FALSE);
- $this->lastError = $sql->getLastError();
-
- if($this->success) {
- $this->result["api_keys"] = array();
- foreach($res as $row) {
- try {
- $validUntil = (new DateTime($row["valid_until"]))->getTimestamp();
- } catch (Exception $e) {
- $validUntil = $row["valid_until"];
- }
-
- $this->result["api_keys"][] = array(
- "uid" => intval($row["uid"]),
- "api_key" => $row["api_key"],
- "valid_until" => $validUntil,
- );
- }
- }
-
- return $this->success;
- }
-}
\ No newline at end of file
diff --git a/core/Api/ApiKey/Refresh.class.php b/core/Api/ApiKey/Refresh.class.php
deleted file mode 100644
index 945c930..0000000
--- a/core/Api/ApiKey/Refresh.class.php
+++ /dev/null
@@ -1,66 +0,0 @@
- new Parameter("id", Parameter::TYPE_INT),
- ));
- $this->loginRequired = true;
- $this->csrfTokenRequired = true;
- }
-
- private function apiKeyExists() {
- $id = $this->getParam("id");
-
- $sql = $this->user->getSQL();
- $res = $sql->select($sql->count())
- ->from("ApiKey")
- ->where(new Compare("uid", $id))
- ->where(new Compare("user_id", $this->user->getId()))
- ->where(new Compare("valid_until", $sql->currentTimestamp(), ">"))
- ->where(new Compare("active", 1))
- ->execute();
-
- $this->success = ($res !== FALSE);
- $this->lastError = $sql->getLastError();
-
- if($this->success && $res[0]["count"] === 0) {
- $this->success = false;
- $this->lastError = "This API-Key does not exist.";
- }
-
- return $this->success;
- }
-
- public function execute($values = array()) {
- if(!parent::execute($values)) {
- return false;
- }
-
- $id = $this->getParam("id");
- if(!$this->apiKeyExists())
- return false;
-
- $validUntil = (new \DateTime)->modify("+30 DAY");
- $sql = $this->user->getSQL();
- $this->success = $sql->update("ApiKey")
- ->set("valid_until", $validUntil)
- ->where(new Compare("uid", $id))
- ->where(new Compare("user_id", $this->user->getId()))
- ->execute();
- $this->lastError = $sql->getLastError();
-
- if ($this->success) {
- $this->result["valid_until"] = $validUntil->getTimestamp();
- }
-
- return $this->success;
- }
-}
\ No newline at end of file
diff --git a/core/Api/ApiKey/Revoke.class.php b/core/Api/ApiKey/Revoke.class.php
deleted file mode 100644
index 2cb15bb..0000000
--- a/core/Api/ApiKey/Revoke.class.php
+++ /dev/null
@@ -1,61 +0,0 @@
- new Parameter("id", Parameter::TYPE_INT),
- ));
- $this->loginRequired = true;
- $this->csrfTokenRequired = true;
- }
-
- private function apiKeyExists() {
- $id = $this->getParam("id");
-
- $sql = $this->user->getSQL();
- $res = $sql->select($sql->count())
- ->from("ApiKey")
- ->where(new Compare("uid", $id))
- ->where(new Compare("user_id", $this->user->getId()))
- ->where(new Compare("valid_until", $sql->currentTimestamp(), ">"))
- ->where(new Compare("active", 1))
- ->execute();
-
- $this->success = ($res !== FALSE);
- $this->lastError = $sql->getLastError();
-
- if($this->success && $res[0]["count"] === 0) {
- $this->success = false;
- $this->lastError = "This API-Key does not exist.";
- }
-
- return $this->success;
- }
-
- public function execute($aValues = array()) {
- if(!parent::execute($aValues)) {
- return false;
- }
-
- $id = $this->getParam("id");
- if(!$this->apiKeyExists())
- return false;
-
- $sql = $this->user->getSQL();
- $this->success = $sql->update("ApiKey")
- ->set("active", false)
- ->where(new Compare("uid", $id))
- ->where(new Compare("user_id", $this->user->getId()))
- ->execute();
- $this->lastError = $sql->getLastError();
-
- return $this->success;
- }
-}
diff --git a/core/Api/ApiKeyAPI.class.php b/core/Api/ApiKeyAPI.class.php
new file mode 100644
index 0000000..45b3893
--- /dev/null
+++ b/core/Api/ApiKeyAPI.class.php
@@ -0,0 +1,191 @@
+user->getSQL();
+ $res = $sql->select($sql->count())
+ ->from("ApiKey")
+ ->where(new Compare("uid", $id))
+ ->where(new Compare("user_id", $this->user->getId()))
+ ->where(new Compare("valid_until", $sql->currentTimestamp(), ">"))
+ ->where(new Compare("active", 1))
+ ->execute();
+
+ $this->success = ($res !== FALSE);
+ $this->lastError = $sql->getLastError();
+
+ if($this->success && $res[0]["count"] === 0) {
+ return $this->createError("This API-Key does not exist.");
+ }
+
+ return $this->success;
+ }
+ }
+}
+
+namespace Api\ApiKey {
+
+ use Api\ApiKeyAPI;
+ use Api\Parameter\Parameter;
+ use DateTime;
+ use Driver\SQL\Condition\Compare;
+ use Exception;
+
+ class Create extends ApiKeyAPI {
+
+ public function __construct($user, $externalCall = false) {
+ parent::__construct($user, $externalCall, array());
+ $this->apiKeyAllowed = false;
+ $this->csrfTokenRequired = true;
+ $this->loginRequired = true;
+ }
+
+ public function execute($values = array()) {
+
+ if(!parent::execute($values)) {
+ return false;
+ }
+
+ $apiKey = generateRandomString(64);
+ $sql = $this->user->getSQL();
+ $validUntil = (new \DateTime())->modify("+30 DAY");
+
+ $this->success = $sql->insert("ApiKey", array("user_id", "api_key", "valid_until"))
+ ->addRow($this->user->getId(), $apiKey, $validUntil)
+ ->returning("uid")
+ ->execute();
+
+ $this->lastError = $sql->getLastError();
+
+ if ($this->success) {
+ $this->result["api_key"] = array(
+ "api_key" => $apiKey,
+ "valid_until" => $validUntil->getTimestamp(),
+ "uid" => $sql->getLastInsertId(),
+ );
+ } else {
+ $this->result["api_key"] = null;
+ }
+ return $this->success;
+ }
+ }
+
+ class Fetch extends ApiKeyAPI {
+
+ public function __construct($user, $externalCall = false) {
+ parent::__construct($user, $externalCall, array());
+ $this->loginRequired = true;
+ $this->csrfTokenRequired = true;
+ }
+
+ public function execute($values = array()) {
+ if(!parent::execute($values)) {
+ return false;
+ }
+
+ $sql = $this->user->getSQL();
+ $res = $sql->select("uid", "api_key", "valid_until")
+ ->from("ApiKey")
+ ->where(new Compare("user_id", $this->user->getId()))
+ ->where(new Compare("valid_until", $sql->currentTimestamp(), ">"))
+ ->where(new Compare("active", true))
+ ->execute();
+
+ $this->success = ($res !== FALSE);
+ $this->lastError = $sql->getLastError();
+
+ if($this->success) {
+ $this->result["api_keys"] = array();
+ foreach($res as $row) {
+ try {
+ $validUntil = (new DateTime($row["valid_until"]))->getTimestamp();
+ } catch (Exception $e) {
+ $validUntil = $row["valid_until"];
+ }
+
+ $this->result["api_keys"][] = array(
+ "uid" => intval($row["uid"]),
+ "api_key" => $row["api_key"],
+ "valid_until" => $validUntil,
+ );
+ }
+ }
+
+ return $this->success;
+ }
+ }
+
+ class Refresh extends ApiKeyAPI {
+
+ public function __construct($user, $externalCall = false) {
+ parent::__construct($user, $externalCall, array(
+ "id" => new Parameter("id", Parameter::TYPE_INT),
+ ));
+ $this->loginRequired = true;
+ $this->csrfTokenRequired = true;
+ }
+
+ public function execute($values = array()) {
+ if(!parent::execute($values)) {
+ return false;
+ }
+
+ $id = $this->getParam("id");
+ if(!$this->apiKeyExists($id))
+ return false;
+
+ $validUntil = (new \DateTime)->modify("+30 DAY");
+ $sql = $this->user->getSQL();
+ $this->success = $sql->update("ApiKey")
+ ->set("valid_until", $validUntil)
+ ->where(new Compare("uid", $id))
+ ->where(new Compare("user_id", $this->user->getId()))
+ ->execute();
+ $this->lastError = $sql->getLastError();
+
+ if ($this->success) {
+ $this->result["valid_until"] = $validUntil->getTimestamp();
+ }
+
+ return $this->success;
+ }
+ }
+
+ class Revoke extends ApiKeyAPI {
+
+ public function __construct($user, $externalCall = false) {
+ parent::__construct($user, $externalCall, array(
+ "id" => new Parameter("id", Parameter::TYPE_INT),
+ ));
+ $this->loginRequired = true;
+ $this->csrfTokenRequired = true;
+ }
+
+ public function execute($aValues = array()) {
+ if(!parent::execute($aValues)) {
+ return false;
+ }
+
+ $id = $this->getParam("id");
+ if(!$this->apiKeyExists($id))
+ return false;
+
+ $sql = $this->user->getSQL();
+ $this->success = $sql->update("ApiKey")
+ ->set("active", false)
+ ->where(new Compare("uid", $id))
+ ->where(new Compare("user_id", $this->user->getId()))
+ ->execute();
+ $this->lastError = $sql->getLastError();
+
+ return $this->success;
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/core/Api/GetLanguages.class.php b/core/Api/GetLanguages.class.php
deleted file mode 100644
index d4298c6..0000000
--- a/core/Api/GetLanguages.class.php
+++ /dev/null
@@ -1,37 +0,0 @@
-user->getSQL();
- $res = $sql->select("uid", "code", "name")
- ->from("Language")
- ->execute();
-
- $this->success = ($res !== FALSE);
- $this->lastError = $sql->getLastError();
-
- if($this->success) {
- $this->result['languages'] = array();
- if(empty($res) === 0) {
- $this->lastError = L("No languages found");
- } else {
- foreach($res as $row) {
- $this->result['languages'][$row['uid']] = $row;
- }
- }
- }
-
- return $this->success;
- }
-}
\ No newline at end of file
diff --git a/core/Api/Groups/Fetch.class.php b/core/Api/Groups/Fetch.class.php
deleted file mode 100644
index 6b7e41e..0000000
--- a/core/Api/Groups/Fetch.class.php
+++ /dev/null
@@ -1,84 +0,0 @@
- new Parameter('page', Parameter::TYPE_INT, true, 1)
- ));
-
- $this->loginRequired = true;
- $this->requiredGroup = USER_GROUP_ADMIN;
- $this->csrfTokenRequired = true;
- $this->groupCount = 0;
- }
-
- private function getGroupCount() {
-
- $sql = $this->user->getSQL();
- $res = $sql->select($sql->count())->from("Group")->execute();
- $this->success = ($res !== FALSE);
- $this->lastError = $sql->getLastError();
-
- if ($this->success) {
- $this->groupCount = $res[0]["count"];
- }
-
- return $this->success;
- }
-
- public function execute($values = array()) {
- if(!parent::execute($values)) {
- return false;
- }
-
- $page = $this->getParam("page");
- if($page < 1) {
- return $this->createError("Invalid page count");
- }
-
- if (!$this->getGroupCount()) {
- return false;
- }
-
- $sql = $this->user->getSQL();
- $res = $sql->select("Group.uid as groupId", "Group.name as groupName", $sql->count("UserGroup.user_id"))
- ->from("Group")
- ->innerJoin("UserGroup", "UserGroup.group_id", "Group.uid")
- ->groupBy("Group.uid")
- ->orderBy("Group.uid")
- ->ascending()
- ->limit(Fetch::SELECT_SIZE)
- ->offset(($page - 1) * Fetch::SELECT_SIZE)
- ->execute();
-
- $this->success = ($res !== FALSE);
- $this->lastError = $sql->getLastError();
-
- if($this->success) {
- $this->result["groups"] = array();
- foreach($res as $row) {
- $groupId = intval($row["groupId"]);
- $groupName = $row["groupName"];
- $memberCount = $row["usergroup_user_id_count"];
- $this->result["groups"][$groupId] = array(
- "name" => $groupName,
- "memberCount" => $memberCount
- );
- }
- $this->result["pageCount"] = intval(ceil($this->groupCount / Fetch::SELECT_SIZE));
- $this->result["totalCount"] = $this->groupCount;
- }
-
- return $this->success;
- }
-}
diff --git a/core/Api/GroupsAPI.class.php b/core/Api/GroupsAPI.class.php
new file mode 100644
index 0000000..d524243
--- /dev/null
+++ b/core/Api/GroupsAPI.class.php
@@ -0,0 +1,94 @@
+ new Parameter('page', Parameter::TYPE_INT, true, 1)
+ ));
+
+ $this->loginRequired = true;
+ $this->requiredGroup = USER_GROUP_ADMIN;
+ $this->csrfTokenRequired = true;
+ $this->groupCount = 0;
+ }
+
+ private function getGroupCount() {
+
+ $sql = $this->user->getSQL();
+ $res = $sql->select($sql->count())->from("Group")->execute();
+ $this->success = ($res !== FALSE);
+ $this->lastError = $sql->getLastError();
+
+ if ($this->success) {
+ $this->groupCount = $res[0]["count"];
+ }
+
+ return $this->success;
+ }
+
+ public function execute($values = array()) {
+ if(!parent::execute($values)) {
+ return false;
+ }
+
+ $page = $this->getParam("page");
+ if($page < 1) {
+ return $this->createError("Invalid page count");
+ }
+
+ if (!$this->getGroupCount()) {
+ return false;
+ }
+
+ $sql = $this->user->getSQL();
+ $res = $sql->select("Group.uid as groupId", "Group.name as groupName", $sql->count("UserGroup.user_id"))
+ ->from("Group")
+ ->innerJoin("UserGroup", "UserGroup.group_id", "Group.uid")
+ ->groupBy("Group.uid")
+ ->orderBy("Group.uid")
+ ->ascending()
+ ->limit(Fetch::SELECT_SIZE)
+ ->offset(($page - 1) * Fetch::SELECT_SIZE)
+ ->execute();
+
+ $this->success = ($res !== FALSE);
+ $this->lastError = $sql->getLastError();
+
+ if($this->success) {
+ $this->result["groups"] = array();
+ foreach($res as $row) {
+ $groupId = intval($row["groupId"]);
+ $groupName = $row["groupName"];
+ $memberCount = $row["usergroup_user_id_count"];
+ $this->result["groups"][$groupId] = array(
+ "name" => $groupName,
+ "memberCount" => $memberCount
+ );
+ }
+ $this->result["pageCount"] = intval(ceil($this->groupCount / Fetch::SELECT_SIZE));
+ $this->result["totalCount"] = $this->groupCount;
+ }
+
+ return $this->success;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/core/Api/LanguageAPI.class.php b/core/Api/LanguageAPI.class.php
new file mode 100644
index 0000000..1ddce94
--- /dev/null
+++ b/core/Api/LanguageAPI.class.php
@@ -0,0 +1,128 @@
+user->getSQL();
+ $res = $sql->select("uid", "code", "name")
+ ->from("Language")
+ ->execute();
+
+ $this->success = ($res !== FALSE);
+ $this->lastError = $sql->getLastError();
+
+ if($this->success) {
+ $this->result['languages'] = array();
+ if(empty($res) === 0) {
+ $this->lastError = L("No languages found");
+ } else {
+ foreach($res as $row) {
+ $this->result['languages'][$row['uid']] = $row;
+ }
+ }
+ }
+
+ return $this->success;
+ }
+ }
+
+ class Set extends LanguageAPI {
+
+ private Language $language;
+
+ public function __construct($user, $externalCall = false) {
+ parent::__construct($user, $externalCall, array(
+ 'langId' => new Parameter('langId', Parameter::TYPE_INT, true, NULL),
+ 'langCode' => new StringType('langCode', 5, true, NULL),
+ ));
+ $this->csrfTokenRequired = true;
+ }
+
+ private function checkLanguage() {
+ $langId = $this->getParam("langId");
+ $langCode = $this->getParam("langCode");
+
+ if(is_null($langId) && is_null($langCode)) {
+ return $this->createError(L("Either langId or langCode must be given"));
+ }
+
+ $res = $this->user->getSQL()
+ ->select("uid", "code", "name")
+ ->from("Language")
+ ->where(new CondOr(new Compare("uid", $langId), new Compare("code", $langCode)))
+ ->execute();
+
+ $this->success = ($res !== FALSE);
+ $this->lastError = $this->user->getSQL()->getLastError();
+
+ if ($this->success) {
+ if(count($res) == 0) {
+ return $this->createError(L("This Language does not exist"));
+ } else {
+ $row = $res[0];
+ $this->language = Language::newInstance($row['uid'], $row['code'], $row['name']);
+ if(!$this->language) {
+ return $this->createError(L("Error while loading language"));
+ }
+ }
+ }
+
+ return $this->success;
+ }
+
+ private function updateLanguage() {
+ $languageId = $this->language->getId();
+ $userId = $this->user->getId();
+ $sql = $this->user->getSQL();
+
+ $this->success = $sql->update("User")
+ ->set("language_id", $languageId)
+ ->where(new Compare("uid", $userId))
+ ->execute();
+ $this->lastError = $sql->getLastError();
+ return $this->success;
+ }
+
+ public function execute($values = array()) {
+ if(!parent::execute($values)) {
+ return false;
+ }
+
+ if(!$this->checkLanguage())
+ return false;
+
+ if($this->user->isLoggedIn()) {
+ $this->updateLanguage();
+ }
+
+ $this->user->setLanguage($this->language);
+ return $this->success;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/core/Api/Notifications/Create.class.php b/core/Api/Notifications/Create.class.php
deleted file mode 100644
index 3a84338..0000000
--- a/core/Api/Notifications/Create.class.php
+++ /dev/null
@@ -1,134 +0,0 @@
- new Parameter('groupId', Parameter::TYPE_INT, true),
- 'userId' => new Parameter('userId', Parameter::TYPE_INT, true),
- 'title' => new StringType('title', 32),
- 'message' => new StringType('message', 256),
- ));
- $this->isPublic = false;
- $this->csrfTokenRequired = true;
- }
-
- private function checkUser($userId) {
- $sql = $this->user->getSQL();
- $res = $sql->select($sql->count())
- ->from("User")
- ->where(new Compare("uid", $userId))
- ->execute();
-
- $this->success = ($res !== FALSE);
- $this->lastError = $sql->getLastError();
-
- if ($this->success) {
- if ($res[0]["count"] == 0) {
- $this->success = false;
- $this->lastError = "User not found";
- }
- }
-
- return $this->success;
- }
-
- private function insertUserNotification($userId, $notificationId) {
- $sql = $this->user->getSQL();
- $res = $sql->insert("UserNotification", array("user_id", "notification_id"))
- ->addRow($userId, $notificationId)
- ->execute();
-
- $this->success = ($res !== FALSE);
- $this->lastError = $sql->getLastError();
- return $this->success;
- }
-
- private function checkGroup($groupId) {
- $sql = $this->user->getSQL();
- $res = $sql->select($sql->count())
- ->from("Group")
- ->where(new Compare("uid", $groupId))
- ->execute();
-
- $this->success = ($res !== FALSE);
- $this->lastError = $sql->getLastError();
-
- if ($this->success) {
- if ($res[0]["count"] == 0) {
- $this->success = false;
- $this->lastError = "Group not found";
- }
- }
-
- return $this->success;
- }
-
- private function insertGroupNotification($groupId, $notificationId) {
- $sql = $this->user->getSQL();
- $res = $sql->insert("GroupNotification", array("group_id", "notification_id"))
- ->addRow($groupId, $notificationId)
- ->execute();
-
- $this->success = ($res !== FALSE);
- $this->lastError = $sql->getLastError();
- return $this->success;
- }
-
- private function createNotification($title, $message) {
- $sql = $this->user->getSQL();
- $res = $sql->insert("Notification", array("title", "message"))
- ->addRow($title, $message)
- ->returning("uid")
- ->execute();
-
- $this->success = ($res !== FALSE);
- $this->lastError = $sql->getLastError();
-
- if ($this->success) {
- return $sql->getLastInsertId();
- }
-
- return $this->success;
- }
-
- public function execute($values = array()) {
- if(!parent::execute($values)) {
- return false;
- }
-
- $userId = $this->getParam("userId");
- $groupId = $this->getParam("groupId");
- $title = $this->getParam("title");
- $message = $this->getParam("message");
-
- if (is_null($userId) && is_null($groupId)) {
- return $this->createError("Either userId or groupId must be specified.");
- } else if(!is_null($userId) && !is_null($groupId)) {
- return $this->createError("Only one of userId and groupId must be specified.");
- } else if(!is_null($userId)) {
- if ($this->checkUser($userId)) {
- $id = $this->createNotification($title, $message);
- if ($this->success) {
- return $this->insertUserNotification($userId, $id);
- }
- }
- } else if(!is_null($groupId)) {
- if ($this->checkGroup($groupId)) {
- $id = $this->createNotification($title, $message);
- if ($this->success) {
- return $this->insertGroupNotification($groupId, $id);
- }
- }
- }
-
- return $this->success;
- }
-}
\ No newline at end of file
diff --git a/core/Api/Notifications/Fetch.class.php b/core/Api/Notifications/Fetch.class.php
deleted file mode 100644
index 78836fe..0000000
--- a/core/Api/Notifications/Fetch.class.php
+++ /dev/null
@@ -1,93 +0,0 @@
-loginRequired = true;
- $this->csrfTokenRequired = true;
- }
-
- private function fetchUserNotifications() {
- $userId = $this->user->getId();
- $sql = $this->user->getSQL();
- $res = $sql->select($sql->distinct("Notification.uid"), "created_at", "title", "message")
- ->from("Notification")
- ->innerJoin("UserNotification", "UserNotification.notification_id", "Notification.uid")
- ->where(new Compare("UserNotification.user_id", $userId))
- ->where(new Compare("UserNotification.seen", false))
- ->orderBy("created_at")->descending()
- ->execute();
-
- $this->success = ($res !== FALSE);
- $this->lastError = $sql->getLastError();
-
- 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;
- }
-
- private function fetchGroupNotifications() {
- $userId = $this->user->getId();
- $sql = $this->user->getSQL();
- $res = $sql->select($sql->distinct("Notification.uid"), "created_at", "title", "message")
- ->from("Notification")
- ->innerJoin("GroupNotification", "GroupNotification.notification_id", "Notification.uid")
- ->innerJoin("UserGroup", "GroupNotification.group_id", "UserGroup.group_id")
- ->where(new Compare("UserGroup.user_id", $userId))
- ->where(new Compare("GroupNotification.seen", false))
- ->orderBy("created_at")->descending()
- ->execute();
-
- $this->success = ($res !== FALSE);
- $this->lastError = $sql->getLastError();
-
- if ($this->success) {
- foreach($res as $row) {
- $id = $row["uid"];
- if (!isset($this->notifications[$id])) {
- $this->notifications[] = array(
- "uid" => $id,
- "title" => $row["title"],
- "message" => $row["message"],
- "created_at" => $row["created_at"],
- );
- }
- }
- }
-
- return $this->success;
- }
-
- public function execute($values = array()) {
- if(!parent::execute($values)) {
- return false;
- }
-
- $this->notifications = array();
- if ($this->fetchUserNotifications() && $this->fetchGroupNotifications()) {
- $this->result["notifications"] = $this->notifications;
- }
-
- return $this->success;
- }
-}
\ No newline at end of file
diff --git a/core/Api/NotificationsAPI.class.php b/core/Api/NotificationsAPI.class.php
new file mode 100644
index 0000000..909eed5
--- /dev/null
+++ b/core/Api/NotificationsAPI.class.php
@@ -0,0 +1,231 @@
+ new Parameter('groupId', Parameter::TYPE_INT, true),
+ 'userId' => new Parameter('userId', Parameter::TYPE_INT, true),
+ 'title' => new StringType('title', 32),
+ 'message' => new StringType('message', 256),
+ ));
+ $this->isPublic = false;
+ $this->csrfTokenRequired = true;
+ }
+
+ private function checkUser($userId) {
+ $sql = $this->user->getSQL();
+ $res = $sql->select($sql->count())
+ ->from("User")
+ ->where(new Compare("uid", $userId))
+ ->execute();
+
+ $this->success = ($res !== FALSE);
+ $this->lastError = $sql->getLastError();
+
+ if ($this->success) {
+ if ($res[0]["count"] == 0) {
+ $this->success = false;
+ $this->lastError = "User not found";
+ }
+ }
+
+ return $this->success;
+ }
+
+ private function insertUserNotification($userId, $notificationId) {
+ $sql = $this->user->getSQL();
+ $res = $sql->insert("UserNotification", array("user_id", "notification_id"))
+ ->addRow($userId, $notificationId)
+ ->execute();
+
+ $this->success = ($res !== FALSE);
+ $this->lastError = $sql->getLastError();
+ return $this->success;
+ }
+
+ private function checkGroup($groupId) {
+ $sql = $this->user->getSQL();
+ $res = $sql->select($sql->count())
+ ->from("Group")
+ ->where(new Compare("uid", $groupId))
+ ->execute();
+
+ $this->success = ($res !== FALSE);
+ $this->lastError = $sql->getLastError();
+
+ if ($this->success) {
+ if ($res[0]["count"] == 0) {
+ $this->success = false;
+ $this->lastError = "Group not found";
+ }
+ }
+
+ return $this->success;
+ }
+
+ private function insertGroupNotification($groupId, $notificationId) {
+ $sql = $this->user->getSQL();
+ $res = $sql->insert("GroupNotification", array("group_id", "notification_id"))
+ ->addRow($groupId, $notificationId)
+ ->execute();
+
+ $this->success = ($res !== FALSE);
+ $this->lastError = $sql->getLastError();
+ return $this->success;
+ }
+
+ private function createNotification($title, $message) {
+ $sql = $this->user->getSQL();
+ $res = $sql->insert("Notification", array("title", "message"))
+ ->addRow($title, $message)
+ ->returning("uid")
+ ->execute();
+
+ $this->success = ($res !== FALSE);
+ $this->lastError = $sql->getLastError();
+
+ if ($this->success) {
+ return $sql->getLastInsertId();
+ }
+
+ return $this->success;
+ }
+
+ public function execute($values = array()) {
+ if(!parent::execute($values)) {
+ return false;
+ }
+
+ $userId = $this->getParam("userId");
+ $groupId = $this->getParam("groupId");
+ $title = $this->getParam("title");
+ $message = $this->getParam("message");
+
+ if (is_null($userId) && is_null($groupId)) {
+ return $this->createError("Either userId or groupId must be specified.");
+ } else if(!is_null($userId) && !is_null($groupId)) {
+ return $this->createError("Only one of userId and groupId must be specified.");
+ } else if(!is_null($userId)) {
+ if ($this->checkUser($userId)) {
+ $id = $this->createNotification($title, $message);
+ if ($this->success) {
+ return $this->insertUserNotification($userId, $id);
+ }
+ }
+ } else if(!is_null($groupId)) {
+ if ($this->checkGroup($groupId)) {
+ $id = $this->createNotification($title, $message);
+ if ($this->success) {
+ return $this->insertGroupNotification($groupId, $id);
+ }
+ }
+ }
+
+ return $this->success;
+ }
+ }
+
+ class Fetch extends NotificationsAPI {
+
+ private array $notifications;
+
+ public function __construct($user, $externalCall = false) {
+ parent::__construct($user, $externalCall, array());
+ $this->loginRequired = true;
+ $this->csrfTokenRequired = true;
+ }
+
+ private function fetchUserNotifications() {
+ $userId = $this->user->getId();
+ $sql = $this->user->getSQL();
+ $res = $sql->select($sql->distinct("Notification.uid"), "created_at", "title", "message")
+ ->from("Notification")
+ ->innerJoin("UserNotification", "UserNotification.notification_id", "Notification.uid")
+ ->where(new Compare("UserNotification.user_id", $userId))
+ ->where(new Compare("UserNotification.seen", false))
+ ->orderBy("created_at")->descending()
+ ->execute();
+
+ $this->success = ($res !== FALSE);
+ $this->lastError = $sql->getLastError();
+
+ 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;
+ }
+
+ private function fetchGroupNotifications() {
+ $userId = $this->user->getId();
+ $sql = $this->user->getSQL();
+ $res = $sql->select($sql->distinct("Notification.uid"), "created_at", "title", "message")
+ ->from("Notification")
+ ->innerJoin("GroupNotification", "GroupNotification.notification_id", "Notification.uid")
+ ->innerJoin("UserGroup", "GroupNotification.group_id", "UserGroup.group_id")
+ ->where(new Compare("UserGroup.user_id", $userId))
+ ->where(new Compare("GroupNotification.seen", false))
+ ->orderBy("created_at")->descending()
+ ->execute();
+
+ $this->success = ($res !== FALSE);
+ $this->lastError = $sql->getLastError();
+
+ if ($this->success) {
+ foreach($res as $row) {
+ $id = $row["uid"];
+ if (!isset($this->notifications[$id])) {
+ $this->notifications[] = array(
+ "uid" => $id,
+ "title" => $row["title"],
+ "message" => $row["message"],
+ "created_at" => $row["created_at"],
+ );
+ }
+ }
+ }
+
+ return $this->success;
+ }
+
+ public function execute($values = array()) {
+ if(!parent::execute($values)) {
+ return false;
+ }
+
+ $this->notifications = array();
+ if ($this->fetchUserNotifications() && $this->fetchGroupNotifications()) {
+ $this->result["notifications"] = $this->notifications;
+ }
+
+ return $this->success;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/core/Api/Routes/Fetch.class.php b/core/Api/Routes/Fetch.class.php
deleted file mode 100644
index 3462263..0000000
--- a/core/Api/Routes/Fetch.class.php
+++ /dev/null
@@ -1,51 +0,0 @@
-loginRequired = true;
- $this->csrfTokenRequired = true;
- }
-
- public function execute($values = array()) {
- if(!parent::execute($values)) {
- return false;
- }
-
- $sql = $this->user->getSQL();
-
- $res = $sql
- ->select("uid", "request", "action", "target", "extra", "active")
- ->from("Route")
- ->orderBy("uid")
- ->ascending()
- ->execute();
-
- $this->lastError = $sql->getLastError();
- $this->success = ($res !== FALSE);
-
- if ($this->success) {
- $routes = array();
- foreach($res as $row) {
- $routes[] = array(
- "uid" => intval($row["uid"]),
- "request" => $row["request"],
- "action" => $row["action"],
- "target" => $row["target"],
- "extra" => $row["extra"] ?? "",
- "active" => intval($row["active"]),
- );
- }
-
- $this->result["routes"] = $routes;
- }
-
- return $this->success;
- }
-}
\ No newline at end of file
diff --git a/core/Api/Routes/Find.class.php b/core/Api/Routes/Find.class.php
deleted file mode 100644
index 249a246..0000000
--- a/core/Api/Routes/Find.class.php
+++ /dev/null
@@ -1,61 +0,0 @@
- new StringType('request', 128, true, '/')
- ));
-
- $this->isPublic = false;
- }
-
- public function execute($values = array()) {
- if(!parent::execute($values)) {
- return false;
- }
-
- $request = $this->getParam('request');
- if (!startsWith($request, '/')) {
- $request = "/$request";
- }
-
- $sql = $this->user->getSQL();
-
- $res = $sql
- ->select("uid", "request", "action", "target", "extra")
- ->from("Route")
- ->where(new CondBool("active"))
- ->where(new Regex($request, new Column("request")))
- ->limit(1)
- ->execute();
-
- $this->lastError = $sql->getLastError();
- $this->success = ($res !== FALSE);
-
- if ($this->success) {
- if (!empty($res)) {
- $row = $res[0];
- $this->result["route"] = array(
- "uid" => intval($row["uid"]),
- "request" => $row["request"],
- "action" => $row["action"],
- "target" => $row["target"],
- "extra" => $row["extra"]
- );
- } else {
- $this->result["route"] = NULL;
- }
- }
-
- return $this->success;
- }
-}
\ No newline at end of file
diff --git a/core/Api/Routes/Save.class.php b/core/Api/Routes/Save.class.php
deleted file mode 100644
index f32616b..0000000
--- a/core/Api/Routes/Save.class.php
+++ /dev/null
@@ -1,99 +0,0 @@
- new Parameter('routes',Parameter::TYPE_ARRAY, false)
- ));
-
- $this->loginRequired = true;
- $this->csrfTokenRequired = true;
- $this->requiredGroup = USER_GROUP_ADMIN;
- }
-
- public function execute($values = array()) {
- if(!parent::execute($values)) {
- return false;
- }
-
- if (!$this->validateRoutes()) {
- return false;
- }
-
-
- $sql = $this->user->getSQL();
-
- // DELETE old rules
- $this->success = ($sql->truncate("Route")->execute() !== FALSE);
- $this->lastError = $sql->getLastError();
-
- // INSERT new routes
- if ($this->success) {
- $stmt = $sql->insert("Route", array("request", "action", "target", "extra", "active"));
- foreach($this->routes as $route) {
- $stmt->addRow($route["request"], $route["action"], $route["target"], $route["extra"], $route["active"]);
- }
- $this->success = ($stmt->execute() !== FALSE);
- $this->lastError = $sql->getLastError();
- }
-
- return $this->success;
- }
-
- private function validateRoutes() {
-
- $this->routes = array();
- $keys = array(
- "request" => Parameter::TYPE_STRING,
- "action" => Parameter::TYPE_STRING,
- "target" => Parameter::TYPE_STRING,
- "extra" => Parameter::TYPE_STRING,
- "active" => Parameter::TYPE_BOOLEAN
- );
-
- $actions = array(
- "redirect_temporary", "redirect_permanently", "static", "dynamic"
- );
-
- foreach($this->getParam("routes") as $index => $route) {
- foreach($keys as $key => $expectedType) {
- if (!array_key_exists($key, $route)) {
- return $this->createError("Route $index missing key: $key");
- }
-
- $value = $route[$key];
- $type = Parameter::parseType($value);
- if ($type !== $expectedType && ($key !== "active" || !is_null($value))) {
- $expectedTypeName = Parameter::names[$expectedType];
- $gotTypeName = Parameter::names[$type];
- return $this->createError("Route $index has invalid value for key: $key, expected: $expectedTypeName, got: $gotTypeName");
- }
- }
-
- $action = $route["action"];
- if (!in_array($action, $actions)) {
- return $this->createError("Invalid action: $action");
- }
-
- if(empty($route["request"])) {
- return $this->createError("Request cannot be empty.");
- }
-
- if(empty($route["target"])) {
- return $this->createError("Target cannot be empty.");
- }
-
- $this->routes[] = $route;
- }
-
- return true;
- }
-}
\ No newline at end of file
diff --git a/core/Api/RoutesAPI.class.php b/core/Api/RoutesAPI.class.php
new file mode 100644
index 0000000..bf00240
--- /dev/null
+++ b/core/Api/RoutesAPI.class.php
@@ -0,0 +1,208 @@
+loginRequired = true;
+ $this->csrfTokenRequired = true;
+ }
+
+ public function execute($values = array()) {
+ if(!parent::execute($values)) {
+ return false;
+ }
+
+ $sql = $this->user->getSQL();
+
+ $res = $sql
+ ->select("uid", "request", "action", "target", "extra", "active")
+ ->from("Route")
+ ->orderBy("uid")
+ ->ascending()
+ ->execute();
+
+ $this->lastError = $sql->getLastError();
+ $this->success = ($res !== FALSE);
+
+ if ($this->success) {
+ $routes = array();
+ foreach($res as $row) {
+ $routes[] = array(
+ "uid" => intval($row["uid"]),
+ "request" => $row["request"],
+ "action" => $row["action"],
+ "target" => $row["target"],
+ "extra" => $row["extra"] ?? "",
+ "active" => intval($row["active"]),
+ );
+ }
+
+ $this->result["routes"] = $routes;
+ }
+
+ return $this->success;
+ }
+}
+
+ class Find extends RoutesAPI {
+
+ public function __construct($user, $externalCall = false) {
+ parent::__construct($user, $externalCall, array(
+ 'request' => new StringType('request', 128, true, '/')
+ ));
+
+ $this->isPublic = false;
+ }
+
+ public function execute($values = array()) {
+ if(!parent::execute($values)) {
+ return false;
+ }
+
+ $request = $this->getParam('request');
+ if (!startsWith($request, '/')) {
+ $request = "/$request";
+ }
+
+ $sql = $this->user->getSQL();
+
+ $res = $sql
+ ->select("uid", "request", "action", "target", "extra")
+ ->from("Route")
+ ->where(new CondBool("active"))
+ ->where(new Regex($request, new Column("request")))
+ ->limit(1)
+ ->execute();
+
+ $this->lastError = $sql->getLastError();
+ $this->success = ($res !== FALSE);
+
+ if ($this->success) {
+ if (!empty($res)) {
+ $row = $res[0];
+ $this->result["route"] = array(
+ "uid" => intval($row["uid"]),
+ "request" => $row["request"],
+ "action" => $row["action"],
+ "target" => $row["target"],
+ "extra" => $row["extra"]
+ );
+ } else {
+ $this->result["route"] = NULL;
+ }
+ }
+
+ return $this->success;
+ }
+ }
+
+ class Save extends RoutesAPI {
+
+ private array $routes;
+
+ public function __construct($user, $externalCall = false) {
+ parent::__construct($user, $externalCall, array(
+ 'routes' => new Parameter('routes',Parameter::TYPE_ARRAY, false)
+ ));
+
+ $this->loginRequired = true;
+ $this->csrfTokenRequired = true;
+ $this->requiredGroup = USER_GROUP_ADMIN;
+ }
+
+ public function execute($values = array()) {
+ if(!parent::execute($values)) {
+ return false;
+ }
+
+ if (!$this->validateRoutes()) {
+ return false;
+ }
+
+
+ $sql = $this->user->getSQL();
+
+ // DELETE old rules
+ $this->success = ($sql->truncate("Route")->execute() !== FALSE);
+ $this->lastError = $sql->getLastError();
+
+ // INSERT new routes
+ if ($this->success) {
+ $stmt = $sql->insert("Route", array("request", "action", "target", "extra", "active"));
+ foreach($this->routes as $route) {
+ $stmt->addRow($route["request"], $route["action"], $route["target"], $route["extra"], $route["active"]);
+ }
+ $this->success = ($stmt->execute() !== FALSE);
+ $this->lastError = $sql->getLastError();
+ }
+
+ return $this->success;
+ }
+
+ private function validateRoutes() {
+
+ $this->routes = array();
+ $keys = array(
+ "request" => Parameter::TYPE_STRING,
+ "action" => Parameter::TYPE_STRING,
+ "target" => Parameter::TYPE_STRING,
+ "extra" => Parameter::TYPE_STRING,
+ "active" => Parameter::TYPE_BOOLEAN
+ );
+
+ $actions = array(
+ "redirect_temporary", "redirect_permanently", "static", "dynamic"
+ );
+
+ foreach($this->getParam("routes") as $index => $route) {
+ foreach($keys as $key => $expectedType) {
+ if (!array_key_exists($key, $route)) {
+ return $this->createError("Route $index missing key: $key");
+ }
+
+ $value = $route[$key];
+ $type = Parameter::parseType($value);
+ if ($type !== $expectedType && ($key !== "active" || !is_null($value))) {
+ $expectedTypeName = Parameter::names[$expectedType];
+ $gotTypeName = Parameter::names[$type];
+ return $this->createError("Route $index has invalid value for key: $key, expected: $expectedTypeName, got: $gotTypeName");
+ }
+ }
+
+ $action = $route["action"];
+ if (!in_array($action, $actions)) {
+ return $this->createError("Invalid action: $action");
+ }
+
+ if(empty($route["request"])) {
+ return $this->createError("Request cannot be empty.");
+ }
+
+ if(empty($route["target"])) {
+ return $this->createError("Target cannot be empty.");
+ }
+
+ $this->routes[] = $route;
+ }
+
+ return true;
+ }
+ }
+
+}
+
diff --git a/core/Api/SetLanguage.class.php b/core/Api/SetLanguage.class.php
deleted file mode 100644
index f956f0c..0000000
--- a/core/Api/SetLanguage.class.php
+++ /dev/null
@@ -1,83 +0,0 @@
- new Parameter('langId', Parameter::TYPE_INT, true, NULL),
- 'langCode' => new StringType('langCode', 5, true, NULL),
- ));
- $this->csrfTokenRequired = true;
- }
-
- private function checkLanguage() {
- $langId = $this->getParam("langId");
- $langCode = $this->getParam("langCode");
-
- if(is_null($langId) && is_null($langCode)) {
- return $this->createError(L("Either langId or langCode must be given"));
- }
-
- $res = $this->user->getSQL()
- ->select("uid", "code", "name")
- ->from("Language")
- ->where(new CondOr(new Compare("uid", $langId), new Compare("code", $langCode)))
- ->execute();
-
- $this->success = ($res !== FALSE);
- $this->lastError = $this->user->getSQL()->getLastError();
-
- if ($this->success) {
- if(count($res) == 0) {
- return $this->createError(L("This Language does not exist"));
- } else {
- $row = $res[0];
- $this->language = Language::newInstance($row['uid'], $row['code'], $row['name']);
- if(!$this->language) {
- return $this->createError(L("Error while loading language"));
- }
- }
- }
-
- return $this->success;
- }
-
- private function updateLanguage() {
- $languageId = $this->language->getId();
- $userId = $this->user->getId();
- $sql = $this->user->getSQL();
-
- $this->success = $sql->update("User")
- ->set("language_id", $languageId)
- ->where(new Compare("uid", $userId))
- ->execute();
- $this->lastError = $sql->getLastError();
- return $this->success;
- }
-
- public function execute($values = array()) {
- if(!parent::execute($values)) {
- return false;
- }
-
- if(!$this->checkLanguage())
- return false;
-
- if($this->user->isLoggedIn()) {
- $this->updateLanguage();
- }
-
- $this->user->setLanguage($this->language);
- return $this->success;
- }
-}
\ No newline at end of file
diff --git a/core/Api/User/Create.class.php b/core/Api/User/Create.class.php
deleted file mode 100644
index 94175e6..0000000
--- a/core/Api/User/Create.class.php
+++ /dev/null
@@ -1,78 +0,0 @@
- new StringType('username', 32),
- 'email' => new StringType('email', 64, true),
- 'password' => new StringType('password'),
- 'confirmPassword' => new StringType('confirmPassword'),
- ));
- $this->csrfTokenRequired = true;
- $this->loginRequired = true;
- $this->requiredGroup = USER_GROUP_ADMIN;
- }
-
- public function execute($values = array()) {
- if (!parent::execute($values)) {
- return false;
- }
-
- $username = $this->getParam('username');
- $email = $this->getParam('email');
- if (!$this->userExists($username, $email) || !$this->success) {
- return false;
- }
-
- $password = $this->getParam('password');
- $confirmPassword = $this->getParam('confirmPassword');
- if ($password !== $confirmPassword) {
- return $this->createError("The given passwords do not match.");
- }
-
- $this->success = $this->createUser($username, $email, $password);
- return $this->success;
- }
-
- private function userExists($username, $email) {
- $sql = $this->user->getSQL();
- $res = $sql->select("User.name", "User.email")
- ->from("User")
- ->where(new Compare("User.name", $username), new Compare("User.email", $email))
- ->execute();
-
- $this->success = ($res !== FALSE);
- $this->lastError = $sql->getLastError();
-
- if ($this->success && !empty($res)) {
- $row = $res[0];
- if (strcasecmp($username, $row['name']) === 0) {
- return $this->createError("This username is already taken.");
- } else if (strcasecmp($username, $row['email']) === 0) {
- return $this->createError("This email address is already in use.");
- }
- }
-
- return $this->success;
- }
-
- private function createUser($username, $email, $password) {
- $sql = $this->user->getSQL();
- $salt = generateRandomString(16);
- $hash = hash('sha256', $password . $salt);
- $res = $sql->insert("User", array("name", "password", "salt", "email"))
- ->addRow($username, $hash, $salt, $email)
- ->execute();
-
- $this->lastError = $sql->getLastError();
- $this->success = ($res !== FALSE);
- return $this->success;
- }
-}
\ No newline at end of file
diff --git a/core/Api/User/Fetch.class.php b/core/Api/User/Fetch.class.php
deleted file mode 100644
index 6d886f0..0000000
--- a/core/Api/User/Fetch.class.php
+++ /dev/null
@@ -1,95 +0,0 @@
- new Parameter('page', Parameter::TYPE_INT, true, 1)
- ));
-
- $this->loginRequired = true;
- $this->requiredGroup = USER_GROUP_ADMIN;
- $this->userCount = 0;
- $this->csrfTokenRequired = true;
- }
-
- private function getUserCount() {
-
- $sql = $this->user->getSQL();
- $res = $sql->select($sql->count())->from("User")->execute();
- $this->success = ($res !== FALSE);
- $this->lastError = $sql->getLastError();
-
- if ($this->success) {
- $this->userCount = $res[0]["count"];
- }
-
- return $this->success;
- }
-
- public function execute($values = array()) {
- if(!parent::execute($values)) {
- return false;
- }
-
- $page = $this->getParam("page");
- if($page < 1) {
- return $this->createError("Invalid page count");
- }
-
- if (!$this->getUserCount()) {
- return false;
- }
-
- $sql = $this->user->getSQL();
- $res = $sql->select("User.uid as userId", "User.name", "User.email", "User.registered_at",
- "Group.uid as groupId", "Group.name as groupName")
- ->from("User")
- ->leftJoin("UserGroup", "User.uid", "UserGroup.user_id")
- ->leftJoin("Group", "Group.uid", "UserGroup.group_id")
- ->orderBy("User.uid")
- ->ascending()
- ->limit(Fetch::SELECT_SIZE)
- ->offset(($page - 1) * Fetch::SELECT_SIZE)
- ->execute();
-
- $this->success = ($res !== FALSE);
- $this->lastError = $sql->getLastError();
-
- if($this->success) {
- $this->result["users"] = array();
- foreach($res as $row) {
- $userId = intval($row["userId"]);
- $groupId = intval($row["groupId"]);
- $groupName = $row["groupName"];
- if (!isset($this->result["users"][$userId])) {
- $this->result["users"][$userId] = array(
- "uid" => $userId,
- "name" => $row["name"],
- "email" => $row["email"],
- "registered_at" => $row["registered_at"],
- "groups" => array(),
- );
- }
-
- if(!is_null($groupId)) {
- $this->result["users"][$userId]["groups"][$groupId] = $groupName;
- }
- }
- $this->result["pageCount"] = intval(ceil($this->userCount / Fetch::SELECT_SIZE));
- $this->result["totalCount"] = $this->userCount;
- }
-
- return $this->success;
- }
-}
diff --git a/core/Api/User/Info.class.php b/core/Api/User/Info.class.php
deleted file mode 100644
index 0ea8eba..0000000
--- a/core/Api/User/Info.class.php
+++ /dev/null
@@ -1,28 +0,0 @@
-csrfTokenRequired = true;
- }
-
- public function execute($values = array()) {
- if(!parent::execute($values)) {
- return false;
- }
-
- if (!$this->user->isLoggedIn()) {
- $this->result["loggedIn"] = false;
- } else {
- $this->result["loggedIn"] = true;
- }
-
- $this->result["user"] = $this->user->jsonSerialize();
- return $this->success;
- }
-}
\ No newline at end of file
diff --git a/core/Api/User/Invite.class.php b/core/Api/User/Invite.class.php
deleted file mode 100644
index c65da11..0000000
--- a/core/Api/User/Invite.class.php
+++ /dev/null
@@ -1,84 +0,0 @@
- new StringType('username', 32),
- 'email' => new StringType('email', 64),
- ));
- $this->csrfTokenRequired = true;
- $this->loginRequired = true;
- $this->requiredGroup = USER_GROUP_ADMIN;
- }
-
- public function execute($values = array()) {
- if(!parent::execute($values)) {
- return false;
- }
-
- $username = $this->getParam('username');
- $email = $this->getParam('email');
- if (!$this->userExists($username, $email) || !$this->success) {
- return false;
- }
-
- //add to DB
- $token = generateRandomString(36);
- $valid_until = (new DateTime())->modify("+48 hour");
- $sql = $this->user->getSQL();
- $res = $sql->insert("UserInvitation", array("username", "email", "token", "valid_until"))
- ->addRow($username, $email, $token, $valid_until)
- ->execute();
- $this->success = ($res !== FALSE);
- $this->lastError = $sql->getLastError();
-
- //send validation mail
- if($this->success) {
- $request = new SendMail($this->user);
- $link = "http://localhost/acceptInvitation?token=$token";
- $this->success = $request->execute(array(
- "from" => "webmaster@romanh.de",
- "to" => $email,
- "subject" => "Account Invitation for web-base@localhost",
- "body" =>
-"Hello,
-you were invited to create an account on web-base@localhost. Click on the following link to confirm the registration, it is 48h valid from now.
-If the invitation was not intended, you can simply ignore this email.
$link"
- )
- );
- $this->lastError = $request->getLastError();
- }
- return $this->success;
- }
-
- private function userExists($username, $email) {
- $sql = $this->user->getSQL();
- $res = $sql->select("User.name", "User.email")
- ->from("User")
- ->where(new Compare("User.name", $username), new Compare("User.email", $email))
- ->execute();
-
- $this->success = ($res !== FALSE);
- $this->lastError = $sql->getLastError();
-
- if ($this->success && !empty($res)) {
- $row = $res[0];
- if (strcasecmp($username, $row['name']) === 0) {
- return $this->createError("This username is already taken.");
- } else if (strcasecmp($username, $row['email']) === 0) {
- return $this->createError("This email address is already in use.");
- }
- }
-
- return $this->success;
- }
-}
\ No newline at end of file
diff --git a/core/Api/User/Login.class.php b/core/Api/User/Login.class.php
deleted file mode 100644
index 9fb3c8f..0000000
--- a/core/Api/User/Login.class.php
+++ /dev/null
@@ -1,81 +0,0 @@
- new StringType('username', 32),
- 'password' => new StringType('password'),
- 'stayLoggedIn' => new Parameter('stayLoggedIn', Parameter::TYPE_BOOLEAN, true, true)
- ));
- $this->forbidMethod("GET");
- }
-
- private function wrongCredentials() {
- $runtime = microtime(true) - $this->startedAt;
- $sleepTime = round(3e6 - $runtime);
- if($sleepTime > 0) usleep($sleepTime);
- return $this->createError(L('Wrong username or password'));
- }
-
- public function execute($values = array()) {
- if(!parent::execute($values)) {
- return false;
- }
-
- if($this->user->isLoggedIn()) {
- $this->lastError = L('You are already logged in');
- $this->success = true;
- return true;
- }
-
- $this->startedAt = microtime(true);
- $this->success = false;
- $username = $this->getParam('username');
- $password = $this->getParam('password');
- $stayLoggedIn = $this->getParam('stayLoggedIn');
-
- $sql = $this->user->getSQL();
- $res = $sql->select("User.uid", "User.password", "User.salt")
- ->from("User")
- ->where(new Compare("User.name", $username))
- ->execute();
-
- $this->success = ($res !== FALSE);
- $this->lastError = $sql->getLastError();
-
- if($this->success) {
- if(count($res) === 0) {
- return $this->wrongCredentials();
- } else {
- $row = $res[0];
- $salt = $row['salt'];
- $uid = $row['uid'];
- $hash = hash('sha256', $password . $salt);
- if($hash === $row['password']) {
- if(!($this->success = $this->user->createSession($uid, $stayLoggedIn))) {
- return $this->createError("Error creating Session: " . $sql->getLastError());
- } else {
- $this->result["loggedIn"] = true;
- $this->result['logoutIn'] = $this->user->getSession()->getExpiresSeconds();
- $this->success = true;
- }
- }
- else {
- return $this->wrongCredentials();
- }
- }
- }
-
- return $this->success;
- }
-}
\ No newline at end of file
diff --git a/core/Api/User/Logout.class.php b/core/Api/User/Logout.class.php
deleted file mode 100644
index f7c52d5..0000000
--- a/core/Api/User/Logout.class.php
+++ /dev/null
@@ -1,25 +0,0 @@
-loginRequired = true;
- $this->apiKeyAllowed = false;
- $this->csrfTokenRequired = true;
- }
-
- public function execute($values = array()) {
- if(!parent::execute($values)) {
- return false;
- }
-
- $this->success = $this->user->logout();
- $this->lastError = $this->user->getSQL()->getLastError();
- return $this->success;
- }
-}
\ No newline at end of file
diff --git a/core/Api/User/Register.php b/core/Api/User/Register.php
deleted file mode 100644
index b8cfad2..0000000
--- a/core/Api/User/Register.php
+++ /dev/null
@@ -1,46 +0,0 @@
-user->isLoggedIn()) {
- $this->lastError = L('You are already logged in');
- $this->success = false;
- return false;
- }
-
- if (!parent::execute($values)) {
- return false;
- }
-
- if($this->success) {
- $email = $this->getParam('email');
- $token = generateRandomString(36);
- $request = new SendMail($this->user);
- $link = "http://localhost/acceptInvitation?token=$token";
- $this->success = $request->execute(array(
- "from" => "webmaster@romanh.de",
- "to" => $email,
- "subject" => "Account Invitation for web-base@localhost",
- "body" =>
- "Hello,
-you were invited to create an account on web-base@localhost. Click on the following link to confirm the registration, it is 48h valid from now.
-If the invitation was not intended, you can simply ignore this email.
$link"
- )
- );
- $this->lastError = $request->getLastError();
- }
- return $this->success;
- }
-}
\ No newline at end of file
diff --git a/core/Api/UserAPI.class.php b/core/Api/UserAPI.class.php
new file mode 100644
index 0000000..cc51998
--- /dev/null
+++ b/core/Api/UserAPI.class.php
@@ -0,0 +1,442 @@
+user->getSQL();
+ $res = $sql->select("User.name", "User.email")
+ ->from("User")
+ ->where(new Compare("User.name", $username), new Compare("User.email", $email))
+ ->execute();
+
+ $this->success = ($res !== FALSE);
+ $this->lastError = $sql->getLastError();
+
+ if ($this->success && !empty($res)) {
+ $row = $res[0];
+ if (strcasecmp($username, $row['name']) === 0) {
+ return $this->createError("This username is already taken.");
+ } else if (strcasecmp($username, $row['email']) === 0) {
+ return $this->createError("This email address is already in use.");
+ }
+ }
+
+ return $this->success;
+ }
+
+ protected function insertUser($username, $email, $password) {
+ $sql = $this->user->getSQL();
+ $salt = generateRandomString(16);
+ $hash = $this->hashPassword($password, $salt);
+ $res = $sql->insert("User", array("name", "password", "salt", "email"))
+ ->addRow($username, $hash, $salt, $email)
+ ->returning("uid")
+ ->execute();
+
+ $this->lastError = $sql->getLastError();
+ $this->success = ($res !== FALSE);
+
+ if ($this->success) {
+ return $sql->getLastInsertId();
+ }
+
+ return $this->success;
+ }
+
+ // TODO: replace this with crypt() in the future
+ protected function hashPassword($password, $salt) {
+ return hash('sha256', $password . $salt);
+ }
+}
+
+}
+
+namespace Api\User {
+
+ use Api\Parameter\Parameter;
+ use Api\Parameter\StringType;
+ use Api\SendMail;
+ use Api\UserAPI;
+ use DateTime;
+ use Driver\SQL\Condition\Compare;
+
+ class Create extends UserAPI {
+
+ public function __construct($user, $externalCall = false) {
+ parent::__construct($user, $externalCall, array(
+ 'username' => new StringType('username', 32),
+ 'email' => new StringType('email', 64, true),
+ 'password' => new StringType('password'),
+ 'confirmPassword' => new StringType('confirmPassword'),
+ ));
+ $this->csrfTokenRequired = true;
+ $this->loginRequired = true;
+ $this->requiredGroup = USER_GROUP_ADMIN;
+ }
+
+ public function execute($values = array()) {
+ if (!parent::execute($values)) {
+ return false;
+ }
+
+ $username = $this->getParam('username');
+ $email = $this->getParam('email');
+ if (!$this->userExists($username, $email) || !$this->success) {
+ return false;
+ }
+
+ $password = $this->getParam('password');
+ $confirmPassword = $this->getParam('confirmPassword');
+ if ($password !== $confirmPassword) {
+ return $this->createError("The given passwords do not match.");
+ }
+
+ return $this->insertUser($username, $email, $password) !== FALSE;
+ }
+}
+
+class Fetch extends UserAPI {
+
+ const SELECT_SIZE = 10;
+
+ private int $userCount;
+
+ public function __construct($user, $externalCall = false) {
+
+ parent::__construct($user, $externalCall, array(
+ 'page' => new Parameter('page', Parameter::TYPE_INT, true, 1)
+ ));
+
+ $this->loginRequired = true;
+ $this->requiredGroup = USER_GROUP_ADMIN;
+ $this->userCount = 0;
+ $this->csrfTokenRequired = true;
+ }
+
+ private function getUserCount() {
+
+ $sql = $this->user->getSQL();
+ $res = $sql->select($sql->count())->from("User")->execute();
+ $this->success = ($res !== FALSE);
+ $this->lastError = $sql->getLastError();
+
+ if ($this->success) {
+ $this->userCount = $res[0]["count"];
+ }
+
+ return $this->success;
+ }
+
+ public function execute($values = array()) {
+ if(!parent::execute($values)) {
+ return false;
+ }
+
+ $page = $this->getParam("page");
+ if($page < 1) {
+ return $this->createError("Invalid page count");
+ }
+
+ if (!$this->getUserCount()) {
+ return false;
+ }
+
+ $sql = $this->user->getSQL();
+ $res = $sql->select("User.uid as userId", "User.name", "User.email", "User.registered_at",
+ "Group.uid as groupId", "Group.name as groupName")
+ ->from("User")
+ ->leftJoin("UserGroup", "User.uid", "UserGroup.user_id")
+ ->leftJoin("Group", "Group.uid", "UserGroup.group_id")
+ ->orderBy("User.uid")
+ ->ascending()
+ ->limit(Fetch::SELECT_SIZE)
+ ->offset(($page - 1) * Fetch::SELECT_SIZE)
+ ->execute();
+
+ $this->success = ($res !== FALSE);
+ $this->lastError = $sql->getLastError();
+
+ if($this->success) {
+ $this->result["users"] = array();
+ foreach($res as $row) {
+ $userId = intval($row["userId"]);
+ $groupId = intval($row["groupId"]);
+ $groupName = $row["groupName"];
+ if (!isset($this->result["users"][$userId])) {
+ $this->result["users"][$userId] = array(
+ "uid" => $userId,
+ "name" => $row["name"],
+ "email" => $row["email"],
+ "registered_at" => $row["registered_at"],
+ "groups" => array(),
+ );
+ }
+
+ if(!is_null($groupId)) {
+ $this->result["users"][$userId]["groups"][$groupId] = $groupName;
+ }
+ }
+ $this->result["pageCount"] = intval(ceil($this->userCount / Fetch::SELECT_SIZE));
+ $this->result["totalCount"] = $this->userCount;
+ }
+
+ return $this->success;
+ }
+}
+
+class Info extends UserAPI {
+
+ public function __construct($user, $externalCall = false) {
+ parent::__construct($user, $externalCall, array());
+ $this->csrfTokenRequired = true;
+ }
+
+ public function execute($values = array()) {
+ if(!parent::execute($values)) {
+ return false;
+ }
+
+ if (!$this->user->isLoggedIn()) {
+ $this->result["loggedIn"] = false;
+ } else {
+ $this->result["loggedIn"] = true;
+ }
+
+ $this->result["user"] = $this->user->jsonSerialize();
+ return $this->success;
+ }
+}
+
+class Invite extends UserAPI {
+
+ public function __construct($user, $externalCall = false) {
+ parent::__construct($user, $externalCall, array(
+ 'username' => new StringType('username', 32),
+ 'email' => new StringType('email', 64),
+ ));
+ $this->csrfTokenRequired = true;
+ $this->loginRequired = true;
+ $this->requiredGroup = USER_GROUP_ADMIN;
+ }
+
+ public function execute($values = array()) {
+ if(!parent::execute($values)) {
+ return false;
+ }
+
+ $username = $this->getParam('username');
+ $email = $this->getParam('email');
+ if (!$this->userExists($username, $email)) {
+ return false;
+ }
+
+ //add to DB
+ $token = generateRandomString(36);
+ $valid_until = (new DateTime())->modify("+48 hour");
+ $sql = $this->user->getSQL();
+ $res = $sql->insert("UserInvitation", array("username", "email", "token", "valid_until"))
+ ->addRow($username, $email, $token, $valid_until)
+ ->execute();
+ $this->success = ($res !== FALSE);
+ $this->lastError = $sql->getLastError();
+
+ //send validation mail
+ if($this->success) {
+ $request = new SendMail($this->user);
+ $link = "http://localhost/acceptInvitation?token=$token";
+ $this->success = $request->execute(array(
+ "from" => "webmaster@romanh.de",
+ "to" => $email,
+ "subject" => "Account Invitation for web-base@localhost",
+ "body" =>
+ "Hello,
+you were invited to create an account on web-base@localhost. Click on the following link to confirm the registration, it is 48h valid from now.
+If the invitation was not intended, you can simply ignore this email.
$link"
+ )
+ );
+ $this->lastError = $request->getLastError();
+ }
+ return $this->success;
+ }
+}
+
+class Login extends UserAPI {
+
+ private int $startedAt;
+
+ public function __construct($user, $externalCall = false) {
+ parent::__construct($user, $externalCall, array(
+ 'username' => new StringType('username', 32),
+ 'password' => new StringType('password'),
+ 'stayLoggedIn' => new Parameter('stayLoggedIn', Parameter::TYPE_BOOLEAN, true, true)
+ ));
+ $this->forbidMethod("GET");
+ }
+
+ private function wrongCredentials() {
+ $runtime = microtime(true) - $this->startedAt;
+ $sleepTime = round(3e6 - $runtime);
+ if($sleepTime > 0) usleep($sleepTime);
+ return $this->createError(L('Wrong username or password'));
+ }
+
+ public function execute($values = array()) {
+ if(!parent::execute($values)) {
+ return false;
+ }
+
+ if($this->user->isLoggedIn()) {
+ $this->lastError = L('You are already logged in');
+ $this->success = true;
+ return true;
+ }
+
+ $this->startedAt = microtime(true);
+ $this->success = false;
+ $username = $this->getParam('username');
+ $password = $this->getParam('password');
+ $stayLoggedIn = $this->getParam('stayLoggedIn');
+
+ $sql = $this->user->getSQL();
+ $res = $sql->select("User.uid", "User.password", "User.salt")
+ ->from("User")
+ ->where(new Compare("User.name", $username))
+ ->execute();
+
+ $this->success = ($res !== FALSE);
+ $this->lastError = $sql->getLastError();
+
+ if($this->success) {
+ if(count($res) === 0) {
+ return $this->wrongCredentials();
+ } else {
+ $row = $res[0];
+ $salt = $row['salt'];
+ $uid = $row['uid'];
+ $hash = $this->hashPassword($password, $salt);
+ if($hash === $row['password']) {
+ if(!($this->success = $this->user->createSession($uid, $stayLoggedIn))) {
+ return $this->createError("Error creating Session: " . $sql->getLastError());
+ } else {
+ $this->result["loggedIn"] = true;
+ $this->result['logoutIn'] = $this->user->getSession()->getExpiresSeconds();
+ $this->success = true;
+ }
+ }
+ else {
+ return $this->wrongCredentials();
+ }
+ }
+ }
+
+ return $this->success;
+ }
+}
+
+class Logout extends UserAPI {
+
+ public function __construct($user, $externalCall = false) {
+ parent::__construct($user, $externalCall);
+ $this->loginRequired = true;
+ $this->apiKeyAllowed = false;
+ $this->csrfTokenRequired = true;
+ }
+
+ public function execute($values = array()) {
+ if(!parent::execute($values)) {
+ return false;
+ }
+
+ $this->success = $this->user->logout();
+ $this->lastError = $this->user->getSQL()->getLastError();
+ return $this->success;
+ }
+}
+
+class Register extends UserAPI {
+
+ private ?int $userId;
+ private string $token;
+
+ public function __construct($user, $externalCall = false) {
+ parent::__construct($user, $externalCall, array(
+ "username" => new StringType("username", 32),
+ "email" => new StringType("email", 64),
+ "password" => new StringType("password"),
+ "confirmPassword" => new StringType("confirmPassword"),
+ ));
+ }
+
+ private function insertToken() {
+ $validUntil = (new DateTime())->modify("+48 hour");
+ $sql = $this->user->getSQL();
+ $res = $sql->insert("UserToken", array("user_id", "token", "token_type", "valid_until"))
+ ->addRow(array($this->userId, $this->token, "confirmation", $validUntil))
+ ->execute();
+
+ $this->success = ($res !== FALSE);
+ $this->lastError = $sql->getLastError();
+ return $this->success;
+ }
+
+ public function execute($values = array()) {
+ if (!parent::execute($values)) {
+ return false;
+ }
+
+ if ($this->user->isLoggedIn()) {
+ $this->lastError = L('You are already logged in');
+ $this->success = false;
+ return false;
+ }
+
+ $username = $this->getParam("username");
+ $email = $this->getParam('email');
+ if (!$this->userExists($username, $email)) {
+ return false;
+ }
+
+ $password = $this->getParam("password");
+ $confirmPassword = $this->getParam("confirmPassword");
+ if(strcmp($password, $confirmPassword) !== 0) {
+ return $this->createError("The given passwords don't match");
+ }
+
+ $id = $this->insertUser($username, $email, $password);
+ if ($id === FALSE) {
+ return false;
+ }
+
+ $this->userId = $id;
+ $this->token = generateRandomString(36);
+ if ($this->insertToken()) {
+ return false;
+ }
+
+ $request = new SendMail($this->user);
+ $link = "http://localhost/confirmEmail?token=$this->token";
+ $this->success = $request->execute(array(
+ "from" => "webmaster@romanh.de",
+ "to" => $email,
+ "subject" => "E-Mail Confirmation for web-base@localhost",
+ "body" =>
+ "Hello,
+you recently registered an account on web-base@localhost. Click on the following link to confirm the registration, it is 48h valid from now.
+If the registration was not intended, you can simply ignore this email.
$link"
+ )
+ );
+
+ if (!$this->success) {
+ $this->lastError = "Your account was registered but the confirmation email could not be sent. " .
+ "Please contact the server administration. Reason: " . $request->getLastError();
+ }
+
+ return $this->success;
+ }
+}
+
+}
\ No newline at end of file
diff --git a/core/Objects/User.class.php b/core/Objects/User.class.php
index 1fb423b..973e948 100644
--- a/core/Objects/User.class.php
+++ b/core/Objects/User.class.php
@@ -2,7 +2,6 @@
namespace Objects;
-use Api\SetLanguage;
use Configuration\Configuration;
use DateTime;
use Driver\SQL\Expression\Add;
@@ -108,7 +107,7 @@ class User extends ApiObject {
public function updateLanguage($lang) {
if($this->sql) {
- $request = new SetLanguage($this);
+ $request = new \Api\Language\Set($this);
return $request->execute(array("langCode" => $lang));
} else {
return false;
diff --git a/core/Views/LanguageFlags.class.php b/core/Views/LanguageFlags.class.php
index c4f0283..555857d 100644
--- a/core/Views/LanguageFlags.class.php
+++ b/core/Views/LanguageFlags.class.php
@@ -2,7 +2,6 @@
namespace Views;
-use Api\GetLanguages;
use Elements\View;
class LanguageFlags extends View {
@@ -17,7 +16,7 @@ class LanguageFlags extends View {
public function loadView() {
parent::loadView();
- $request = new GetLanguages($this->getDocument()->getUser());
+ $request = new \Api\Language\Get($this->getDocument()->getUser());
if($request->execute()) {
$requestUri = $_SERVER["REQUEST_URI"];
diff --git a/core/core.php b/core/core.php
index 7063ae5..26af7e6 100644
--- a/core/core.php
+++ b/core/core.php
@@ -87,7 +87,14 @@
function getClassPath($class, $suffix=true) {
$path = str_replace('\\', '/', $class);
- if (startsWith($path, "/")) $path = substr($path, 1);
+ $path = array_values(array_filter(explode("/", $path)));
+
+ if (strcasecmp($path[0], "api") === 0 && count($path) > 2 && strcasecmp($path[1], "Parameter") !== 0) {
+ $path = "Api/" . $path[1] . "API";
+ } else {
+ $path = implode("/", $path);
+ }
+
$suffix = ($suffix ? ".class" : "");
return "core/$path$suffix.php";
}
diff --git a/index.php b/index.php
index 9b2cd1b..7ead9e1 100644
--- a/index.php
+++ b/index.php
@@ -41,21 +41,35 @@ if(isset($_GET["api"]) && is_string($_GET["api"])) {
header("400 Bad Request");
$response = createError("Invalid Method");
} else {
- $apiFunction = implode("\\", array_map('ucfirst', explode("/", $apiFunction)));
- if($apiFunction[0] !== "\\") $apiFunction = "\\$apiFunction";
- $class = "\\Api$apiFunction";
- $file = getClassPath($class);
- if(!file_exists($file)) {
- header("404 Not Found");
- $response = createError("Not found");
- } else if(!is_subclass_of($class, Request::class)) {
- header("400 Bad Request");
- $response = createError("Invalid Method");
+ $apiFunction = array_filter(array_map('ucfirst', explode("/", $apiFunction)));
+ if (count($apiFunction) > 1) {
+ $parentClass = "\\Api\\" . reset($apiFunction) . "API";
+ $apiClass = "\\Api\\" . implode("\\", $apiFunction);
} else {
- $request = new $class($user, true);
- $success = $request->execute();
- $msg = $request->getLastError();
- $response = $request->getJsonResult();
+ $apiClass = "\\Api\\" . implode("\\", $apiFunction);
+ $parentClass = $apiClass;
+ }
+
+ try {
+ $file = getClassPath($parentClass);
+ if(!file_exists($file)) {
+ header("404 Not Found");
+ $response = createError("Not found");
+ } else {
+ $parentClass = new ReflectionClass($parentClass);
+ $apiClass = new ReflectionClass($apiClass);
+ if(!$apiClass->isSubclassOf(Request::class) || !$apiClass->isInstantiable()) {
+ header("400 Bad Request");
+ $response = createError("Invalid Method");
+ } else {
+ $request = $apiClass->newInstanceArgs(array($user, true));
+ $success = $request->execute();
+ $msg = $request->getLastError();
+ $response = $request->getJsonResult();
+ }
+ }
+ } catch (ReflectionException $e) {
+ $response = createError("Error instantiating class: $e");
}
}
}