subjectId = $subjectId; if (Util::containsIllegalCharacters($id)) { return false; } if (self::exists($subjectId, $id)) { return false; } $result->id = $id; $result->displayName = $displayName; $result->icon = $icon; $result->description = $description; $result->relatedTopics = $relatedTopics; $result->files = array(); $result->article = $article; $result->tasks = array(); return $result; } /** * Prüft, ob das Thema zu den angegebenen IDs existiert * @param string $subjectId ID des Faches * @param string $topicId ID des Themas * @return bool true, wenn es existiert, sonst false */ public static function exists(string $subjectId, string $topicId): bool { if (!is_dir(Config::getTopicDirectory($subjectId, $topicId))) { return false; } return true; } /** * Gibt alle Themen zu einem gegebenen Fach zurück * @param $subjectId string Die ID des Faches * @return array Alle zugehörigen Themen als TopicData Objekte */ public static function getAll(string $subjectId): array { $result = array(); $topicNames = scandir(Config::getTopicsDirectory($subjectId)); usort($topicNames, function ($a, $b) { return strcmp($a, $b); }); foreach ($topicNames as $topicName) { if ($topicName == "." || $topicName == "..") { continue; } $topicData = TopicData::fromName($subjectId, $topicName); if (!isset($topicData)) { continue; } $result[$topicData->id] = $topicData; } return $result; } /** * Gibt Themendaten zu einem bestimmten Thema eines Faches zurück * @param $subjectId string Die ID des Faches * @param $topicId string Die ID des Themas * @return TopicData|null Die Themendaten oder null, wenn das Thema nicht existiert */ public static function fromName(string $subjectId, string $topicId): TopicData|null { $result = new TopicData(); if (Util::containsIllegalCharacters($subjectId)) { return null; } if (Util::containsIllegalCharacters($topicId)) { return null; } $result->id = $topicId; $result->subjectId = $subjectId; $data = Util::parseJsonFromFile(Config::getTopicDirectory($subjectId, $topicId) . "properties.json"); if (!isset($data)) { return null; } if (!isset($data->displayName)) { return null; } $result->displayName = $data->displayName; if (!isset($data->icon)) { return null; } $result->icon = $data->icon; if (!isset($data->description)) { return null; } $result->description = $data->description; $relatedTopics = array(); if (isset($data->relatedTopics)) { $relatedTopics = $data->relatedTopics; } $result->relatedTopics = $relatedTopics; $files = Util::getFilesFromDirectory(Config::getTopicDirectory($subjectId, $topicId) . "downloads/"); $result->files = $files; $article = Util::readFileContent(Config::getTopicDirectory($subjectId, $topicId) . "article.html"); if (!isset($article)) { $article = "Kein Erklärtext vorhanden"; } $result->article = $article; $taskJson = Util::readFileContent(Config::getTopicDirectory($subjectId, $topicId) . "tasks.json"); $result->tasks = array(); if(isset($taskJson)) { $arr = json_decode($taskJson, true); foreach ($arr as $rawTask) { $text = $rawTask["text"]; if(!isset($text)) { continue; } $vars = $rawTask["vars"]; if (!isset($vars)) { continue; } $result->tasks[] = new Task($text, $vars); } } $result->cleanupRelatedTopics(); $result->cleanupFiles(); return $result; } /** * Schreibt alle Daten in Dateien * @return bool true, wenn erfolgreich, sonst false */ public function save(): bool { $this->cleanupRelatedTopics(); $this->cleanupFiles(); $data = array(); $data["displayName"] = $this->displayName; $data["icon"] = $this->icon; $data["description"] = $this->description; $data["relatedTopics"] = $this->relatedTopics; $json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); if (!$json) { return false; } if (!is_dir(Config::getSubjectDirectory($this->getSubjectId()))) { return false; } $topicDirectory = Config::getTopicDirectory($this->getSubjectId(), $this->getId()); if (!is_dir($topicDirectory)) { mkdir($topicDirectory, 0777, true); } $taskArray = array(); foreach ($this->tasks as $task) { $element = array(); $element["text"] = $task->getText(); $element["vars"] = $task->getVariables(); $taskArray[] = $element; } $taskJson = json_encode($taskArray, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); if (!$taskJson) { return false; } if (!(Util::writeFileContent($topicDirectory . "properties.json", $json) && Util::writeFileContent($topicDirectory . "article.html", $this->article) && Util::writeFileContent($topicDirectory . "tasks.json", $taskJson) ) ) { return false; } return true; } /** * Lädt eine Datei als Download zum Thema hoch * @param string $name Dateiname von User, z.B. $_FILES['html-input-name']['name'][0] * @param string $tmp_name Temporärer Pfad zur hochgeladenen Datei, z.B. $_FILES['html-input-name']['tmp_name'][0] * @return bool true, wenn erfolgreich, sonst false */ public function addDownload(string $name, string $tmp_name): bool { $downloadDirectory = Config::getTopicDirectory($this->getSubjectId(), $this->getId()) . "downloads/"; if (!is_dir($downloadDirectory)) { if (!mkdir($downloadDirectory)) { return false; } } if (!move_uploaded_file($tmp_name, $downloadDirectory . $name)) { return false; } $this->files[] = $name; return true; } /** * Löscht eine downloadbare Datei des Themas * @param string $name Dateiname * @return bool true, wenn erfolgreich, sonst false */ public function deleteDownload(string $name): bool { if (!isset($this->files[$name])) { return false; } if (!unlink(Config::getTopicDirectory($this->getSubjectId(), $this->getId()) . "downloads/$name")) { return false; } $this->files = array_diff($this->files, [$name]); return true; } /** * Lädt eine Datei als Bild zum Thema hoch * @param string $name Dateiname von User, z.B. $_FILES['html-input-name']['name'][0] * @param string $tmp_name Temporärer Pfad zum hochgeladenen Bild, z.B. $_FILES['html-input-name']['tmp_name'][0] * @return bool true, wenn erfolgreich, sonst false */ public function addImage(string $name, string $tmp_name): bool { $imageDirectory = Config::getTopicDirectory($this->getSubjectId(), $this->getId()) . "images/"; if (!is_dir($imageDirectory)) { if (!mkdir($imageDirectory)) { return false; } } if (!move_uploaded_file($tmp_name, $imageDirectory . $name)) { return false; } return true; } /** * Prüft für alle verwandten Themen, ob diese auch existieren. Wenn nicht, wird es aus der Liste entfernt * @return bool true, wenn Elemente entfernt wurden, sonst false */ private function cleanupRelatedTopics(): bool { $changed = false; $nonexistentEntries = array(); foreach ($this->relatedTopics as $topic) { if (!self::exists($this->subjectId, $topic)) { $nonexistentEntries[] = $topic; $changed = true; } } $this->relatedTopics = array_diff($this->relatedTopics, $nonexistentEntries); return $changed; } /** * Prüft für alle Downloads, ob die zugehörige Datei existiert und ob zu jeder Datei ein Eintrag existiert. * Wenn eine Datei nicht existiert, wird auch der zugehörige Eintrag entfernt. * Wenn ein Eintrag nicht existiert, wird auch die Datei gelöscht. * @return bool true, wenn etwas verändert wurde */ private function cleanupFiles(): bool { $changed = false; $nonexistentEntries = array(); foreach ($this->files as $file) { if(!file_exists(Config::getTopicDirectory($this->subjectId, $this->id) . "downloads/$file")) { $nonexistentEntries[] = $file; $changed = true; } } $this->files = array_diff($this->files, $nonexistentEntries); foreach (Util::getFilesFromDirectory(Config::getTopicDirectory($this->subjectId, $this->id) . "downloads/") as $file) { if(!array_search($file, $this->files)) { $this->deleteDownload($file); $changed = true; } } return $changed; } /** * Löscht ein Bild des Themas * @param string $name Dateiname * @return bool true, wenn erfolgreich, sonst false */ public function deleteImage(string $name): bool { if (!unlink(Config::getTopicDirectory($this->getSubjectId(), $this->getId()) . "images/$name")) { return false; } return true; } /** * Löscht das Thema inklusive aller zugehörigen Dateien * @return bool true, wenn erfolgreich gelöscht, sonst false */ public function delete(): bool { if (!Util::delete(Config::getTopicDirectory($this->getSubjectId(), $this->getId()))) { return false; } return true; } public function addTask(Task $task): bool { $this->tasks[] = $task; return true; } public function removeTask(Task $task): bool { $this->tasks = array_diff($this->tasks, [$task]); return true; } public function getId(): string { return $this->id; } public function setId(string $id): void { $this->id = $id; } public function getSubjectId(): string { return $this->subjectId; } public function setSubjectId(string $subjectId): void { $this->subjectId = $subjectId; } public function getDisplayName(): string { return $this->displayName; } public function setDisplayName(string $displayName): void { $this->displayName = $displayName; } public function getIcon(): string { return $this->icon; } public function setIcon(string $icon): void { $this->icon = $icon; } public function getDescription(): string { return $this->description; } public function setDescription(string $description): void { $this->description = $description; } public function getRelatedTopics(): array { return $this->relatedTopics; } public function setRelatedTopics(array $relatedTopics): void { $this->relatedTopics = $relatedTopics; } public function getFiles(): array { return $this->files; } public function setFiles(array $files): void { $this->files = $files; } /** * Gibt anders als getArticle() Bildpfade richtig aus * @return string HTML Quelltext für den Erklärtext */ public function getFinishedArticle(): string { return str_replace('$TOPICPATH', Config::getTopicDirectory($this->subjectId, $this->id) . "images", $this->article); } public function getArticle(): string { return $this->article; } public function setArticle(string $article): void { $this->article = $article; } public function getTasks(): array { return $this->tasks; } public function setTasks(array $tasks): void { $this->tasks = $tasks; } }