xref: /plugin/combo/ComboStrap/MetadataFrontmatterStore.php (revision 70bbd7f1f72440223cc13f3495efdcb2b0a11514)
1c3437056SNickeau<?php
2c3437056SNickeau
3c3437056SNickeau
4c3437056SNickeaunamespace ComboStrap;
5c3437056SNickeau
6c3437056SNickeau
704fd306cSNickeauuse ComboStrap\Meta\Api\Metadata;
804fd306cSNickeauuse ComboStrap\Meta\Api\MetadataStore;
904fd306cSNickeauuse ComboStrap\Meta\Store\MetadataDokuWikiStore;
10c3437056SNickeauuse syntax_plugin_combo_frontmatter;
11c3437056SNickeau
1204fd306cSNickeau/**
1304fd306cSNickeau * A page represented as a frontmatter
1404fd306cSNickeau *
1504fd306cSNickeau * By giving a page to the constructor, you can:
1604fd306cSNickeau *   * change the content of the frontmatter
1704fd306cSNickeau *   * add a header/frontmatter to the content
1804fd306cSNickeau *   * and persist
1904fd306cSNickeau * MetadataFrontmatterStore
2004fd306cSNickeau * @package ComboStrap
2104fd306cSNickeau */
22c3437056SNickeauclass MetadataFrontmatterStore extends MetadataSingleArrayStore
23c3437056SNickeau{
24c3437056SNickeau
25c3437056SNickeau    const NAME = "frontmatter";
26c3437056SNickeau    const CANONICAL = self::NAME;
2704fd306cSNickeau    public const CONF_ENABLE_FRONT_MATTER_ON_SUBMIT = "enableFrontMatterOnSubmit";
28c3437056SNickeau
29c3437056SNickeau    /**
30c3437056SNickeau     * @var bool Do we have a frontmatter on the page
31c3437056SNickeau     */
3204fd306cSNickeau    private bool $isPresent = false;
33c3437056SNickeau    /**
34c3437056SNickeau     * @var string
35c3437056SNickeau     */
3604fd306cSNickeau    private string $contentWithoutFrontMatter;
37c3437056SNickeau
38c3437056SNickeau    /**
3904fd306cSNickeau     * @throws ExceptionCompile
40c3437056SNickeau     */
41c3437056SNickeau    private function syncData()
42c3437056SNickeau    {
43c3437056SNickeau
44c3437056SNickeau        /**
4504fd306cSNickeau         * @var MarkupPath $resourceCombo
46c3437056SNickeau         */
47c3437056SNickeau        $resourceCombo = $this->getResource();
48c3437056SNickeau
49c3437056SNickeau        /**
50c3437056SNickeau         * Resource Id special
51c3437056SNickeau         */
52c3437056SNickeau        $guidObject = $resourceCombo->getUidObject();
5304fd306cSNickeau        try {
5404fd306cSNickeau            $guidValue = $guidObject->getValue();
5504fd306cSNickeau        } catch (ExceptionNotFound $e) {
5604fd306cSNickeau            $guidValue = null;
5704fd306cSNickeau        }
58c3437056SNickeau        if (
59c3437056SNickeau            !$this->hasProperty($guidObject::getPersistentName())
60c3437056SNickeau            &&
6104fd306cSNickeau            $guidValue !== null
62c3437056SNickeau        ) {
6304fd306cSNickeau            $this->setFromPersistentName($guidObject::getPersistentName(), $guidValue);
64c3437056SNickeau        }
65c3437056SNickeau
66c3437056SNickeau        /**
67c3437056SNickeau         * Read store
68c3437056SNickeau         */
69c3437056SNickeau        $dokuwikiStore = MetadataDokuWikiStore::getOrCreateFromResource($resourceCombo);
70c3437056SNickeau        $metaFilePath = $dokuwikiStore->getMetaFilePath();
71c3437056SNickeau        if ($metaFilePath !== null) {
72c3437056SNickeau            $metaModifiedTime = FileSystems::getModifiedTime($metaFilePath);
7304fd306cSNickeau            $pageModifiedTime = FileSystems::getModifiedTime($resourceCombo->getPathObject());
7404fd306cSNickeau            $older = Iso8601Date::createFromDateTime($pageModifiedTime)->olderThan($metaModifiedTime);
7504fd306cSNickeau            if (!$older) {
7604fd306cSNickeau                /**
7704fd306cSNickeau                 * Weird case that may happen
7804fd306cSNickeau                 */
79c3437056SNickeau                $resourceCombo->renderMetadataAndFlush();
80c3437056SNickeau            }
81c3437056SNickeau        }
8204fd306cSNickeau
83c3437056SNickeau        /**
84c3437056SNickeau         * Update the mutable data
85c3437056SNickeau         * (ie delete insert)
86c3437056SNickeau         */
8704fd306cSNickeau        foreach (Meta\Api\MetadataSystem::getMutableMetadata() as $metadata) {
8804fd306cSNickeau
89c3437056SNickeau            $metadata
90c3437056SNickeau                ->setResource($resourceCombo)
91c3437056SNickeau                ->setReadStore($dokuwikiStore)
92c3437056SNickeau                ->setWriteStore($this);
93c3437056SNickeau
94c3437056SNickeau            $sourceValue = $this->get($metadata);
9504fd306cSNickeau            try {
96c3437056SNickeau                $targetValue = $metadata->getValue();
9704fd306cSNickeau            } catch (ExceptionNotFound $e) {
9804fd306cSNickeau                $targetValue = null;
9904fd306cSNickeau            }
10004fd306cSNickeau            try {
101c3437056SNickeau                $defaultValue = $metadata->getDefaultValue();
10204fd306cSNickeau            } catch (ExceptionNotFound $e) {
10304fd306cSNickeau                $defaultValue = null;
10404fd306cSNickeau            }
105c3437056SNickeau            /**
10604fd306cSNickeau             * Strict because otherwise the comparison `false == null` is true
107c3437056SNickeau             */
108c3437056SNickeau            $targetValueShouldBeStore = !in_array($targetValue, [$defaultValue, null], true);
109c3437056SNickeau            if ($targetValueShouldBeStore) {
110c3437056SNickeau                if ($sourceValue !== $targetValue) {
111c3437056SNickeau                    $this->set($metadata);
112c3437056SNickeau                }
113c3437056SNickeau            } else {
114c3437056SNickeau                if ($sourceValue !== null) {
115c3437056SNickeau                    $this->remove($metadata);
116c3437056SNickeau                }
117c3437056SNickeau            }
118c3437056SNickeau        }
119c3437056SNickeau    }
120c3437056SNickeau
121c3437056SNickeau    /**
122c3437056SNickeau     * Update the frontmatter with the managed metadata
123c3437056SNickeau     * Used after a submit from the form
124c3437056SNickeau     * @return Message
125c3437056SNickeau     */
126c3437056SNickeau    public function sync(): Message
127c3437056SNickeau    {
128c3437056SNickeau
129c3437056SNickeau        /**
130c3437056SNickeau         * Default update value for the frontmatter
131c3437056SNickeau         */
13204fd306cSNickeau        $updateFrontMatter = SiteConfig::getConfValue(self::CONF_ENABLE_FRONT_MATTER_ON_SUBMIT, syntax_plugin_combo_frontmatter::CONF_ENABLE_FRONT_MATTER_ON_SUBMIT_DEFAULT);
133c3437056SNickeau
134c3437056SNickeau
135c3437056SNickeau        if ($this->isPresent()) {
136c3437056SNickeau            $updateFrontMatter = 1;
137c3437056SNickeau        }
138c3437056SNickeau
139c3437056SNickeau
140c3437056SNickeau        if ($updateFrontMatter === 0) {
141c3437056SNickeau            return Message::createInfoMessage("The frontmatter is not enabled")
142c3437056SNickeau                ->setStatus(syntax_plugin_combo_frontmatter::UPDATE_EXIT_CODE_NOT_ENABLED);
143c3437056SNickeau        }
144c3437056SNickeau
145c3437056SNickeau        try {
146c3437056SNickeau            $this->syncData();
14704fd306cSNickeau        } catch (ExceptionCompile $e) {
14804fd306cSNickeau            if (PluginUtility::isDevOrTest()) {
14904fd306cSNickeau                throw new ExceptionRuntime("Error while synchronizing data in the frontmatter", self::CANONICAL, 1, $e);
15004fd306cSNickeau            }
151c3437056SNickeau            return Message::createInfoMessage($e->getMessage())
152c3437056SNickeau                ->setStatus(syntax_plugin_combo_frontmatter::UPDATE_EXIT_CODE_ERROR);
153c3437056SNickeau        }
154c3437056SNickeau
155c3437056SNickeau
156c3437056SNickeau        /**
157c3437056SNickeau         * Same ?
158c3437056SNickeau         */
159c3437056SNickeau        if (!$this->hasStateChanged()) {
160c3437056SNickeau            return Message::createInfoMessage("The frontmatter are the same (no update)")
161c3437056SNickeau                ->setStatus(syntax_plugin_combo_frontmatter::UPDATE_EXIT_CODE_NOT_CHANGED);
162c3437056SNickeau        }
163c3437056SNickeau
164c3437056SNickeau        $this->persist();
165c3437056SNickeau
166c3437056SNickeau        return Message::createInfoMessage("The frontmatter was changed")
167c3437056SNickeau            ->setStatus(syntax_plugin_combo_frontmatter::UPDATE_EXIT_CODE_DONE);
168c3437056SNickeau
169c3437056SNickeau    }
170c3437056SNickeau
171c3437056SNickeau
172c3437056SNickeau    public function isPresent(): bool
173c3437056SNickeau    {
174c3437056SNickeau        return $this->isPresent;
175c3437056SNickeau    }
176c3437056SNickeau
177c3437056SNickeau    /**
178c3437056SNickeau     * MetadataFrontmatterStore constructor.
179c3437056SNickeau     * @param ResourceCombo $page
180c3437056SNickeau     * @param array|null $data
181c3437056SNickeau     */
182c3437056SNickeau    public function __construct(ResourceCombo $page, array $data = null)
183c3437056SNickeau    {
184c3437056SNickeau        parent::__construct($page, $data);
185c3437056SNickeau    }
186c3437056SNickeau
187c3437056SNickeau    /**
188c3437056SNickeau     * @param $match
189c3437056SNickeau     * @return array|null - null if decodage problem, empty array if no json or an associative array
190c3437056SNickeau     * @deprecated used {@link MetadataFrontmatterStore::loadAsString()} instead
191c3437056SNickeau     */
192c3437056SNickeau    public static function frontMatterMatchToAssociativeArray($match): ?array
193c3437056SNickeau    {
194c3437056SNickeau        $jsonString = self::stripFrontmatterTag($match);
195c3437056SNickeau
196c3437056SNickeau        // Empty front matter
197c3437056SNickeau        if (trim($jsonString) == "") {
198c3437056SNickeau            return [];
199c3437056SNickeau        }
200c3437056SNickeau
201c3437056SNickeau        // Otherwise you get an object ie $arrayFormat-> syntax
202c3437056SNickeau        $arrayFormat = true;
203c3437056SNickeau        return json_decode($jsonString, $arrayFormat);
204c3437056SNickeau    }
205c3437056SNickeau
206c3437056SNickeau    public static function stripFrontmatterTag($match)
207c3437056SNickeau    {
208c3437056SNickeau        // strip
209c3437056SNickeau        //   from start `---json` + eol = 8
210c3437056SNickeau        //   from end   `---` + eol = 4
211c3437056SNickeau        return substr($match, 7, -3);
212c3437056SNickeau    }
213c3437056SNickeau
214c3437056SNickeau
215c3437056SNickeau    public static function getOrCreateFromResource(ResourceCombo $resourceCombo): MetadataStore
216c3437056SNickeau    {
217c3437056SNickeau        return new MetadataFrontmatterStore($resourceCombo, null);
218c3437056SNickeau    }
219c3437056SNickeau
220c3437056SNickeau    public static function createFromArray(ResourceCombo $page, array $jsonArray): MetadataFrontmatterStore
221c3437056SNickeau    {
222c3437056SNickeau        return new MetadataFrontmatterStore($page, $jsonArray);
223c3437056SNickeau    }
224c3437056SNickeau
225c3437056SNickeau    /**
22604fd306cSNickeau     * @throws ExceptionBadSyntax
227c3437056SNickeau     */
228c3437056SNickeau    public static function createFromFrontmatterString($page, $frontmatter = null): MetadataFrontmatterStore
229c3437056SNickeau    {
230c3437056SNickeau        if ($frontmatter === null) {
231c3437056SNickeau            return new MetadataFrontmatterStore($page, []);
232c3437056SNickeau        }
233c3437056SNickeau        $jsonArray = self::frontMatterMatchToAssociativeArray($frontmatter);
234c3437056SNickeau        if ($jsonArray === null) {
23504fd306cSNickeau            throw new ExceptionBadSyntax("The frontmatter is not valid");
236c3437056SNickeau        }
23704fd306cSNickeau        $frontmatter = new MetadataFrontmatterStore($page, $jsonArray);
23804fd306cSNickeau        $frontmatter->setContentWithoutFrontMatter('');
23904fd306cSNickeau        return $frontmatter;
240c3437056SNickeau    }
241c3437056SNickeau
242c3437056SNickeau    /**
24304fd306cSNickeau     * @throws ExceptionBadSyntax - if the content has a syntax problem
24404fd306cSNickeau     * @throws ExceptionNotFound - if the page does not exist
245c3437056SNickeau     */
24604fd306cSNickeau    public static function createFromPage(MarkupPath $page): MetadataFrontmatterStore
247c3437056SNickeau    {
24804fd306cSNickeau        $content = FileSystems::getContent($page->getPathObject());
249c3437056SNickeau        $frontMatterStartTag = syntax_plugin_combo_frontmatter::START_TAG;
250c3437056SNickeau        if (strpos($content, $frontMatterStartTag) === 0) {
251c3437056SNickeau
252c3437056SNickeau            /**
253c3437056SNickeau             * Extract the actual values
254c3437056SNickeau             */
255c3437056SNickeau            $pattern = syntax_plugin_combo_frontmatter::PATTERN;
256c3437056SNickeau            $split = preg_split("/($pattern)/ms", $content, 2, PREG_SPLIT_DELIM_CAPTURE);
257c3437056SNickeau
258c3437056SNickeau            /**
259c3437056SNickeau             * The split normally returns an array
260c3437056SNickeau             * where the first element is empty followed by the frontmatter
261c3437056SNickeau             */
262c3437056SNickeau            $emptyString = array_shift($split);
263c3437056SNickeau            if (!empty($emptyString)) {
26404fd306cSNickeau                throw new ExceptionBadSyntax("The frontmatter is not the first element");
265c3437056SNickeau            }
266c3437056SNickeau
267c3437056SNickeau            $frontMatterMatch = array_shift($split);
268c3437056SNickeau            /**
269c3437056SNickeau             * Building the document again
270c3437056SNickeau             */
271c3437056SNickeau            $contentWithoutFrontMatter = "";
272c3437056SNickeau            while (($element = array_shift($split)) != null) {
273c3437056SNickeau                $contentWithoutFrontMatter .= $element;
274c3437056SNickeau            }
275c3437056SNickeau
276c3437056SNickeau            return MetadataFrontmatterStore::createFromFrontmatterString($page, $frontMatterMatch)
277c3437056SNickeau                ->setIsPresent(true)
278c3437056SNickeau                ->setContentWithoutFrontMatter($contentWithoutFrontMatter);
279c3437056SNickeau
280c3437056SNickeau        }
281c3437056SNickeau        return (new MetadataFrontmatterStore($page))
282c3437056SNickeau            ->setIsPresent(false)
283c3437056SNickeau            ->setContentWithoutFrontMatter($content);
284c3437056SNickeau
285c3437056SNickeau    }
286c3437056SNickeau
287c3437056SNickeau
288c3437056SNickeau    public function __toString()
289c3437056SNickeau    {
290c3437056SNickeau        return self::NAME;
291c3437056SNickeau    }
292c3437056SNickeau
293c3437056SNickeau
294c3437056SNickeau    public function getJsonString(): string
295c3437056SNickeau    {
296c3437056SNickeau
297c3437056SNickeau        $jsonArray = $this->getData();
298c3437056SNickeau        ksort($jsonArray);
299c3437056SNickeau        return self::toFrontmatterJsonString($jsonArray);
300c3437056SNickeau
301c3437056SNickeau    }
302c3437056SNickeau
303c3437056SNickeau    /**
304c3437056SNickeau     * This formatting make the object on one line for a list of object
305c3437056SNickeau     * making the frontmatter compacter (one line, one meta)
306c3437056SNickeau     * @param $jsonArray
307c3437056SNickeau     * @return string
308c3437056SNickeau     */
309c3437056SNickeau    public static function toFrontmatterJsonString($jsonArray): string
310c3437056SNickeau    {
311c3437056SNickeau
312c3437056SNickeau        if (sizeof($jsonArray) === 0) {
313c3437056SNickeau            return "{}";
314c3437056SNickeau        }
315c3437056SNickeau        $jsonString = "";
316c3437056SNickeau        self::jsonFlatRecursiveEncoding($jsonArray, $jsonString);
317c3437056SNickeau
318c3437056SNickeau        /**
319c3437056SNickeau         * Double Guard (frontmatter should be quick enough)
320c3437056SNickeau         * to support this overhead
321c3437056SNickeau         */
322c3437056SNickeau        $decoding = json_decode($jsonString);
323c3437056SNickeau        if ($decoding === null) {
32404fd306cSNickeau            throw new ExceptionRuntime("The generated frontmatter json is no a valid json");
325c3437056SNickeau        }
326c3437056SNickeau        return $jsonString;
327c3437056SNickeau    }
328c3437056SNickeau
329c3437056SNickeau    private static function jsonFlatRecursiveEncoding(array $jsonProperty, &$jsonString, $level = 0, $endOfFieldCharacter = DOKU_LF, $type = Json::TYPE_OBJECT, $parentType = Json::TYPE_OBJECT)
330c3437056SNickeau    {
331c3437056SNickeau        /**
332c3437056SNickeau         * Open the root object
333c3437056SNickeau         */
334c3437056SNickeau        if ($type === Json::TYPE_OBJECT) {
335c3437056SNickeau            $jsonString .= "{";
336c3437056SNickeau        } else {
337c3437056SNickeau            $jsonString .= "[";
338c3437056SNickeau        }
339c3437056SNickeau
340c3437056SNickeau        /**
341c3437056SNickeau         * Level indentation
342c3437056SNickeau         */
343c3437056SNickeau        $levelSpaceIndentation = str_repeat(" ", ($level + 1) * Json::TAB_SPACES_COUNTER);
344c3437056SNickeau
345c3437056SNickeau        /**
346c3437056SNickeau         * Loop
347c3437056SNickeau         */
348c3437056SNickeau        $elementCounter = 0;
349c3437056SNickeau        foreach ($jsonProperty as $key => $value) {
350c3437056SNickeau
351c3437056SNickeau            $elementCounter++;
352c3437056SNickeau
353c3437056SNickeau            /**
354c3437056SNickeau             * Close the previous property
355c3437056SNickeau             */
356c3437056SNickeau            $isFirstProperty = $elementCounter === 1;
357c3437056SNickeau            if ($isFirstProperty && $parentType !== Json::PARENT_TYPE_ARRAY) {
358c3437056SNickeau                // go the line if this is not a list of object
359c3437056SNickeau                $jsonString .= DOKU_LF;
360c3437056SNickeau            }
361c3437056SNickeau            if (!$isFirstProperty) {
362c3437056SNickeau                $jsonString .= ",$endOfFieldCharacter";
363c3437056SNickeau            }
364c3437056SNickeau            if ($endOfFieldCharacter === DOKU_LF) {
365c3437056SNickeau                $tab = $levelSpaceIndentation;
366c3437056SNickeau            } else {
367c3437056SNickeau                $tab = " ";
368c3437056SNickeau            }
369c3437056SNickeau            $jsonString .= $tab;
370c3437056SNickeau
371c3437056SNickeau            /**
372c3437056SNickeau             * Recurse
373c3437056SNickeau             */
374c3437056SNickeau            $jsonEncodedKey = json_encode($key);
375c3437056SNickeau            if (is_array($value)) {
376c3437056SNickeau                $childLevel = $level + 1;
377c3437056SNickeau                if (is_numeric($key)) {
378c3437056SNickeau                    /**
379c3437056SNickeau                     * List of object
380c3437056SNickeau                     */
381c3437056SNickeau                    $childType = Json::TYPE_OBJECT;
382c3437056SNickeau                    $childEndOField = "";
383c3437056SNickeau                } else {
384c3437056SNickeau                    /**
385c3437056SNickeau                     * Array
386c3437056SNickeau                     */
387c3437056SNickeau                    $jsonString .= "$jsonEncodedKey: ";
388c3437056SNickeau                    $childType = Json::TYPE_OBJECT;
389*70bbd7f1Sgerardnico                    if (($value[0] ?? null) !== null) {
390c3437056SNickeau                        $childType = Json::PARENT_TYPE_ARRAY;
391c3437056SNickeau                    }
392c3437056SNickeau                    $childEndOField = $endOfFieldCharacter;
393c3437056SNickeau                }
394c3437056SNickeau                self::jsonFlatRecursiveEncoding($value, $jsonString, $childLevel, $childEndOField, $childType, $type);
395c3437056SNickeau
396c3437056SNickeau            } else {
397c3437056SNickeau                /**
398c3437056SNickeau                 * Single property
399c3437056SNickeau                 */
400c3437056SNickeau                $jsonEncodedValue = json_encode($value);
401c3437056SNickeau                $jsonString .= "$jsonEncodedKey: $jsonEncodedValue";
402c3437056SNickeau
403c3437056SNickeau            }
404c3437056SNickeau
405c3437056SNickeau        }
406c3437056SNickeau
407c3437056SNickeau        /**
408c3437056SNickeau         * Close the object or array
409c3437056SNickeau         */
410c3437056SNickeau        $closingLevelSpaceIndentation = str_repeat(" ", $level * Json::TAB_SPACES_COUNTER);
411c3437056SNickeau        if ($type === Json::TYPE_OBJECT) {
412c3437056SNickeau            if ($parentType !== Json::PARENT_TYPE_ARRAY) {
413c3437056SNickeau                $jsonString .= DOKU_LF . $closingLevelSpaceIndentation;
414c3437056SNickeau            } else {
415c3437056SNickeau                $jsonString .= " ";
416c3437056SNickeau            }
417c3437056SNickeau            $jsonString .= "}";
418c3437056SNickeau        } else {
419c3437056SNickeau            /**
420c3437056SNickeau             * The array is not going one level back
421c3437056SNickeau             */
422c3437056SNickeau            $jsonString .= DOKU_LF . $closingLevelSpaceIndentation . "]";
423c3437056SNickeau        }
424c3437056SNickeau    }
425c3437056SNickeau
426c3437056SNickeau
427c3437056SNickeau    public function toFrontmatterString(): string
428c3437056SNickeau    {
429c3437056SNickeau        $frontmatterStartTag = syntax_plugin_combo_frontmatter::START_TAG;
430c3437056SNickeau        $frontmatterEndTag = syntax_plugin_combo_frontmatter::END_TAG;
431c3437056SNickeau        $jsonArray = $this->getData();
432c3437056SNickeau        ksort($jsonArray);
433c3437056SNickeau        $jsonEncode = self::toFrontmatterJsonString($jsonArray);
434c3437056SNickeau
435c3437056SNickeau        return <<<EOF
436c3437056SNickeau$frontmatterStartTag
437c3437056SNickeau$jsonEncode
438c3437056SNickeau$frontmatterEndTag
439c3437056SNickeauEOF;
440c3437056SNickeau
441c3437056SNickeau
442c3437056SNickeau    }
443c3437056SNickeau
444c3437056SNickeau    private function setIsPresent(bool $bool): MetadataFrontmatterStore
445c3437056SNickeau    {
446c3437056SNickeau        $this->isPresent = $bool;
447c3437056SNickeau        return $this;
448c3437056SNickeau    }
449c3437056SNickeau
450c3437056SNickeau    public function persist()
451c3437056SNickeau    {
45204fd306cSNickeau        if (!isset($this->contentWithoutFrontMatter)) {
453c3437056SNickeau            LogUtility::msg("The content without frontmatter should have been set. Did you you use the createFromPage constructor");
454c3437056SNickeau            return $this;
455c3437056SNickeau        }
45604fd306cSNickeau        $newPageContent = $this->toMarkup();
45704fd306cSNickeau        $resourceCombo = $this->getResource();
45804fd306cSNickeau        if ($resourceCombo instanceof MarkupPath) {
45904fd306cSNickeau            $resourceCombo->setContentWithLog($newPageContent, "Metadata frontmatter store upsert");
46004fd306cSNickeau        }
46104fd306cSNickeau        return $this;
46204fd306cSNickeau    }
46304fd306cSNickeau
46404fd306cSNickeau    private function setContentWithoutFrontMatter(string $contentWithoutFrontMatter): MetadataFrontmatterStore
46504fd306cSNickeau    {
46604fd306cSNickeau        $this->contentWithoutFrontMatter = $contentWithoutFrontMatter;
46704fd306cSNickeau        return $this;
46804fd306cSNickeau    }
46904fd306cSNickeau
47004fd306cSNickeau
47104fd306cSNickeau    /**
47204fd306cSNickeau     * @return string - the new markup (ie the new frontmatter and the markup)
47304fd306cSNickeau     */
47404fd306cSNickeau    public function toMarkup(): string
47504fd306cSNickeau    {
47604fd306cSNickeau
477c3437056SNickeau        $targetFrontMatterJsonString = $this->toFrontmatterString();
478c3437056SNickeau
479c3437056SNickeau        /**
480c3437056SNickeau         * EOL for the first frontmatter
481c3437056SNickeau         */
482c3437056SNickeau        $sep = "";
483c3437056SNickeau        if (strlen($this->contentWithoutFrontMatter) > 0) {
484c3437056SNickeau            $firstChar = $this->contentWithoutFrontMatter[0];
485c3437056SNickeau            if (!in_array($firstChar, ["\n", "\r"])) {
486c3437056SNickeau                $sep = "\n";
487c3437056SNickeau            }
488c3437056SNickeau        }
489c3437056SNickeau
490c3437056SNickeau        /**
491c3437056SNickeau         * Build the new document
492c3437056SNickeau         */
49304fd306cSNickeau        return <<<EOF
494c3437056SNickeau$targetFrontMatterJsonString$sep$this->contentWithoutFrontMatter
495c3437056SNickeauEOF;
49604fd306cSNickeau
497c3437056SNickeau    }
498c3437056SNickeau
49904fd306cSNickeau    public function getContentWithoutFrontMatter(): string
500c3437056SNickeau    {
50104fd306cSNickeau        return $this->contentWithoutFrontMatter;
502c3437056SNickeau    }
503c3437056SNickeau
504c3437056SNickeau
505c3437056SNickeau}
506