library: * combo>image: */ const COMBO_DRIVE = "combo"; /** * The home directory for all themes */ const COMBO_DATA_THEME_DRIVE = "combo-theme"; const CACHE_DRIVE = "cache"; const MARKUP_DEFAULT_TXT_EXTENSION = "txt"; const MARKUP_MD_TXT_EXTENSION = "md"; const REV_ATTRIBUTE = "rev"; const CURRENT_PATH_CHARACTER = "."; const CURRENT_PARENT_PATH_CHARACTER = ".."; const CANONICAL = "wiki-path"; const ALL_MARKUP_EXTENSIONS = [self::MARKUP_DEFAULT_TXT_EXTENSION, self::MARKUP_MD_TXT_EXTENSION]; /** * @var string[] */ private static $reservedWords; /** * @var string the path id passed to function (cleaned) */ private $id; /** * @var string */ private $drive; /** * @var string|null - ie mtime */ private $rev; /** * The separator from the {@link WikiPath::getDrive()} */ const DRIVE_SEPARATOR = ">"; /** * @var string - the absolute path (we use it for now to handle directory by adding a separator at the end) */ protected $absolutePath; /** * DokuPath constructor. * * A path for the Dokuwiki File System * * @param string $path - the path (may be relative) * @param string $drive - the drive (media, page, combo) - same as in windows for the drive prefix (c, d, ...) * @param string|null $rev - the revision (mtime) * * Thee path should be a qualified/absolute path because in Dokuwiki, a link to a {@link MarkupPath} * that ends with the {@link WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT} points to a start page * and not to a namespace. The qualification occurs in the transformation * from ref to page. * For a page: in {@link MarkupRef::getInternalPage()} * For a media: in the {@link MediaLink::createMediaLinkFromId()} * Because this class is mostly the file representation, it should be able to * represents also a namespace */ protected function __construct(string $path, string $drive, string $rev = null) { $executionContext = ExecutionContext::getActualOrCreateFromEnv(); /** * Due to the fact that the request environment is set on the setup in test, * the path may be not normalized */ $path = self::normalizeWikiPath($path); if (trim($path) === "") { try { $path = WikiPath::getContextPath()->toAbsoluteId(); } catch (ExceptionNotFound $e) { throw new ExceptionRuntimeInternal("The context path is unknwon. The empty path string needs it."); } } /** * Relative Path ? */ $this->absolutePath = $path; $firstCharacter = substr($path, 0, 1); if ($drive === self::MARKUP_DRIVE && $firstCharacter !== WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT) { $parts = preg_split('/' . WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT . '/', $path); switch ($parts[0]) { case WikiPath::CURRENT_PATH_CHARACTER: // delete the relative character $parts = array_splice($parts, 1); try { $rootRelativePath = $executionContext->getContextNamespacePath(); } catch (ExceptionNotFound $e) { // Root case: the relative path is in the root // the root has no parent LogUtility::error("The current relative path ({$this->absolutePath}) returns an error: {$e->getMessage()}", self::CANONICAL); $rootRelativePath = WikiPath::createMarkupPathFromPath(WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT); } break; case WikiPath::CURRENT_PARENT_PATH_CHARACTER: // delete the relative character $parts = array_splice($parts, 1); $currentPagePath = $executionContext->getContextNamespacePath(); try { $rootRelativePath = $currentPagePath->getParent(); } catch (ExceptionNotFound $e) { LogUtility::error("The parent relative path ({$this->absolutePath}) returns an error: {$e->getMessage()}", self::CANONICAL); $rootRelativePath = $executionContext->getContextNamespacePath(); } break; default: /** * just a relative name path * (ie hallo) */ $rootRelativePath = $executionContext->getContextNamespacePath(); break; } // is relative directory path ? // ie ..: or .: $isRelativeDirectoryPath = false; $countParts = sizeof($parts); if ($countParts > 0 && $parts[$countParts - 1] === "") { $isRelativeDirectoryPath = true; $parts = array_splice($parts, 0, $countParts - 1); } foreach ($parts as $part) { $rootRelativePath = $rootRelativePath->resolve($part); } $absolutePathString = $rootRelativePath->getAbsolutePath(); if ($isRelativeDirectoryPath && !WikiPath::isNamespacePath($absolutePathString)) { $absolutePathString = $absolutePathString . WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT; } $this->absolutePath = $absolutePathString; } /** * ACL check does not care about the type of id * https://www.dokuwiki.org/devel:event:auth_acl_check * https://github.com/splitbrain/dokuwiki/issues/3476 * * We check if there is an extension * If this is the case, this is a media */ if ($drive === self::UNKNOWN_DRIVE) { $lastPosition = StringUtility::lastIndexOf($path, "."); if ($lastPosition === FALSE) { $drive = self::MARKUP_DRIVE; } else { $drive = self::MEDIA_DRIVE; } } $this->drive = $drive; /** * We use interwiki to define the combo resources * (Internal use only) */ $comboInterWikiScheme = "combo>"; if (strpos($this->absolutePath, $comboInterWikiScheme) === 0) { $pathPart = substr($this->absolutePath, strlen($comboInterWikiScheme)); $this->id = $this->toDokuWikiIdDriveContextual($pathPart); $this->drive = self::COMBO_DRIVE; } else { WikiPath::addRootSeparatorIfNotPresent($this->absolutePath); $this->id = $this->toDokuWikiIdDriveContextual($this->absolutePath); } $this->rev = $rev; } /** * For a Markup drive path, a file path should have an extension * if it's not a namespace * * This function checks that * * @param string $parameterPath - the path in a wiki form that may be relative - if the path is blank, it's the current markup (the requested markup) * @param string|null $rev - the revision (ie timestamp in number format) * @return WikiPath - the wiki path * @throws ExceptionBadArgument - if a relative path is given and the context path does not have any parent */ public static function createMarkupPathFromPath(string $parameterPath, string $rev = null): WikiPath { $executionContext = ExecutionContext::getActualOrCreateFromEnv(); if ($parameterPath == "") { return $executionContext->getContextPath(); } if (WikiPath::isNamespacePath($parameterPath)) { if ($parameterPath[0] !== self::CURRENT_PATH_CHARACTER) { /** * Not a relative path */ return new WikiPath($parameterPath, self::MARKUP_DRIVE, $rev); } /** * A relative path */ $contextPath = $executionContext->getContextPath(); if ($parameterPath === self::CURRENT_PARENT_PATH_CHARACTER . self::NAMESPACE_SEPARATOR_DOUBLE_POINT) { /** * ie processing `..:` */ try { return $contextPath->getParent()->getParent(); } catch (ExceptionNotFound $e) { throw new ExceptionBadArgument("The context path ($contextPath) does not have a grand parent, therefore the relative path ($parameterPath) is invalid.", $e); } } /** * ie processing `.:` */ try { return $contextPath->getParent(); } catch (ExceptionNotFound $e) { LogUtility::internalError("A context path is a page and should therefore have a parent", $e); } } /** * Default Path * (we add the txt extension if not present) */ $defaultPath = $parameterPath; $lastName = $parameterPath; $lastSeparator = strrpos($parameterPath, self::NAMESPACE_SEPARATOR_DOUBLE_POINT); if ($lastSeparator !== false) { $lastName = substr($parameterPath, $lastSeparator); } $lastPoint = strpos($lastName, "."); if ($lastPoint === false) { $defaultPath = $defaultPath . '.' . self::MARKUP_DEFAULT_TXT_EXTENSION; } else { /** * Case such as file `1.22` */ $parameterPathExtension = substr($lastName, $lastPoint + 1); if (!in_array($parameterPathExtension, self::ALL_MARKUP_EXTENSIONS)) { $defaultPath = $defaultPath . '.' . self::MARKUP_DEFAULT_TXT_EXTENSION; } } $defaultWikiPath = new WikiPath($defaultPath, self::MARKUP_DRIVE, $rev); if (FileSystems::exists($defaultWikiPath)) { return $defaultWikiPath; } /** * Markup extension (Markdown, ...) */ if (!isset($parameterPathExtension)) { foreach (self::ALL_MARKUP_EXTENSIONS as $markupExtension) { if ($markupExtension == self::MARKUP_DEFAULT_TXT_EXTENSION) { continue; } $markupWikiPath = new WikiPath($parameterPath . '.' . $markupExtension, self::MARKUP_DRIVE, $rev); if (FileSystems::exists($markupWikiPath)) { return $markupWikiPath; } } } /** * Return the non-existen default wiki path */ return $defaultWikiPath; } public static function createMediaPathFromPath($path, $rev = null): WikiPath { return new WikiPath($path, WikiPath::MEDIA_DRIVE, $rev); } /** * If the media may come from the * dokuwiki media or combo resources media, * you should use this function * * The constructor will determine the type based on * the id structure. * @param $id * @return WikiPath */ public static function createFromUnknownRoot($id): WikiPath { return new WikiPath($id, WikiPath::UNKNOWN_DRIVE); } /** * @param $url - a URL path http://whatever/hello/my/lord (The canonical) * @return WikiPath - a dokuwiki Id hello:my:lord * @deprecated for {@link FetcherPage::createPageFragmentFetcherFromUrl()} */ public static function createFromUrl($url): WikiPath { // Replace / by : and suppress the first : because the global $ID does not have it $parsedQuery = parse_url($url, PHP_URL_QUERY); $parsedQueryArray = []; parse_str($parsedQuery, $parsedQueryArray); $queryId = 'id'; if (array_key_exists($queryId, $parsedQueryArray)) { // Doku form (ie doku.php?id=) $id = $parsedQueryArray[$queryId]; } else { // Slash form ie (/my/id) $urlPath = parse_url($url, PHP_URL_PATH); $id = substr(str_replace("/", ":", $urlPath), 1); } return self::createMarkupPathFromPath(":$id"); } /** * Static don't ask why * @param $pathId * @return false|string */ public static function getLastPart($pathId) { $endSeparatorLocation = StringUtility::lastIndexOf($pathId, WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT); if ($endSeparatorLocation === false) { $endSeparatorLocation = StringUtility::lastIndexOf($pathId, WikiPath::NAMESPACE_SEPARATOR_SLASH); } if ($endSeparatorLocation === false) { $lastPathPart = $pathId; } else { $lastPathPart = substr($pathId, $endSeparatorLocation + 1); } return $lastPathPart; } /** * @param $id * @return string * Return an path from a id */ public static function IdToAbsolutePath($id) { if (is_null($id)) { LogUtility::msg("The id passed should not be null"); } return WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT . $id; } function toDokuWikiIdDriveContextual($path): string { /** * Delete the first separator */ $id = self::removeRootSepIfPresent($path); /** * If this is a markup, we delete the txt extension if any */ if ($this->getDrive() === self::MARKUP_DRIVE) { StringUtility::rtrim($id, '.' . self::MARKUP_DEFAULT_TXT_EXTENSION); } return $id; } public static function createMediaPathFromId($id, $rev = null): WikiPath { WikiPath::addRootSeparatorIfNotPresent($id); return self::createMediaPathFromPath($id, $rev); } public static function getComboCustomThemeHomeDirectory(): WikiPath { return new WikiPath(self::NAMESPACE_SEPARATOR_DOUBLE_POINT, self::COMBO_DATA_THEME_DRIVE); } /** * @throws ExceptionBadArgument */ public static function createFromUri(string $uri): WikiPath { $schemeQualified = WikiFileSystem::SCHEME . "://"; $lengthSchemeQualified = strlen($schemeQualified); $uriScheme = substr($uri, 0, $lengthSchemeQualified); if ($uriScheme !== $schemeQualified) { throw new ExceptionBadArgument("The uri ($uri) is not a wiki uri"); } $uriWithoutScheme = substr($uri, $lengthSchemeQualified); $locationQuestionMark = strpos($uriWithoutScheme, "?"); if ($locationQuestionMark === false) { $pathAndDrive = $uriWithoutScheme; $rev = ''; } else { $pathAndDrive = substr($uriWithoutScheme, 0, $locationQuestionMark); $query = substr($uriWithoutScheme, $locationQuestionMark + 1); parse_str($query, $queryKeys); $queryKeys = new ArrayCaseInsensitive($queryKeys); $rev = $queryKeys['rev']; } $locationGreaterThan = strpos($pathAndDrive, ">"); if ($locationGreaterThan === false) { $path = $pathAndDrive; $locationLastPoint = strrpos($pathAndDrive, "."); if ($locationLastPoint === false) { $drive = WikiPath::MARKUP_DRIVE; } else { $extension = substr($pathAndDrive, $locationLastPoint + 1); if (in_array($extension, WikiPath::ALL_MARKUP_EXTENSIONS)) { $drive = WikiPath::MARKUP_DRIVE; } else { $drive = WikiPath::MEDIA_DRIVE; } } } else { $drive = substr($pathAndDrive, 0, $locationGreaterThan); $path = substr($pathAndDrive, $locationGreaterThan + 1); } return new WikiPath(":$path", $drive, $rev); } public static function createMarkupPathFromId($id, $rev = null): WikiPath { if (strpos($id, WikiFileSystem::SCHEME . "://") !== false) { return WikiPath::createFromUri($id); } WikiPath::addRootSeparatorIfNotPresent($id); return self::createMarkupPathFromPath($id); } /** * If the id does not have a root separator, * it's added (ie to transform an id to a path) * @param string $path */ public static function addRootSeparatorIfNotPresent(string &$path) { $firstCharacter = substr($path, 0, 1); if (!in_array($firstCharacter, [WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT, WikiPath::CURRENT_PATH_CHARACTER])) { $path = WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT . $path; } } /** * @param string $relativePath * @return string - a dokuwiki path (replacing the windows or linux path separator to the dokuwiki separator) */ public static function toDokuWikiSeparator(string $relativePath): string { return preg_replace('/[\\\\\/]/', ":", $relativePath); } /** * @param $path - a manual path value * @return string - a valid path */ public static function toValidAbsolutePath($path): string { $path = cleanID($path); WikiPath::addRootSeparatorIfNotPresent($path); return $path; } /** */ public static function createComboResource($stringPath): WikiPath { return new WikiPath($stringPath, self::COMBO_DRIVE); } /** * @param $path - relative or absolute path * @param $drive - the drive * @param string $rev - the revision * @return WikiPath */ public static function createWikiPath($path, $drive, string $rev = ''): WikiPath { return new WikiPath($path, $drive, $rev); } /** * The executing markup * @throws ExceptionNotFound */ public static function createExecutingMarkupWikiPath(): WikiPath { return ExecutionContext::getActualOrCreateFromEnv() ->getExecutingWikiPath(); } /** * @throws ExceptionNotFound */ public static function createRequestedPagePathFromRequest(): WikiPath { return ExecutionContext::getActualOrCreateFromEnv()->getRequestedPath(); } /** * @throws ExceptionBadArgument - if the path is not a local path or is not in a known drive */ public static function createFromPathObject(Path $path): WikiPath { if ($path instanceof WikiPath) { return $path; } if (!($path instanceof LocalPath)) { throw new ExceptionBadArgument("The path ($path) is not a local path and cannot be converted to a wiki path"); } $driveRoots = WikiPath::getDriveRoots(); foreach ($driveRoots as $driveRoot => $drivePath) { try { $relativePath = $path->relativize($drivePath); } catch (ExceptionBadArgument $e) { /** * The drive may be a symlink link * (not the path) */ if (!$drivePath->isSymlink()) { continue; } try { $drivePath = $drivePath->toCanonicalAbsolutePath(); $relativePath = $path->relativize($drivePath); } catch (ExceptionBadArgument $e) { // not a relative path continue; } } $wikiId = $relativePath->toAbsoluteId(); if (FileSystems::isDirectory($path)) { WikiPath::addNamespaceEndSeparatorIfNotPresent($wikiId); } WikiPath::addRootSeparatorIfNotPresent($wikiId); return WikiPath::createWikiPath($wikiId, $driveRoot); } throw new ExceptionBadArgument("The local path ($path) is not inside a wiki path drive"); } /** * @return LocalPath[] */ public static function getDriveRoots(): array { return [ self::MEDIA_DRIVE => Site::getMediaDirectory(), self::MARKUP_DRIVE => Site::getPageDirectory(), self::COMBO_DRIVE => DirectoryLayout::getComboResourcesDirectory(), self::COMBO_DATA_THEME_DRIVE => Site::getDataDirectory()->resolve("combo")->resolve("theme"), self::CACHE_DRIVE => Site::getCacheDirectory() ]; } /** * * Wiki path system cannot make the difference between a txt file * and a directory natively because there is no extension. * * ie `ns:name` is by default the file `ns:name.txt` * * To make this distinction, we add a `:` at the end * * TODO: May be ? We may also just check if the txt file exists * and if not if the directory exists * * Also related {@link WikiPath::addNamespaceEndSeparatorIfNotPresent()} * * @param string $namespacePath * @return bool */ public static function isNamespacePath(string $namespacePath): bool { if (substr($namespacePath, -1) !== WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT) { return false; } return true; } /** * @throws ExceptionBadSyntax */ public static function checkNamespacePath(string $namespacePath) { if (!self::isNamespacePath($namespacePath)) { throw new ExceptionBadSyntax("The path ($namespacePath) is not a namespace path"); } } /** * Add a end separator to the wiki path to pass the fact that this is a directory/namespace * See {@link WikiPath::isNamespacePath()} for more info * * @param string $namespaceAttribute * @return void */ public static function addNamespaceEndSeparatorIfNotPresent(string &$namespaceAttribute) { if (substr($namespaceAttribute, -1) !== WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT) { $namespaceAttribute = $namespaceAttribute . WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT; } } /** * @param string $path - path or id * @param string $drive * @param string|null $rev * @return WikiPath */ public static function createFromPath(string $path, string $drive, string $rev = null): WikiPath { return new WikiPath($path, $drive, $rev); } public static function getContextPath(): WikiPath { return ExecutionContext::getActualOrCreateFromEnv()->getContextPath(); } /** * Normalize a valid id * (ie from / to :) * * @param string $id * @return array|string|string[] * * This is not the same than {@link MarkupRef::normalizePath()} * because there is no relativity or any reserved character in a id * * as an {@link WikiPath::getWikiId() id} is a validated absolute path without root character */ public static function normalizeWikiPath(string $id) { return str_replace(WikiPath::NAMESPACE_SEPARATOR_SLASH, WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT, $id); } public static function createRootNamespacePathOnMarkupDrive(): WikiPath { return WikiPath::createMarkupPathFromPath(self::NAMESPACE_SEPARATOR_DOUBLE_POINT); } /** * @param $path * @return string with the root path */ public static function removeRootSepIfPresent($path): string { $id = $path; if ($id[0] === WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT) { return substr($id, 1); } return $id; } /** * The last part of the path * @throws ExceptionNotFound */ public function getLastName(): string { /** * See also {@link noNSorNS} */ $names = $this->getNames(); $lastName = $names[sizeOf($names) - 1] ?? null; if ($lastName === null) { throw new ExceptionNotFound("This path ($this) does not have any last name"); } return $lastName; } public function getNames(): array { $actualNames = explode(self::NAMESPACE_SEPARATOR_DOUBLE_POINT, $this->absolutePath); /** * First element can be an empty string * Case of only one string without path separator * the first element returned is an empty string * Last element can be empty (namespace split, ie :ns:) */ $names = []; foreach ($actualNames as $name) { /** * Don't use the {@link empty()} function * In the cache, we may have the directory '0' * and it's empty but is valid name */ if ($name !== "") { $names[] = $name; } } return $names; } /** * @return bool true if this id represents a page */ public function isPage(): bool { if ( $this->drive === self::MARKUP_DRIVE && !$this->isGlob() ) { return true; } else { return false; } } public function isGlob(): bool { /** * {@link search_universal} triggers ACL check * with id of the form :path:* * (for directory ?) */ return StringUtility::endWiths($this->getWikiId(), ":*"); } public function __toString() { return $this->toUriString(); } /** * * * @return string - the wiki id is the absolute path * without the root separator (ie normalized) * * The index stores needs this value * And most of the function that are not links related * use this format (What fucked up is fucked up) * * The id is a validated absolute path without any root character. * * Heavily used inside Dokuwiki */ public function getWikiId(): string { return $this->id; } public function getPath(): string { return $this->absolutePath; } public function getScheme(): string { return WikiFileSystem::SCHEME; } /** * The wiki revision value * as seen in the {@link basicinfo()} function * is the {@link File::getModifiedTime()} of the file * * Let op passing a revision to Dokuwiki will * make it search to the history * The actual file will then not be found * * @return string|null * @throws ExceptionNotFound */ public function getRevision(): string { /** * Empty because the value may be null or empty string */ if (empty($this->rev)) { throw new ExceptionNotFound("The rev was not set"); } return $this->rev; } /** * * @throws ExceptionNotFound - if the revision is not set and the path does not exist */ public function getRevisionOrDefault() { try { return $this->getRevision(); } catch (ExceptionNotFound $e) { // same as $INFO['lastmod']; return FileSystems::getModifiedTime($this)->getTimestamp(); } } /** * @return string * * This is the local absolute path WITH the root separator. * It's used in ref present in {@link MarkupRef link} or {@link MediaLink} * when creating test, otherwise the ref is considered as relative * * * Otherwise everywhere in Dokuwiki, they use the {@link WikiPath::getWikiId()} absolute value that does not have any root separator * and is absolute (internal index, function, ...) * */ public function getAbsolutePath(): string { return $this->absolutePath; } /** * @return array the pages where the wiki file (page or media) is used * * backlinks for page * * page with media for media */ public function getReferencedBy(): array { $absoluteId = $this->getWikiId(); if ($this->drive == self::MEDIA_DRIVE) { return idx_get_indexer()->lookupKey('relation_media', $absoluteId); } else { return idx_get_indexer()->lookupKey('relation_references', $absoluteId); } } /** * Return the path relative to the base directory * (ie $conf[basedir]) * @return string */ public function toRelativeFileSystemPath(): string { $relativeSystemPath = "."; if (!empty($this->getWikiId())) { $relativeSystemPath .= "/" . utf8_encodeFN(str_replace(':', '/', $this->getWikiId())); } return $relativeSystemPath; } public function isPublic(): bool { return $this->getAuthAclValue() >= AUTH_READ; } /** * @return int - An AUTH_ value for this page for the current logged user * See the file defines.php * */ public function getAuthAclValue(): int { return auth_quickaclcheck($this->getWikiId()); } public static function getReservedWords(): array { if (self::$reservedWords == null) { self::$reservedWords = array_merge(Url::RESERVED_WORDS, LocalPath::RESERVED_WINDOWS_CHARACTERS); } return self::$reservedWords; } /** * The absolute path for a wiki path * @return string - the wiki id with a root separator */ function toAbsoluteId(): string { return self::NAMESPACE_SEPARATOR_DOUBLE_POINT . $this->getWikiId(); } function toAbsolutePath(): Path { return new WikiPath($this->absolutePath, $this->drive, $this->rev); } /** * The parent path is a directory (namespace) * The root path throw an errors * * @return WikiPath * @throws ExceptionNotFound when the root */ function getParent(): Path { /** * Same as {@link getNS()} */ $names = $this->getNames(); switch (sizeof($names)) { case 0: throw new ExceptionNotFound("The path `{$this}` does not have any parent"); case 1: return new WikiPath(WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT, $this->drive, $this->rev); default: $names = array_slice($names, 0, sizeof($names) - 1); $path = implode(WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT, $names); /** * Because DokuPath does not have the notion of extension * if this is a page, we don't known if this is a directory * or a page. To make the difference, we add a separator at the end */ $sep = self::NAMESPACE_SEPARATOR_DOUBLE_POINT; $path = "$sep$path$sep"; return new WikiPath($path, $this->drive, $this->rev); } } /** * @throws ExceptionNotFound */ function getMime(): Mime { if ($this->drive === self::MARKUP_DRIVE) { return new Mime(Mime::PLAIN_TEXT); } return FileSystems::getMime($this); } public function getDrive(): string { return $this->drive; } public function resolve(string $name): WikiPath { // Directory path have already separator at the end, don't add it if ($this->absolutePath[strlen($this->absolutePath) - 1] !== WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT) { $path = $this->absolutePath . WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT . $name; } else { $path = $this->absolutePath . $name; } return new WikiPath($path, $this->getDrive()); } function toUriString(): string { $driveSep = self::DRIVE_SEPARATOR; $absolutePath = self::removeRootSepIfPresent($this->absolutePath); $uri = "{$this->getScheme()}://$this->drive$driveSep$absolutePath"; if (!empty($this->rev)) { $uri = "$uri?rev={$this->rev}"; } return $uri; } function getUrl(): Url { return $this->toLocalPath()->getUrl(); } function getHost(): string { return "localhost"; } public function resolveId($markupId): WikiPath { if ($this->getDrive() !== self::MARKUP_DRIVE) { return $this->resolve($markupId); } if (!WikiPath::isNamespacePath($this->absolutePath)) { try { $contextId = $this->getParent()->getWikiId() . self::NAMESPACE_SEPARATOR_DOUBLE_POINT; } catch (ExceptionNotFound $e) { $contextId = ""; } } else { $contextId = $this->getWikiId(); } return WikiPath::createMarkupPathFromId($contextId . $markupId); } /** * @return LocalPath * TODO: change it for a constructor on LocalPath * @throws ExceptionCast */ public function toLocalPath(): LocalPath { /** * File path */ $isNamespacePath = self::isNamespacePath($this->absolutePath); if ($isNamespacePath) { /** * Namespace * (Fucked up is fucked up) * We qualify for the namespace here * because there is no link or media for a namespace */ global $conf; switch ($this->drive) { case self::MEDIA_DRIVE: $localPath = LocalPath::createFromPathString($conf['mediadir']); break; case self::MARKUP_DRIVE: $localPath = LocalPath::createFromPathString($conf['datadir']); break; default: $localPath = WikiPath::getDriveRoots()[$this->drive]; break; } foreach ($this->getNames() as $name) { $localPath = $localPath->resolve($name); } return $localPath; } // File switch ($this->drive) { case self::MEDIA_DRIVE: if (!empty($rev)) { $filePathString = mediaFN($this->id, $rev); } else { $filePathString = mediaFN($this->id); } break; case self::MARKUP_DRIVE: /** * Adaptation of {@link WikiFN} */ global $conf; try { $extension = $this->getExtension(); } catch (ExceptionNotFound $e) { LogUtility::internalError("For a markup path file, the extension should have been set. This is not the case for ($this)"); $extension = self::MARKUP_DEFAULT_TXT_EXTENSION; } $idFileSystem = str_replace(':', '/', $this->id); if (empty($this->rev)) { $filePathString = Site::getPageDirectory()->resolve(utf8_encodeFN($idFileSystem) . '.' . $extension)->toAbsoluteId(); } else { $filePathString = Site::getOldDirectory()->resolve(utf8_encodeFN($idFileSystem) . '.' . $this->rev . '.' . $extension)->toAbsoluteId(); if ($conf['compression']) { //test for extensions here, we want to read both compressions if (file_exists($filePathString . '.gz')) { $filePathString .= '.gz'; } elseif (file_exists($filePathString . '.bz2')) { $filePathString .= '.bz2'; } else { // File doesnt exist yet, so we take the configured extension $filePathString .= '.' . $conf['compression']; } } } break; default: $baseDirectory = WikiPath::getDriveRoots()[$this->drive]; if ($baseDirectory === null) { // We don't throw, the file will just not exist // this is metadata throw new ExceptionCast("The drive ($this->drive) is unknown, the local file system path could not be found"); } $filePath = $baseDirectory; foreach ($this->getNames() as $name) { $filePath = $filePath->resolve($name); } $filePathString = $filePath->toAbsoluteId(); break; } return LocalPath::createFromPathString($filePathString); } public function hasRevision(): bool { try { $this->getRevision(); return true; } catch (ExceptionNotFound $e) { return false; } } }