diff --git a/.idea/dataSources.local.xml b/.idea/dataSources.local.xml new file mode 100644 index 0000000..cdc31de --- /dev/null +++ b/.idea/dataSources.local.xml @@ -0,0 +1,18 @@ + + + + + + " + + + master_key + no-auth + + + + + + + + \ No newline at end of file diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 0000000..3f17fd0 --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,12 @@ + + + + + sqlite.xerial + true + org.sqlite.JDBC + jdbc:sqlite:$PROJECT_DIR$/db/articles + $ProjectFileDir$ + + + \ No newline at end of file diff --git a/.idea/dataSources/315cb5c9-2b0f-435b-b602-59823b160908.xml b/.idea/dataSources/315cb5c9-2b0f-435b-b602-59823b160908.xml new file mode 100644 index 0000000..4fccad1 --- /dev/null +++ b/.idea/dataSources/315cb5c9-2b0f-435b-b602-59823b160908.xml @@ -0,0 +1,1833 @@ + + + + + 3.51.1 + + + + + + + + + + + + + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + + window + + + 1 + + + 1 + + + 1 + + + + 1 + 1 + + + + + 1 + 1 + + + 1 + 1 + + + 1 + 1 + + + 1 + + + 1 + + + + + window + + + window + + + window + + + + + + 1 + 1 + + + 1 + 1 + + + 1 + + + window + + + + 1 + + + window + + + 1 + + + 1 + 1 + + + + + + 1 + + + + + 1 + + + 1 + + + window + + + window + + + 1 + + + 1 + + + 1 + 1 + + + 1 + + + 1 + 1 + + + 1 + + + 1 + + + 1 + 1 + + + 1 + + + 1 + + + 1 + + + 1 + 1 + + + 1 + window + + + 1 + window + + + 1 + 1 + + + 1 + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + 1 + + + 1 + 1 + + + 1 + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + 1 + + + 1 + 1 + + + 1 + window + + + 1 + window + + + 1 + 1 + + + 1 + 1 + + + 1 + + + 1 + 1 + + + 1 + 1 + + + 1 + 1 + + + 1 + 1 + + + window + + + window + + + window + + + + window + + + window + + + window + + + window + + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + aggregate + + + 1 + + + 1 + + + + + + 1 + 1 + + + window + + + aggregate + + + 1 + 1 + + + window + + + 1 + + + aggregate + + + window + + + window + + + 1 + + + 1 + + + + + + + + window + + + 1 + + + 1 + + + 1 + + + 1 + 1 + + + + 1 + + + 1 + + + + + window + + + 1 + + + 1 + + + + + + 1 + + + 1 + + + window + + + 1 + + + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + + + 1 + + + + aggregate + + + + 1 + 1 + + + window + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + window + + + 1 + + + 1 + + + 1 + 1 + + + 1 + + + window + + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + 1 + + + 1 + + + 1 + + + aggregate + + + aggregate + + + 1 + + + 1 + 2026-06-05.07:13:27 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + R + + + R + + + 1 + + + R + + + 1 + + + R + + + R + + + R + + + 1 + + + 2 + + + R + + + 1 + + + 2 + + + 3 + + + R + + + R + + + R + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + R + + + R + + + 1 + + + R + + + R + + + R + + + R + + + R + + + R + + + R + + + 1 + + + R + + + R + + + 1 + + + 2 + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + R + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + R + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + R + + + 1 + + + 2 + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + R + + + R + + + R + + + 1 + + + 2 + + + R + + + R + + + 1 + + + 2 + + + R + + + 1 + + + R + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + R + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + R + + + R + + + 1 + + + 2 + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + R + + + R + + + R + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + R + + + R + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + R + + + R + + + 1 + + + 2 + + + R + + + R + + + R + + + R + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + 2 + + + 3 + + + R + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + 2 + + + 3 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + 2 + + + 3 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + R + + + 1 + + + R + + + 1 + + + R + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + 2 + + + R + + + R + + + R + + + 1 + + + 2 + + + R + + + 1 + + + 2 + + + R + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + R + + + R + + + 1 + + + R + + + R + + + R + + + 1 + + + 2 + + + 3 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + R + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + R + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + R + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + R + + + 1 + + + 2 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + 2 + + + 3 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + 2 + + + 3 + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + R + + + R + + + 1 + + + 2 + + + R + + + 1 + + + R + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + 2 + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + R + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + R + + + 1 + + + 1 +
+ + 1 + TEXT|0s + + + 2 + TEXT|0s + + + 3 + TEXT|0s + + + 4 + INT|0s + + + 5 + TEXT|0s + +
+
\ No newline at end of file diff --git a/.idea/dataSources/315cb5c9-2b0f-435b-b602-59823b160908/storage_v2/_src_/schema/main.uQUzAA.meta b/.idea/dataSources/315cb5c9-2b0f-435b-b602-59823b160908/storage_v2/_src_/schema/main.uQUzAA.meta new file mode 100644 index 0000000..8dab49c --- /dev/null +++ b/.idea/dataSources/315cb5c9-2b0f-435b-b602-59823b160908/storage_v2/_src_/schema/main.uQUzAA.meta @@ -0,0 +1,2 @@ +#n:main +! [0, 0, null, null, -2147483648, -2147483648] diff --git a/db/.htaccess b/db/.htaccess new file mode 100644 index 0000000..437a11a --- /dev/null +++ b/db/.htaccess @@ -0,0 +1,3 @@ + + deny from all + \ No newline at end of file diff --git a/php/model/ArticleManager.php b/php/model/ArticleManager.php index 8962aa3..5ce70da 100644 --- a/php/model/ArticleManager.php +++ b/php/model/ArticleManager.php @@ -1,5 +1,6 @@ getArticle(1) == null ){ diff --git a/php/model/DatabaseArticleManager.php b/php/model/DatabaseArticleManager.php new file mode 100644 index 0000000..1697071 --- /dev/null +++ b/php/model/DatabaseArticleManager.php @@ -0,0 +1,313 @@ +getConnection(); + + $db->exec(" + CREATE TABLE articles ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + title TEXT, + content TEXT, + author TEXT, + category TEXT, + tags TEXT, + created TIMESTAMP DEFAULT CURRENT_TIMESTAMP + );"); + unset($db); + } catch (PDOException $e) { + throw new InternalServerErrorException($e->getMessage()); + } + } + } + + /** + * Baut die Verbindung zur Datenbank auf. + * @throws InternalServerErrorException + */ + private function getConnection() + { + try { + $user = 'root'; + $pw = null; + $dsn = 'sqlite:' . __DIR__ . '/../../db/articles.db'; + return new PDO($dsn, $user, $pw); + } catch (PDOException $e) { + throw new InternalServerErrorException($e->getMessage()); + } + } + + /** + * Gibt die DatabaseArticleManager-Instanz zurück. + * @return DatabaseArticleManager + */ + public static function getInstance() + { + if (self::$instance == null) { + self::$instance = new DatabaseArticleManager(); + } + return self::$instance; + } + + public function addArticle($title, $content, $author, $category, $tags) + { + try { + $db = $this->getConnection(); + + $sql = "INSERT INTO articles (title, content, author, category, tags) + VALUES (:title, :content, :author, :category, :tags);"; + + $command = $db->prepare($sql); + if (!$command) { + throw new InternalServerErrorException("internal_error"); + } + + // Verknüpft die übergebenen Parameter exakt mit den SQL-Platzhaltern + $success = $command->execute([ + ":title" => $title, + ":content" => $content, + ":author" => $author, + ":category" => $category, + ":tags" => $tags + ]); + + if (!$success) { + throw new InternalServerErrorException("internal_error"); + } + + return intval($db->lastInsertId()); + + } catch (PDOException $e) { + throw new InternalServerErrorException($e->getMessage()); + } + } + + public function updateArticle($id, $article, $author) + { + if (empty($article)) { + throw new InternalServerErrorException("internal_error"); + } + + // Berechtigungsprüfung analog zur lokalen Implementierung: + if ($article->getAuthor() !== $author) { + throw new UnauthorizedAccessException("unauthorized_access"); + } + + try { + $db = $this->getConnection(); + + $sql = "UPDATE articles + SET title = :title, content = :content, author = :author, category = :category, tags = :tags + WHERE id = :id;"; + + $command = $db->prepare($sql); + if (!$command) { + throw new InternalServerErrorException("internal_error"); + } + + $success = $command->execute([ + ":id" => $id, + ":title" => $article->getTitle(), + ":content" => $article->getContent(), + ":author" => $author, + ":category" => $article->getCategory(), + ":tags" => $article->getTags() + ]); + + // rowCount() prüft, ob eine Zeile mit dieser ID existierte und geändert werden konnte + if (!$success || $command->rowCount() === 0) { + // Falls die ID nicht existiert, prüfen wir, ob sie überhaupt da ist + if (!$this->getArticle($id)) { + throw new NotFoundException("missing_id"); + } + } + } catch (PDOException $e) { + throw new InternalServerErrorException("internal_error"); + } + } + + public function deleteArticle($id, $author) + { + $article = getArticle($id); + if (empty($article)) { + throw new NotFoundException("not_found_article"); + } + + // Berechtigungsprüfung: + if ($article->getAuthor() !== $author) { + throw new UnauthorizedAccessException("unauthorized_access"); + } + + try { + $db = $this->getConnection(); + $sql = "DELETE FROM articles WHERE id = :id;"; + + $command = $db->prepare($sql); + if (!$command) { + throw new InternalServerErrorException("internal_error"); + } + + if (!$command->execute([":id" => $id])) { + throw new InternalServerErrorException("internal_error"); + } + } catch (PDOException $exc) { + throw new InternalServerErrorException("internal_error"); + } + } + + public function getArticle($id) + { + try { + $db = $this->getConnection(); + $sql = "SELECT * FROM articles WHERE id = :id;"; + + $command = $db->prepare($sql); + if (!$command) { + throw new InternalServerErrorException("internal_error"); + } + + $command->execute([":id" => $id]); + $row = $command->fetch(PDO::FETCH_ASSOC); + + if ($row) { + return new Article( + intval($row['id']), + $row['title'], + $row['content'], + $row['author'], + $row['category'], + $row['tags'], + $row['created'] + ); + } + + return null; + } catch (PDOException $e) { + throw new InternalServerErrorException("internal_error"); + } + } + + public function getAllArticles() + { + try { + $db = $this->getConnection(); + $sql = "SELECT * FROM articles;"; + + $command = $db->query($sql); + if (!$command) { + throw new InternalServerErrorException("internal_error"); + } + + $rows = $command->fetchAll(PDO::FETCH_ASSOC); + $articles = []; + + foreach ($rows as $row) { + $articles[] = new Article( + intval($row['id']), + $row['title'], + $row['content'], + $row['author'], + $row['category'], + $row['tags'], + $row['created'] + ); + } + + return $articles; + } catch (PDOException $e) { + throw new InternalServerErrorException("internal_error"); + } + } + + public function getArticlesByAuthor($author) + { + try { + $db = $this->getConnection(); + $sql = "SELECT * FROM articles WHERE author = :author;"; + + $command = $db->prepare($sql); + if (!$command) { + throw new InternalServerErrorException("internal_error"); + } + + $command->execute([":author" => $author]); + $rows = $command->fetchAll(PDO::FETCH_ASSOC); + $filteredArticles = []; + + foreach ($rows as $row) { + $filteredArticles[] = new Article( + intval($row['id']), + $row['title'], + $row['content'], + $row['author'], + $row['category'], + $row['tags'], + $row['created'] + ); + } + + return $filteredArticles; + } catch (PDOException $e) { + throw new InternalServerErrorException("internal_error"); + } + } + + public function getArticlesByCategory($category) + { + try { + $db = $this->getConnection(); + $sql = "SELECT * FROM articles WHERE category = :category;"; + + $command = $db->prepare($sql); + if (!$command) { + throw new InternalServerErrorException("internal_error"); + } + + $command->execute([":category" => $category]); + $rows = $command->fetchAll(PDO::FETCH_ASSOC); + $filteredArticles = []; + + foreach ($rows as $row) { + $filteredArticles[] = new Article( + intval($row['id']), + $row['title'], + $row['content'], + $row['author'], + $row['category'], + $row['tags'], + $row['created'] + ); + } + + return $filteredArticles; + } catch (PDOException $exc) { + throw new InternalServerErrorException("internal_error"); + } + } + + public function search(string $keyword): array + { + // TODO: implement search() + return []; + } + +} \ No newline at end of file diff --git a/php/model/DatabaseUserManager.php b/php/model/DatabaseUserManager.php new file mode 100644 index 0000000..cd27d99 --- /dev/null +++ b/php/model/DatabaseUserManager.php @@ -0,0 +1,26 @@ +