getRuntimeObject(MetadataDokuWikiStore::class); } catch (ExceptionNotFound $e) { $executionCachedStores = []; $context->setRuntimeObject(MetadataDokuWikiStore::class, $executionCachedStores); } $path = $resourceCombo->getPathObject()->toAbsoluteId(); if (isset($executionCachedStores[$path])) { return $executionCachedStores[$path]; } $metadataStore = new MetadataDokuWikiStore($resourceCombo); $executionCachedStores[$path] = $metadataStore; return $metadataStore; } /** * * In a rendering, you should not use the {@link p_set_metadata()} * but use {@link \Doku_Renderer_metadata::meta} and {@link \Doku_Renderer_metadata::$persistent} * to set the metadata * * Why ? * The metadata are set in $METADATA_RENDERERS (A global cache variable where the persistent data is set/exist * only during metadata rendering with the function {@link p_render_metadata()}) and then * saved at the end * */ private static function isRendering(string $wikiId): bool { global $METADATA_RENDERERS; if (isset($METADATA_RENDERERS[$wikiId])) { return true; } return false; } /** * Delete the globals */ public static function unsetGlobalVariables() { /** * {@link p_read_metadata() global cache} */ unset($GLOBALS['cache_metadata']); /** * {@link p_render_metadata()} temporary render cache * global $METADATA_RENDERERS; */ unset($GLOBALS['METADATA_RENDERERS']); } /** * @throws ExceptionBadState - if for any reason, it's not possible to store the data */ public function set(Metadata $metadata) { $name = $metadata->getName(); $persistentValue = $metadata->toStoreValue(); $defaultValue = $metadata->toStoreDefaultValue(); $resource = $metadata->getResource(); $this->checkResource($resource); if ($resource === null) { throw new ExceptionBadState("A resource is mandatory", self::CANONICAL); } if (!($resource instanceof MarkupPath)) { throw new ExceptionBadState("The DokuWiki metadata store is only for page resource", self::CANONICAL); } $this->setFromPersistentName($name, $persistentValue, $defaultValue); } /** * @param Metadata $metadata * @param null $default * @return mixed|null */ public function get(Metadata $metadata, $default = null) { $resource = $metadata->getResource(); $this->checkResource($resource); if ($resource === null) { throw new ExceptionRuntime("A resource is mandatory", self::CANONICAL); } if (!($resource instanceof MarkupPath)) { throw new ExceptionRuntime("The DokuWiki metadata store is only for page resource", self::CANONICAL); } return $this->getFromName($metadata->getName(), $default); } /** * * Getting a metadata for a resource via its name * when we don't want to create a class * * This function is used primarily by derived / process metadata * * @param string $name * @param null $default * @return mixed */ public function getFromName(string $name, $default = null) { /** * We don't use {@link p_get_metadata()} * because it will trigger a {@link p_render_metadata()} * But we may just want to check if there is a {@link PageId} * before rendering */ $data = $this->getData(); $value = $data[$name] ?? null; /** * Empty string return null * because Dokuwiki does not allow to delete keys * {@link p_set_metadata()} */ if ($value !== null && $value !== "") { return $value; } return $default; } public function persist() { /** * Done on set via the dokuwiki function */ } /** * @param string $name * @param string|array $value * @param null $default * @return MetadataDokuWikiStore */ public function setFromPersistentName(string $name, $value, $default = null): MetadataDokuWikiStore { $oldValue = $this->getFromName($name); if (is_bool($value) && $oldValue !== null) { $oldValue = DataType::toBoolean($oldValue); } if ($oldValue !== $value) { /** * Metadata in Dokuwiki is fucked up. * * You can't remove a metadata, * You need to know if this is a rendering or not * * See just how fucked {@link p_set_metadata()} is * * Also don't change the type of the value to a string * otherwise dokuwiki will not see a change * between true and a string and will not persist the value * * By default, the value is copied in the current and persistent array * and there is no render */ $wikiId = $this->getWikiId(); if (self::isRendering($wikiId)) { /** * It seems that {@link p_set_metadata()} uses it also * but we show it here */ global $METADATA_RENDERERS; $METADATA_RENDERERS[$wikiId][self::CURRENT_METADATA][$name] = $value; $METADATA_RENDERERS[$wikiId][self::PERSISTENT_DOKUWIKI_KEY][$name] = $value; } else { p_set_metadata($wikiId, [ $name => $value ] ); } $this->setGlobalCacheIfAny($name, $value); } return $this; } public function getData(): array { /** * We return only the current data. * * Why ? * To be consistent with {@link p_get_metadata()} that retrieves only from the `current` array * Therefore the `persistent` array values should always be duplicated in the `current` array * * (the only diff is that the persistent value are still available during a {@link p_render_metadata() metadata render}) * * Note that Dokuwiki load them also for the requested path * at `global $INFO, $info['meta']` with {@link pageinfo()} * and is synced in {@link p_save_metadata()} * */ return $this->getDataCurrentAndPersistent()[self::CURRENT_METADATA]; } private function getWikiId(): string { try { return $this->getResource()->getPathObject()->toWikiPath()->getWikiId(); } catch (ExceptionCast $e) { throw new ExceptionRuntimeInternal("Should not happen", $e); } } /** * * @param $name * @return mixed|null * @deprecated - the data should always be replicated in current use {@link self::getFromName()} */ public function getCurrentFromName($name) { return $this->getFromName($name); } /** * @return MetadataDokuWikiStore * @deprecated should use a fetcher markup ? */ public function renderAndPersist(): MetadataDokuWikiStore { /** * Read/render the metadata from the file * with parsing */ $wikiPage = $this->getResource(); if (!$wikiPage instanceof WikiPath) { LogUtility::errorIfDevOrTest("The resource is not a wiki path"); return $this; } try { FetcherMarkup::confRoot() ->setRequestedExecutingPath($wikiPage) ->setRequestedContextPath($wikiPage) ->setRequestedMimeToMetadata() ->build() ->processMetadataIfNotYetDone(); } catch (ExceptionNotExists $e) { LogUtility::error("Metadata Build Error", self::CANONICAL, $e); } return $this; } public function isHierarchicalTextBased(): bool { return true; } /** * @return Path - the full path to the meta file */ public function getMetaFilePath(): ?Path { $dokuwikiId = $this->getWikiId(); return LocalPath::createFromPathString(metaFN($dokuwikiId, '.meta')); } public function __toString() { return "DokuMeta ({$this->getWikiId()}"; } public function deleteAndFlush() { $emptyMeta = [MetadataDokuWikiStore::CURRENT_METADATA => [], self::PERSISTENT_DOKUWIKI_KEY => []]; $dokuwikiId = $this->getWikiId(); p_save_metadata($dokuwikiId, $emptyMeta); } public function reset() { self::unsetGlobalVariables(); } /** * In {@link p_read_metadata()}, there is a global cache * @param string $name * @param mixed $value */ private function setGlobalCacheIfAny(string $name, $value) { global $cache_metadata; $id = $this->getWikiId(); if (isset($cache_metadata[$id])) { $cache_metadata[$id]['persistent'][$name] = $value; $cache_metadata[$id]['current'][$name] = $value; } } /** * @return array -the full array only needed by the rendering process * You should use {@link self::getData()} otherwise */ public function getDataCurrentAndPersistent(): array { $id = $this->getWikiId(); $data = p_read_metadata($id, true); if (empty($data)) { LogUtility::internalError("The metadata cache was empty"); $data = p_read_metadata($id); } return $data; } }