User::class, 'foreign_key' => 'user_id' ]; $config['belongs_to']['course'] = [ 'class_name' => Course::class, 'foreign_key' => 'range_id', ]; $config['additional_fields']['parent'] = [ 'get' => function ($page) { return \WikiPage::findLatestPage($page->range_id, $page->ancestor); } ]; $config['additional_fields']['children'] = [ 'get' => function ($page) { $query = "SELECT range_id, keyword, MAX(version) as version FROM wiki WHERE range_id = ? AND ancestor = ? GROUP BY keyword ORDER BY keyword ASC"; $stmt = \DBManager::get()->prepare($query); $stmt->execute([$page->range_id, $page->keyword]); $pageIds = $stmt->fetchAll(\PDO::FETCH_NUM); return array_map( function ($pageId) { return self::find($pageId); }, $pageIds ); } ]; $config['additional_fields']['config']['get'] = function ($page) { return new WikiPageConfig([$page->range_id, $page->keyword]); }; $config['registered_callbacks']['before_delete'][] = function ($page) { if ($page->version == 1 && $page->config) { $page->config->delete(); } }; $config['default_values']['user_id'] = 'nobody'; parent::configure($config); } /** * Finds all latest versions of all pages for the given course. * @param string $course_id Course id * @return SimpleCollection of all pages */ public static function findLatestPages($course_id) { $query = "SELECT range_id, keyword, MAX(version) as version FROM wiki WHERE range_id = ? GROUP BY keyword ORDER BY keyword ASC"; $st = DBManager::get()->prepare($query); $st->execute([$course_id]); $ids = $st->fetchAll(PDO::FETCH_NUM); $pages = new SimpleORMapCollection(); $pages->setClassName(__CLASS__); foreach ($ids as $id) { $pages[] = self::find($id); } return $pages; } /** * Finds the latest version for the given course and keyword * @param string $course_id Course id * @param string $keyword Keyword * @return WikiPage or null */ public static function findLatestPage($course_id, $keyword) { $results = self::findBySQL( "range_id = ? AND keyword = ? ORDER BY version DESC LIMIT 1", [$course_id, $keyword] ); if (count($results) === 0) { return null; } return $results[0]; } /** * Returns whether this page is visible to the given user. * @param mixed $user User object or id * @return boolean indicating whether the page is visible */ public function isVisibleTo($user) { // anyone can see this page if it belongs to a free course if (!$this->config->read_restricted && Config::get()->ENABLE_FREE_ACCESS && $this->course && $this->course->lesezugriff == 0) { return true; } return $GLOBALS['perm']->have_studip_perm( $this->config->read_restricted ? 'tutor' : 'user', $this->range_id, is_object($user) ? $user->id : $user ); } /** * Returns whether this page is editable to the given user. * @param mixed $user User object or id * @return boolean indicating whether the page is editable */ public function isEditableBy($user) { return $GLOBALS['perm']->have_studip_perm( $this->config->edit_restricted ? 'tutor' : 'autor', $this->range_id, is_object($user) ? $user->id : $user ); } /** * Returns whether this page is creatable to the given user. * @param mixed $user User object or id * @return boolean indicating whether the page is creatable * @todo this method is kinda bogus as an instance method */ public function isCreatableBy($user) { return $this->isEditableBy($user); } /** * Returns whether this version of this page is the latest version availabe. * @return boolean */ public function isLatestVersion() { return self::countBySQL( 'range_id = ? AND keyword = ? AND version > ?', [$this->range_id, $this->keyword, $this->version] ) === 0; } /** * Returns the start page of a wiki for a given course. The start page has * the keyword 'WikiWikiWeb'. * * @param string $course_id Course id * @return WikiPage */ public static function getStartPage($course_id) { $start = self::findLatestPage($course_id, ''); if (!$start) { $start = new self([$course_id, 'WikiWikiWeb', 0]); $start->body = _('Dieses Wiki ist noch leer.'); if ($start->isEditableBy($GLOBALS['user'])) { $start->body .= ' ' . _("Bearbeiten Sie es!\nNeue Seiten oder Links werden einfach durch Eingeben von [nop][[Wikinamen]][/nop] in doppelten eckigen Klammern angelegt."); } } return $start; } /** * Export available data of a given user into a storage object * (an instance of the StoredUserData class) for that user. * * @param StoredUserData $storage object to store data into */ public static function exportUserData(StoredUserData $storage) { $sorm = self::findBySQL("user_id = ?", [$storage->user_id]); if ($sorm) { $field_data = []; foreach ($sorm as $row) { $field_data[] = $row->toRawArray(); } if ($field_data) { $storage->addTabularData(_('Wiki Einträge'), 'wiki', $field_data); } } } /** * Sets the parent page for all versions of a Wikipage. * * @param string ancestor Wikipage name to be set as the parent */ public function setAncestorForAllVersions($ancestor) { $query = "UPDATE wiki SET ancestor = ? WHERE range_id = ? AND keyword = ?"; $st = DBManager::get()->prepare($query); $st->execute([$ancestor, $this->range_id, $this->keyword]); } /** * Tests if a given Wikipage name (keyword) is a valid ancestor for this page. * * @param string ancestor Wikipage name to be tested to be an ancestor * @return boolean true if ok, false if not * */ public function isValidAncestor($ancestor) { if ($this->keyword === 'WikiWikiWeb' || $this->keyword === $ancestor) { return false; } $keywords = array_map( function ($descendant) { return $descendant['keyword']; }, $this->getDescendants() ); return !in_array($ancestor, $keywords); } /** * Retrieve an array of all descending WikiPages (recursive). * * @return array Array of all descendant WikiPages * */ public function getDescendants() { $descendants = []; foreach ($this->children as $child) { array_push($descendants, $child, ...$child->getDescendants()); } return $descendants; } /** * @param string $other_keyword * @return bool */ public function isDescendantOf(string $other_keyword): bool { if ($other_keyword === $this->keyword) { return false; } $other_page = WikiPage::findLatestPage($this->range_id, $other_keyword); if ($other_page) { foreach ($other_page->getDescendants() as $descendant) { if ($descendant->keyword === $this->keyword) { return true; } } } return false; } }