xref: /plugin/combo/ComboStrap/MarkupPath.php (revision 04fd306c7c155fa133ebb3669986875d65988276)
1*04fd306cSNickeau<?php
2*04fd306cSNickeau
3*04fd306cSNickeaunamespace ComboStrap;
4*04fd306cSNickeau
5*04fd306cSNickeau
6*04fd306cSNickeauuse ComboStrap\Api\QualityMessageHandler;
7*04fd306cSNickeauuse ComboStrap\Meta\Api\Metadata;
8*04fd306cSNickeauuse ComboStrap\Meta\Api\MetadataBoolean;
9*04fd306cSNickeauuse ComboStrap\Meta\Api\MetadataStore;
10*04fd306cSNickeauuse ComboStrap\Meta\Api\MetadataStoreAbs;
11*04fd306cSNickeauuse ComboStrap\Meta\Field\Alias;
12*04fd306cSNickeauuse ComboStrap\Meta\Field\Aliases;
13*04fd306cSNickeauuse ComboStrap\Meta\Field\AliasType;
14*04fd306cSNickeauuse ComboStrap\Meta\Field\PageH1;
15*04fd306cSNickeauuse ComboStrap\Meta\Field\PageImage;
16*04fd306cSNickeauuse ComboStrap\Meta\Field\PageImages;
17*04fd306cSNickeauuse ComboStrap\Meta\Field\PageTemplateName;
18*04fd306cSNickeauuse ComboStrap\Meta\Field\Region;
19*04fd306cSNickeauuse ComboStrap\Meta\Store\MetadataDokuWikiStore;
20*04fd306cSNickeauuse ComboStrap\Web\Url;
21*04fd306cSNickeauuse ComboStrap\Web\UrlEndpoint;
22*04fd306cSNickeauuse DateTime;
23*04fd306cSNickeauuse dokuwiki\ChangeLog\ChangeLog;
24*04fd306cSNickeauuse Exception;
25*04fd306cSNickeauuse renderer_plugin_combo_analytics;
26*04fd306cSNickeau
27*04fd306cSNickeau
28*04fd306cSNickeau/**
29*04fd306cSNickeau *
30*04fd306cSNickeau * A markup is a logical unit that represents a markup file.
31*04fd306cSNickeau *
32*04fd306cSNickeau * It has its own file system {@link MarkupFileSystem} explained in the
33*04fd306cSNickeau * https://combostrap.com/page/system (or system.txt file).
34*04fd306cSNickeau * ie the {@link Path::getParent()} is not the same than on an normal file system.
35*04fd306cSNickeau *
36*04fd306cSNickeau * This should be an extension of {@link WikiPath} but for now, we are not extending {@link WikiPath}
37*04fd306cSNickeau * for the following old reasons:
38*04fd306cSNickeau *   * we want to be able to return a {@link MarkupPath} in the {@link MarkupPath::getParent()} function
39*04fd306cSNickeau * otherwise if we do, we get a hierarchical error.
40*04fd306cSNickeau *   * we can then accepts also {@link LocalPath}
41*04fd306cSNickeau *
42*04fd306cSNickeau * But because this is a {@link ResourceCombo}, we see tht this is part of the {@link WikiPath}
43*04fd306cSNickeau * system with an {@link ResourceCombo::getUid()} unique uid.
44*04fd306cSNickeau *
45*04fd306cSNickeau * We should find a way to be able to create a wiki path with a {@link LocalPath}
46*04fd306cSNickeau * via the {@link WikiPath::getDrive()} ?
47*04fd306cSNickeau *
48*04fd306cSNickeau */
49*04fd306cSNickeauclass MarkupPath extends PathAbs implements ResourceCombo, Path
50*04fd306cSNickeau{
51*04fd306cSNickeau
52*04fd306cSNickeau    const CANONICAL_PAGE = "markup";
53*04fd306cSNickeau
54*04fd306cSNickeau
55*04fd306cSNickeau    const TYPE = "page";
56*04fd306cSNickeau
57*04fd306cSNickeau    /**
58*04fd306cSNickeau     * @var Canonical
59*04fd306cSNickeau     */
60*04fd306cSNickeau    private $canonical;
61*04fd306cSNickeau    /**
62*04fd306cSNickeau     * @var PageH1
63*04fd306cSNickeau     */
64*04fd306cSNickeau    private $h1;
65*04fd306cSNickeau    /**
66*04fd306cSNickeau     * @var ResourceName
67*04fd306cSNickeau     */
68*04fd306cSNickeau    private $pageName;
69*04fd306cSNickeau    /**
70*04fd306cSNickeau     * @var PageType
71*04fd306cSNickeau     */
72*04fd306cSNickeau    private $type;
73*04fd306cSNickeau    /**
74*04fd306cSNickeau     * @var PageTitle $title
75*04fd306cSNickeau     */
76*04fd306cSNickeau    private $title;
77*04fd306cSNickeau
78*04fd306cSNickeau    private $uidObject;
79*04fd306cSNickeau
80*04fd306cSNickeau    private LowQualityPageOverwrite $canBeOfLowQuality;
81*04fd306cSNickeau    /**
82*04fd306cSNickeau     * @var Region
83*04fd306cSNickeau     */
84*04fd306cSNickeau    private $region;
85*04fd306cSNickeau    /**
86*04fd306cSNickeau     * @var Lang
87*04fd306cSNickeau     */
88*04fd306cSNickeau    private $lang;
89*04fd306cSNickeau    /**
90*04fd306cSNickeau     * @var PageId
91*04fd306cSNickeau     */
92*04fd306cSNickeau    private $pageId;
93*04fd306cSNickeau
94*04fd306cSNickeau    /**
95*04fd306cSNickeau     * @var LowQualityCalculatedIndicator
96*04fd306cSNickeau     */
97*04fd306cSNickeau    private $lowQualityIndicatorCalculated;
98*04fd306cSNickeau
99*04fd306cSNickeau    /**
100*04fd306cSNickeau     * @var PageTemplateName
101*04fd306cSNickeau     */
102*04fd306cSNickeau    private $layout;
103*04fd306cSNickeau    /**
104*04fd306cSNickeau     * @var Aliases
105*04fd306cSNickeau     */
106*04fd306cSNickeau    private $aliases;
107*04fd306cSNickeau    /**
108*04fd306cSNickeau     * @var Slug a slug path
109*04fd306cSNickeau     */
110*04fd306cSNickeau    private $slug;
111*04fd306cSNickeau
112*04fd306cSNickeau
113*04fd306cSNickeau    /**
114*04fd306cSNickeau     * @var QualityDynamicMonitoringOverwrite
115*04fd306cSNickeau     */
116*04fd306cSNickeau    private $qualityMonitoringIndicator;
117*04fd306cSNickeau
118*04fd306cSNickeau    /**
119*04fd306cSNickeau     * @var string the alias used to build this page
120*04fd306cSNickeau     */
121*04fd306cSNickeau    private $buildAliasPath;
122*04fd306cSNickeau    /**
123*04fd306cSNickeau     * @var PagePublicationDate
124*04fd306cSNickeau     */
125*04fd306cSNickeau    private $publishedDate;
126*04fd306cSNickeau    /**
127*04fd306cSNickeau     * @var StartDate
128*04fd306cSNickeau     */
129*04fd306cSNickeau    private $startDate;
130*04fd306cSNickeau    /**
131*04fd306cSNickeau     * @var EndDate
132*04fd306cSNickeau     */
133*04fd306cSNickeau    private $endDate;
134*04fd306cSNickeau    /**
135*04fd306cSNickeau     * @var PageImages
136*04fd306cSNickeau     */
137*04fd306cSNickeau    private $pageImages;
138*04fd306cSNickeau
139*04fd306cSNickeau    private PageKeywords $keywords;
140*04fd306cSNickeau    /**
141*04fd306cSNickeau     * @var CacheExpirationFrequency
142*04fd306cSNickeau     */
143*04fd306cSNickeau    private $cacheExpirationFrequency;
144*04fd306cSNickeau    /**
145*04fd306cSNickeau     * @var CacheExpirationDate
146*04fd306cSNickeau     */
147*04fd306cSNickeau    private $cacheExpirationDate;
148*04fd306cSNickeau    /**
149*04fd306cSNickeau     *
150*04fd306cSNickeau     * @var LdJson
151*04fd306cSNickeau     */
152*04fd306cSNickeau    private $ldJson;
153*04fd306cSNickeau
154*04fd306cSNickeau
155*04fd306cSNickeau    /**
156*04fd306cSNickeau     * @var PageDescription $description
157*04fd306cSNickeau     */
158*04fd306cSNickeau    private $description;
159*04fd306cSNickeau    /**
160*04fd306cSNickeau     * @var CreationDate
161*04fd306cSNickeau     */
162*04fd306cSNickeau    private $creationTime;
163*04fd306cSNickeau    /**
164*04fd306cSNickeau     * @var Locale
165*04fd306cSNickeau     */
166*04fd306cSNickeau    private $locale;
167*04fd306cSNickeau    /**
168*04fd306cSNickeau     * @var ModificationDate
169*04fd306cSNickeau     */
170*04fd306cSNickeau    private $modifiedTime;
171*04fd306cSNickeau    /**
172*04fd306cSNickeau     * @var PageUrlPath
173*04fd306cSNickeau     */
174*04fd306cSNickeau    private $pageUrlPath;
175*04fd306cSNickeau    /**
176*04fd306cSNickeau     * @var MetadataStore|string
177*04fd306cSNickeau     */
178*04fd306cSNickeau    private $readStore;
179*04fd306cSNickeau
180*04fd306cSNickeau    /**
181*04fd306cSNickeau     * @var Path -  {@link MarkupPath} has other hierachy system in regards with parent
182*04fd306cSNickeau     * May be we just should extends {@link WikiPath} but it was a way to be able to locate
183*04fd306cSNickeau     * default markup path file that were not in any drive
184*04fd306cSNickeau     * TODO: Just extends WikiPath and add private drive when data should be accessed locally ?
185*04fd306cSNickeau     */
186*04fd306cSNickeau    private Path $path;
187*04fd306cSNickeau
188*04fd306cSNickeau    /**
189*04fd306cSNickeau     * Page constructor.
190*04fd306cSNickeau     *
191*04fd306cSNickeau     */
192*04fd306cSNickeau    public function __construct(Path $path)
193*04fd306cSNickeau    {
194*04fd306cSNickeau
195*04fd306cSNickeau        $this->path = $path;
196*04fd306cSNickeau        if (FileSystems::isDirectory($path)) {
197*04fd306cSNickeau            $this->setCorrectPathForDirectoryToIndexPage();
198*04fd306cSNickeau        }
199*04fd306cSNickeau        $this->buildPropertiesFromFileSystem();
200*04fd306cSNickeau
201*04fd306cSNickeau    }
202*04fd306cSNickeau
203*04fd306cSNickeau    /**
204*04fd306cSNickeau     * The current running rendering markup
205*04fd306cSNickeau     * @throws ExceptionNotFound
206*04fd306cSNickeau     */
207*04fd306cSNickeau    public static function createPageFromExecutingId(): MarkupPath
208*04fd306cSNickeau    {
209*04fd306cSNickeau        $wikiPath = WikiPath::createExecutingMarkupWikiPath();
210*04fd306cSNickeau        return self::createPageFromPathObject($wikiPath);
211*04fd306cSNickeau    }
212*04fd306cSNickeau
213*04fd306cSNickeau
214*04fd306cSNickeau    public static function createMarkupFromId($id): MarkupPath
215*04fd306cSNickeau    {
216*04fd306cSNickeau        return new MarkupPath(WikiPath::createMarkupPathFromId($id));
217*04fd306cSNickeau    }
218*04fd306cSNickeau
219*04fd306cSNickeau    /**
220*04fd306cSNickeau     * @param string $path -  relative or absolute
221*04fd306cSNickeau     * @return MarkupPath
222*04fd306cSNickeau     */
223*04fd306cSNickeau    public static function createMarkupFromStringPath(string $path): MarkupPath
224*04fd306cSNickeau    {
225*04fd306cSNickeau        $wikiPath = WikiPath::createMarkupPathFromPath($path);
226*04fd306cSNickeau        return new MarkupPath($wikiPath);
227*04fd306cSNickeau
228*04fd306cSNickeau    }
229*04fd306cSNickeau
230*04fd306cSNickeau    /**
231*04fd306cSNickeau     * @return MarkupPath - the requested page
232*04fd306cSNickeau     * @throws ExceptionNotFound
233*04fd306cSNickeau     */
234*04fd306cSNickeau    public static function createFromRequestedPage(): MarkupPath
235*04fd306cSNickeau    {
236*04fd306cSNickeau        $path = WikiPath::createRequestedPagePathFromRequest();
237*04fd306cSNickeau        return MarkupPath::createPageFromPathObject($path);
238*04fd306cSNickeau    }
239*04fd306cSNickeau
240*04fd306cSNickeau
241*04fd306cSNickeau    public static function createPageFromPathObject(Path $path): MarkupPath
242*04fd306cSNickeau    {
243*04fd306cSNickeau        if ($path instanceof MarkupPath) {
244*04fd306cSNickeau            return $path;
245*04fd306cSNickeau        }
246*04fd306cSNickeau        return new MarkupPath($path);
247*04fd306cSNickeau    }
248*04fd306cSNickeau
249*04fd306cSNickeau
250*04fd306cSNickeau    /**
251*04fd306cSNickeau     *
252*04fd306cSNickeau     * @throws ExceptionBadSyntax - if this is not a
253*04fd306cSNickeau     * @deprecated just pass a namespace path to the page creation and you will get the index page in return
254*04fd306cSNickeau     */
255*04fd306cSNickeau    public static function getIndexPageFromNamespace(string $namespacePath): MarkupPath
256*04fd306cSNickeau    {
257*04fd306cSNickeau        WikiPath::checkNamespacePath($namespacePath);
258*04fd306cSNickeau
259*04fd306cSNickeau        return MarkupPath::createMarkupFromId($namespacePath);
260*04fd306cSNickeau    }
261*04fd306cSNickeau
262*04fd306cSNickeau
263*04fd306cSNickeau    static function createPageFromAbsoluteId($qualifiedPath): MarkupPath
264*04fd306cSNickeau    {
265*04fd306cSNickeau        $path = WikiPath::createMarkupPathFromId($qualifiedPath);
266*04fd306cSNickeau        return new MarkupPath($path);
267*04fd306cSNickeau    }
268*04fd306cSNickeau
269*04fd306cSNickeau
270*04fd306cSNickeau    /**
271*04fd306cSNickeau     *
272*04fd306cSNickeau     * @throws ExceptionCompile
273*04fd306cSNickeau     */
274*04fd306cSNickeau    public
275*04fd306cSNickeau    function setCanonical($canonical): MarkupPath
276*04fd306cSNickeau    {
277*04fd306cSNickeau        $this->canonical
278*04fd306cSNickeau            ->setValue($canonical)
279*04fd306cSNickeau            ->sendToWriteStore();
280*04fd306cSNickeau        return $this;
281*04fd306cSNickeau    }
282*04fd306cSNickeau
283*04fd306cSNickeau
284*04fd306cSNickeau    /**
285*04fd306cSNickeau     * @return bool true if this is a fragment markup
286*04fd306cSNickeau     */
287*04fd306cSNickeau    public function isSlot(): bool
288*04fd306cSNickeau    {
289*04fd306cSNickeau        $slotNames = SlotSystem::getSlotNames();
290*04fd306cSNickeau        try {
291*04fd306cSNickeau            $name = $this->getPathObject()->getLastNameWithoutExtension();
292*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
293*04fd306cSNickeau            // root case
294*04fd306cSNickeau            return false;
295*04fd306cSNickeau        }
296*04fd306cSNickeau        return in_array($name, $slotNames, true);
297*04fd306cSNickeau    }
298*04fd306cSNickeau
299*04fd306cSNickeau    /**
300*04fd306cSNickeau     * @return bool true if this is the side slot
301*04fd306cSNickeau     */
302*04fd306cSNickeau    public function isSideSlot(): bool
303*04fd306cSNickeau    {
304*04fd306cSNickeau        $slotNames = SlotSystem::getSidebarName();
305*04fd306cSNickeau        try {
306*04fd306cSNickeau            $name = $this->getPathObject()->getLastNameWithoutExtension();
307*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
308*04fd306cSNickeau            // root case
309*04fd306cSNickeau            return false;
310*04fd306cSNickeau        }
311*04fd306cSNickeau        return $name === $slotNames;
312*04fd306cSNickeau    }
313*04fd306cSNickeau
314*04fd306cSNickeau    /**
315*04fd306cSNickeau     * @return bool true if this is the main
316*04fd306cSNickeau     */
317*04fd306cSNickeau    public function isMainHeaderFooterSlot(): bool
318*04fd306cSNickeau    {
319*04fd306cSNickeau
320*04fd306cSNickeau        $slotNames = [SlotSystem::getMainHeaderSlotName(), SlotSystem::getMainFooterSlotName()];
321*04fd306cSNickeau        try {
322*04fd306cSNickeau            $name = $this->getPathObject()->getLastNameWithoutExtension();
323*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
324*04fd306cSNickeau            // root case
325*04fd306cSNickeau            return false;
326*04fd306cSNickeau        }
327*04fd306cSNickeau
328*04fd306cSNickeau        return in_array($name, $slotNames, true);
329*04fd306cSNickeau    }
330*04fd306cSNickeau
331*04fd306cSNickeau
332*04fd306cSNickeau    /**
333*04fd306cSNickeau     * Return a canonical if set
334*04fd306cSNickeau     * otherwise derive it from the id
335*04fd306cSNickeau     * by taking the last two parts
336*04fd306cSNickeau     *
337*04fd306cSNickeau     * @return WikiPath
338*04fd306cSNickeau     * @throws ExceptionNotFound
339*04fd306cSNickeau     * @deprecated for {@link Canonical::getValueOrDefault()}
340*04fd306cSNickeau     */
341*04fd306cSNickeau    public
342*04fd306cSNickeau    function getCanonicalOrDefault(): WikiPath
343*04fd306cSNickeau    {
344*04fd306cSNickeau        return $this->canonical->getValueFromStoreOrDefault();
345*04fd306cSNickeau
346*04fd306cSNickeau    }
347*04fd306cSNickeau
348*04fd306cSNickeau
349*04fd306cSNickeau    /**
350*04fd306cSNickeau     * Rebuild the page
351*04fd306cSNickeau     * (refresh from disk, reset object to null)
352*04fd306cSNickeau     * @return $this
353*04fd306cSNickeau     */
354*04fd306cSNickeau    public
355*04fd306cSNickeau    function rebuild(): MarkupPath
356*04fd306cSNickeau    {
357*04fd306cSNickeau        $this->readStore = null;
358*04fd306cSNickeau        $this->buildPropertiesFromFileSystem();
359*04fd306cSNickeau        return $this;
360*04fd306cSNickeau    }
361*04fd306cSNickeau
362*04fd306cSNickeau    /**
363*04fd306cSNickeau     *
364*04fd306cSNickeau     * @return MarkupPath[]|null the internal links or null
365*04fd306cSNickeau     */
366*04fd306cSNickeau    public
367*04fd306cSNickeau    function getLinkReferences(): ?array
368*04fd306cSNickeau    {
369*04fd306cSNickeau        $store = $this->getReadStoreOrDefault();
370*04fd306cSNickeau        if (!($store instanceof MetadataDokuWikiStore)) {
371*04fd306cSNickeau            return null;
372*04fd306cSNickeau        }
373*04fd306cSNickeau        $metadata = $store->getCurrentFromName('relation');
374*04fd306cSNickeau        if ($metadata === null) {
375*04fd306cSNickeau            /**
376*04fd306cSNickeau             * Happens when no rendering has been made
377*04fd306cSNickeau             */
378*04fd306cSNickeau            return null;
379*04fd306cSNickeau        }
380*04fd306cSNickeau        if (!key_exists('references', $metadata)) {
381*04fd306cSNickeau            return null;
382*04fd306cSNickeau        }
383*04fd306cSNickeau
384*04fd306cSNickeau        $pages = [];
385*04fd306cSNickeau        foreach (array_keys($metadata['references']) as $referencePageId) {
386*04fd306cSNickeau            $pages[$referencePageId] = MarkupPath::createMarkupFromId($referencePageId);
387*04fd306cSNickeau        }
388*04fd306cSNickeau        return $pages;
389*04fd306cSNickeau
390*04fd306cSNickeau    }
391*04fd306cSNickeau
392*04fd306cSNickeau
393*04fd306cSNickeau    /**
394*04fd306cSNickeau     *
395*04fd306cSNickeau     * @throws ExceptionNotExists - if the path does not exists
396*04fd306cSNickeau     * @throws ExceptionCast - if the path is not a wiki path which is mandatory for the context
397*04fd306cSNickeau     */
398*04fd306cSNickeau    public function createHtmlFetcherWithItselfAsContextPath(): FetcherMarkup
399*04fd306cSNickeau    {
400*04fd306cSNickeau        $path = $this->getPathObject();
401*04fd306cSNickeau        return FetcherMarkup::createXhtmlMarkupFetcherFromPath($path, $path->toWikiPath());
402*04fd306cSNickeau    }
403*04fd306cSNickeau
404*04fd306cSNickeau    /**
405*04fd306cSNickeau     * @throws ExceptionCompile
406*04fd306cSNickeau     */
407*04fd306cSNickeau    public function getHtmlPath(): LocalPath
408*04fd306cSNickeau    {
409*04fd306cSNickeau
410*04fd306cSNickeau        $fetcher = $this->createHtmlFetcherWithItselfAsContextPath();
411*04fd306cSNickeau        return $fetcher->processIfNeededAndGetFetchPath();
412*04fd306cSNickeau
413*04fd306cSNickeau    }
414*04fd306cSNickeau
415*04fd306cSNickeau    /**
416*04fd306cSNickeau     * Set the page quality
417*04fd306cSNickeau     * @param boolean $value true if this is a low quality page rank false otherwise
418*04fd306cSNickeau     * @throws ExceptionCompile
419*04fd306cSNickeau     */
420*04fd306cSNickeau    public
421*04fd306cSNickeau    function setCanBeOfLowQuality(bool $value): MarkupPath
422*04fd306cSNickeau    {
423*04fd306cSNickeau        return $this->setQualityIndicatorAndDeleteCacheIfNeeded($this->canBeOfLowQuality, $value);
424*04fd306cSNickeau    }
425*04fd306cSNickeau
426*04fd306cSNickeau    /**
427*04fd306cSNickeau     * @return MarkupPath[] the backlinks
428*04fd306cSNickeau     * Duplicate of related
429*04fd306cSNickeau     *
430*04fd306cSNickeau     * Same as {@link WikiPath::getReferencedBy()} ?
431*04fd306cSNickeau     */
432*04fd306cSNickeau    public
433*04fd306cSNickeau    function getBacklinks(): array
434*04fd306cSNickeau    {
435*04fd306cSNickeau        $backlinks = array();
436*04fd306cSNickeau        /**
437*04fd306cSNickeau         * Same as
438*04fd306cSNickeau         * idx_get_indexer()->lookupKey('relation_references', $ID);
439*04fd306cSNickeau         */
440*04fd306cSNickeau        $ft_backlinks = ft_backlinks($this->getWikiId());
441*04fd306cSNickeau        foreach ($ft_backlinks as $backlinkId) {
442*04fd306cSNickeau            $backlinks[$backlinkId] = MarkupPath::createMarkupFromId($backlinkId);
443*04fd306cSNickeau        }
444*04fd306cSNickeau        return $backlinks;
445*04fd306cSNickeau    }
446*04fd306cSNickeau
447*04fd306cSNickeau
448*04fd306cSNickeau    /**
449*04fd306cSNickeau     * Low page quality
450*04fd306cSNickeau     * @return bool true if this is a low quality page
451*04fd306cSNickeau     */
452*04fd306cSNickeau    function isLowQualityPage(): bool
453*04fd306cSNickeau    {
454*04fd306cSNickeau
455*04fd306cSNickeau
456*04fd306cSNickeau        if (!$this->getCanBeOfLowQuality()) {
457*04fd306cSNickeau            return false;
458*04fd306cSNickeau        }
459*04fd306cSNickeau
460*04fd306cSNickeau        if (!Site::isLowQualityProtectionEnable()) {
461*04fd306cSNickeau            return false;
462*04fd306cSNickeau        }
463*04fd306cSNickeau        try {
464*04fd306cSNickeau            return $this->getLowQualityIndicatorCalculated();
465*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
466*04fd306cSNickeau            // We were returning null but null used in a condition is falsy
467*04fd306cSNickeau            // we return false
468*04fd306cSNickeau            return false;
469*04fd306cSNickeau        }
470*04fd306cSNickeau
471*04fd306cSNickeau    }
472*04fd306cSNickeau
473*04fd306cSNickeau
474*04fd306cSNickeau    /**
475*04fd306cSNickeau     *
476*04fd306cSNickeau     */
477*04fd306cSNickeau    public function getCanBeOfLowQuality(): bool
478*04fd306cSNickeau    {
479*04fd306cSNickeau
480*04fd306cSNickeau        return $this->canBeOfLowQuality->getValueOrDefault();
481*04fd306cSNickeau
482*04fd306cSNickeau    }
483*04fd306cSNickeau
484*04fd306cSNickeau
485*04fd306cSNickeau    /**
486*04fd306cSNickeau     * Return the Title
487*04fd306cSNickeau     * @deprecated for {@link PageTitle::getValue()}
488*04fd306cSNickeau     */
489*04fd306cSNickeau    public
490*04fd306cSNickeau    function getTitle(): ?string
491*04fd306cSNickeau    {
492*04fd306cSNickeau        return $this->title->getValueFromStore();
493*04fd306cSNickeau    }
494*04fd306cSNickeau
495*04fd306cSNickeau    /**
496*04fd306cSNickeau     * If true, the page is quality monitored (a note is shown to the writer)
497*04fd306cSNickeau     * @return null|bool
498*04fd306cSNickeau     */
499*04fd306cSNickeau    public
500*04fd306cSNickeau    function getQualityMonitoringIndicator(): ?bool
501*04fd306cSNickeau    {
502*04fd306cSNickeau        return $this->qualityMonitoringIndicator->getValueFromStore();
503*04fd306cSNickeau    }
504*04fd306cSNickeau
505*04fd306cSNickeau    /**
506*04fd306cSNickeau     * @return string the title, or h1 if empty or the id if empty
507*04fd306cSNickeau     * Shortcut to {@link PageTitle::getValueOrDefault()}
508*04fd306cSNickeau     *
509*04fd306cSNickeau     */
510*04fd306cSNickeau    public
511*04fd306cSNickeau    function getTitleOrDefault(): string
512*04fd306cSNickeau    {
513*04fd306cSNickeau        try {
514*04fd306cSNickeau            return $this->title->getValueOrDefault();
515*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
516*04fd306cSNickeau            LogUtility::internalError("Internal Error: The page ($this) does not have any default title");
517*04fd306cSNickeau            return $this->getPathObject()->getLastNameWithoutExtension();
518*04fd306cSNickeau        }
519*04fd306cSNickeau
520*04fd306cSNickeau    }
521*04fd306cSNickeau
522*04fd306cSNickeau
523*04fd306cSNickeau    public function getH1OrDefault(): string
524*04fd306cSNickeau    {
525*04fd306cSNickeau
526*04fd306cSNickeau        return $this->h1->getValueOrDefault();
527*04fd306cSNickeau
528*04fd306cSNickeau    }
529*04fd306cSNickeau
530*04fd306cSNickeau    /**
531*04fd306cSNickeau     * @return string
532*04fd306cSNickeau     * @throws ExceptionNotFound
533*04fd306cSNickeau     */
534*04fd306cSNickeau    public
535*04fd306cSNickeau    function getDescription(): string
536*04fd306cSNickeau    {
537*04fd306cSNickeau        return $this->description->getValue();
538*04fd306cSNickeau    }
539*04fd306cSNickeau
540*04fd306cSNickeau
541*04fd306cSNickeau    /**
542*04fd306cSNickeau     * @return string - the description or the dokuwiki generated description
543*04fd306cSNickeau     */
544*04fd306cSNickeau    public
545*04fd306cSNickeau    function getDescriptionOrElseDokuWiki(): string
546*04fd306cSNickeau    {
547*04fd306cSNickeau        return $this->description->getValueOrDefault();
548*04fd306cSNickeau    }
549*04fd306cSNickeau
550*04fd306cSNickeau
551*04fd306cSNickeau    /**
552*04fd306cSNickeau     * @return string
553*04fd306cSNickeau     * The content / markup that should be parsed by the parser
554*04fd306cSNickeau     */
555*04fd306cSNickeau    public
556*04fd306cSNickeau    function getMarkup(): string
557*04fd306cSNickeau    {
558*04fd306cSNickeau
559*04fd306cSNickeau        try {
560*04fd306cSNickeau            return FileSystems::getContent($this->getPathObject());
561*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
562*04fd306cSNickeau            LogUtility::msg("The page ($this) was not found");
563*04fd306cSNickeau            return "";
564*04fd306cSNickeau        }
565*04fd306cSNickeau
566*04fd306cSNickeau    }
567*04fd306cSNickeau
568*04fd306cSNickeau
569*04fd306cSNickeau    public
570*04fd306cSNickeau    function isInIndex(): bool
571*04fd306cSNickeau    {
572*04fd306cSNickeau        $Indexer = idx_get_indexer();
573*04fd306cSNickeau        $pages = $Indexer->getPages();
574*04fd306cSNickeau        $return = array_search($this->getPathObject()->getWikiId(), $pages, true);
575*04fd306cSNickeau        return $return !== false;
576*04fd306cSNickeau    }
577*04fd306cSNickeau
578*04fd306cSNickeau
579*04fd306cSNickeau    /**
580*04fd306cSNickeau     * Save the content with the {@link ChangeLog}
581*04fd306cSNickeau     * @param string $content
582*04fd306cSNickeau     * @param string $summary
583*04fd306cSNickeau     * @return $this
584*04fd306cSNickeau     * Use {@link FileSystems::setContent()} if you don't want any log
585*04fd306cSNickeau     * This function wraps {@link saveWikiText()} it implements the events system and may have side-effects
586*04fd306cSNickeau     */
587*04fd306cSNickeau    public
588*04fd306cSNickeau    function setContentWithLog(string $content, string $summary = "Default"): MarkupPath
589*04fd306cSNickeau    {
590*04fd306cSNickeau        $path = $this->getPathObject();
591*04fd306cSNickeau        if (!($path instanceof WikiPath)) {
592*04fd306cSNickeau            throw new ExceptionRuntime("The path of this markup is not a wiki path");
593*04fd306cSNickeau        }
594*04fd306cSNickeau        saveWikiText($path->getWikiId(), $content, $summary);
595*04fd306cSNickeau        return $this;
596*04fd306cSNickeau    }
597*04fd306cSNickeau
598*04fd306cSNickeau    public
599*04fd306cSNickeau    function addToIndex()
600*04fd306cSNickeau    {
601*04fd306cSNickeau        /**
602*04fd306cSNickeau         * Add to index check the metadata cache
603*04fd306cSNickeau         * Because we log the cache at the requested page level, we need to
604*04fd306cSNickeau         * set the global ID
605*04fd306cSNickeau         */
606*04fd306cSNickeau        global $ID;
607*04fd306cSNickeau        $keep = $ID;
608*04fd306cSNickeau        global $ACT;
609*04fd306cSNickeau        $keepACT = $ACT;
610*04fd306cSNickeau        try {
611*04fd306cSNickeau            $ACT = "show";
612*04fd306cSNickeau            $ID = $this->getPathObject()->toWikiPath()->getWikiId();
613*04fd306cSNickeau            idx_addPage($ID);
614*04fd306cSNickeau        } finally {
615*04fd306cSNickeau            $ID = $keep;
616*04fd306cSNickeau            $ACT = $keepACT;
617*04fd306cSNickeau        }
618*04fd306cSNickeau        return $this;
619*04fd306cSNickeau
620*04fd306cSNickeau    }
621*04fd306cSNickeau
622*04fd306cSNickeau    /**
623*04fd306cSNickeau     * @return mixed
624*04fd306cSNickeau     */
625*04fd306cSNickeau    public
626*04fd306cSNickeau    function getTypeOrDefault()
627*04fd306cSNickeau    {
628*04fd306cSNickeau        return $this->type->getValueFromStoreOrDefault();
629*04fd306cSNickeau    }
630*04fd306cSNickeau
631*04fd306cSNickeau
632*04fd306cSNickeau    /**
633*04fd306cSNickeau     * @throws ExceptionNotFound
634*04fd306cSNickeau     */
635*04fd306cSNickeau    public
636*04fd306cSNickeau    function getFirstImage(): IFetcherLocalImage
637*04fd306cSNickeau    {
638*04fd306cSNickeau        try {
639*04fd306cSNickeau            return IFetcherLocalImage::createImageFetchFromPath(FirstRasterImage::createForPage($this)->getValue());
640*04fd306cSNickeau        } catch (ExceptionBadSyntax|ExceptionBadArgument $e) {
641*04fd306cSNickeau            LogUtility::error("First Raster Image error. Error: " . $e->getMessage(), self::CANONICAL_PAGE, $e);
642*04fd306cSNickeau            throw new ExceptionNotFound();
643*04fd306cSNickeau        } catch (ExceptionNotExists $e) {
644*04fd306cSNickeau            throw new ExceptionNotFound();
645*04fd306cSNickeau        }
646*04fd306cSNickeau
647*04fd306cSNickeau    }
648*04fd306cSNickeau
649*04fd306cSNickeau    /**
650*04fd306cSNickeau     * Return the media stored during parsing
651*04fd306cSNickeau     *
652*04fd306cSNickeau     * They are saved via the function {@link \Doku_Renderer_metadata::_recordMediaUsage()}
653*04fd306cSNickeau     * called by the {@link \Doku_Renderer_metadata::internalmedia()}
654*04fd306cSNickeau     *
655*04fd306cSNickeau     *
656*04fd306cSNickeau     * {@link \Doku_Renderer_metadata::externalmedia()} does not save them
657*04fd306cSNickeau     */
658*04fd306cSNickeau    public
659*04fd306cSNickeau    function getMediasMetadata(): ?array
660*04fd306cSNickeau    {
661*04fd306cSNickeau
662*04fd306cSNickeau        $store = $this->getReadStoreOrDefault();
663*04fd306cSNickeau        if (!($store instanceof MetadataDokuWikiStore)) {
664*04fd306cSNickeau            return null;
665*04fd306cSNickeau        }
666*04fd306cSNickeau        $medias = [];
667*04fd306cSNickeau
668*04fd306cSNickeau        $relation = $store->getCurrentFromName('relation');
669*04fd306cSNickeau        if (isset($relation['media'])) {
670*04fd306cSNickeau            /**
671*04fd306cSNickeau             * The relation is
672*04fd306cSNickeau             * $this->meta['relation']['media'][$src] = $exists;
673*04fd306cSNickeau             *
674*04fd306cSNickeau             */
675*04fd306cSNickeau            foreach ($relation['media'] as $src => $exists) {
676*04fd306cSNickeau                if ($exists) {
677*04fd306cSNickeau                    $medias[] = $src;
678*04fd306cSNickeau                }
679*04fd306cSNickeau            }
680*04fd306cSNickeau        }
681*04fd306cSNickeau        return $medias;
682*04fd306cSNickeau    }
683*04fd306cSNickeau
684*04fd306cSNickeau
685*04fd306cSNickeau    /**
686*04fd306cSNickeau     * Get author name
687*04fd306cSNickeau     *
688*04fd306cSNickeau     * @return string
689*04fd306cSNickeau     */
690*04fd306cSNickeau    public
691*04fd306cSNickeau    function getAuthor(): ?string
692*04fd306cSNickeau    {
693*04fd306cSNickeau        $store = $this->getReadStoreOrDefault();
694*04fd306cSNickeau        if (!($store instanceof MetadataDokuWikiStore)) {
695*04fd306cSNickeau            return null;
696*04fd306cSNickeau        }
697*04fd306cSNickeau
698*04fd306cSNickeau        return $store->getFromName('creator');
699*04fd306cSNickeau    }
700*04fd306cSNickeau
701*04fd306cSNickeau    /**
702*04fd306cSNickeau     * Get author ID
703*04fd306cSNickeau     *
704*04fd306cSNickeau     * @return string
705*04fd306cSNickeau     */
706*04fd306cSNickeau    public
707*04fd306cSNickeau    function getAuthorID(): ?string
708*04fd306cSNickeau    {
709*04fd306cSNickeau
710*04fd306cSNickeau        $store = $this->getReadStoreOrDefault();
711*04fd306cSNickeau        if (!($store instanceof MetadataDokuWikiStore)) {
712*04fd306cSNickeau            return null;
713*04fd306cSNickeau        }
714*04fd306cSNickeau
715*04fd306cSNickeau        return $store->getFromName('user');
716*04fd306cSNickeau
717*04fd306cSNickeau    }
718*04fd306cSNickeau
719*04fd306cSNickeau
720*04fd306cSNickeau    /**
721*04fd306cSNickeau     * Get the create date of page
722*04fd306cSNickeau     *
723*04fd306cSNickeau     * @return DateTime
724*04fd306cSNickeau     * @throws ExceptionNotFound
725*04fd306cSNickeau     */
726*04fd306cSNickeau    public
727*04fd306cSNickeau    function getCreatedTime(): ?DateTime
728*04fd306cSNickeau    {
729*04fd306cSNickeau        return $this->creationTime->getValue();
730*04fd306cSNickeau    }
731*04fd306cSNickeau
732*04fd306cSNickeau
733*04fd306cSNickeau    /**
734*04fd306cSNickeau     *
735*04fd306cSNickeau     * @return DateTime
736*04fd306cSNickeau     */
737*04fd306cSNickeau    public
738*04fd306cSNickeau    function getModifiedTime(): DateTime
739*04fd306cSNickeau    {
740*04fd306cSNickeau        return $this->modifiedTime->getValueFromStore();
741*04fd306cSNickeau    }
742*04fd306cSNickeau
743*04fd306cSNickeau    /**
744*04fd306cSNickeau     * @throws ExceptionNotFound
745*04fd306cSNickeau     */
746*04fd306cSNickeau    public
747*04fd306cSNickeau    function getModifiedTimeOrDefault(): DateTime
748*04fd306cSNickeau    {
749*04fd306cSNickeau        return $this->modifiedTime->getValueFromStoreOrDefault();
750*04fd306cSNickeau    }
751*04fd306cSNickeau
752*04fd306cSNickeau
753*04fd306cSNickeau    /**
754*04fd306cSNickeau     * Utility class, refresh the metadata (used only in test)
755*04fd306cSNickeau     * @deprecated if possible used {@link FetcherMarkup} instead
756*04fd306cSNickeau     */
757*04fd306cSNickeau    public function renderMetadataAndFlush(): MarkupPath
758*04fd306cSNickeau    {
759*04fd306cSNickeau
760*04fd306cSNickeau        if (!FileSystems::exists($this)) {
761*04fd306cSNickeau            if (PluginUtility::isDevOrTest()) {
762*04fd306cSNickeau                LogUtility::msg("You can't render the metadata of a markup path that does not exist ($this)");
763*04fd306cSNickeau            }
764*04fd306cSNickeau            return $this;
765*04fd306cSNickeau        }
766*04fd306cSNickeau
767*04fd306cSNickeau        try {
768*04fd306cSNickeau            $wikiPath = $this->getPathObject()->toWikiPath();
769*04fd306cSNickeau            FetcherMarkup::confRoot()
770*04fd306cSNickeau                ->setRequestedContextPath($wikiPath)
771*04fd306cSNickeau                ->setRequestedExecutingPath($wikiPath)
772*04fd306cSNickeau                ->setRequestedMimeToMetadata()
773*04fd306cSNickeau                ->build()
774*04fd306cSNickeau                ->processMetadataIfNotYetDone();
775*04fd306cSNickeau        } catch (ExceptionCast|ExceptionNotExists $e) {
776*04fd306cSNickeau            // not a wiki path, no meta
777*04fd306cSNickeau        }
778*04fd306cSNickeau
779*04fd306cSNickeau
780*04fd306cSNickeau        return $this;
781*04fd306cSNickeau
782*04fd306cSNickeau    }
783*04fd306cSNickeau
784*04fd306cSNickeau    /**
785*04fd306cSNickeau     * @return string|null
786*04fd306cSNickeau     * @deprecated for {@link Region}
787*04fd306cSNickeau     */
788*04fd306cSNickeau    public
789*04fd306cSNickeau    function getLocaleRegion(): ?string
790*04fd306cSNickeau    {
791*04fd306cSNickeau        return $this->region->getValueFromStore();
792*04fd306cSNickeau    }
793*04fd306cSNickeau
794*04fd306cSNickeau    public
795*04fd306cSNickeau    function getRegionOrDefault()
796*04fd306cSNickeau    {
797*04fd306cSNickeau
798*04fd306cSNickeau        return $this->region->getValueFromStoreOrDefault();
799*04fd306cSNickeau
800*04fd306cSNickeau    }
801*04fd306cSNickeau
802*04fd306cSNickeau    public
803*04fd306cSNickeau    function getLang(): ?string
804*04fd306cSNickeau    {
805*04fd306cSNickeau        return $this->lang->getValueFromStore();
806*04fd306cSNickeau    }
807*04fd306cSNickeau
808*04fd306cSNickeau    public function getLangOrDefault(): string
809*04fd306cSNickeau    {
810*04fd306cSNickeau        return $this->lang->getValueOrDefault();
811*04fd306cSNickeau    }
812*04fd306cSNickeau
813*04fd306cSNickeau    /**
814*04fd306cSNickeau     * The home page is an index page
815*04fd306cSNickeau     * Adapted from {@link FsWikiUtility::getHomePagePath()}
816*04fd306cSNickeau     * @return bool
817*04fd306cSNickeau     */
818*04fd306cSNickeau    public function isIndexPage(): bool
819*04fd306cSNickeau    {
820*04fd306cSNickeau
821*04fd306cSNickeau        $startPageName = Site::getIndexPageName();
822*04fd306cSNickeau        try {
823*04fd306cSNickeau            if ($this->getPathObject()->getLastNameWithoutExtension() === $startPageName) {
824*04fd306cSNickeau                return true;
825*04fd306cSNickeau            }
826*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
827*04fd306cSNickeau            // ok
828*04fd306cSNickeau        }
829*04fd306cSNickeau
830*04fd306cSNickeau        try {
831*04fd306cSNickeau            /**
832*04fd306cSNickeau             * page named like the NS inside the NS
833*04fd306cSNickeau             * ie ns:ns
834*04fd306cSNickeau             */
835*04fd306cSNickeau            $objectPath = $this->path;
836*04fd306cSNickeau            $parentPath = $this->path->getParent();
837*04fd306cSNickeau            if (!($parentPath instanceof WikiPath)) {
838*04fd306cSNickeau                return false;
839*04fd306cSNickeau            }
840*04fd306cSNickeau            if ($parentPath->getLastNameWithoutExtension() === $objectPath->getLastNameWithoutExtension()) {
841*04fd306cSNickeau                /**
842*04fd306cSNickeau                 * If the start page does not exists, this is the index page
843*04fd306cSNickeau                 */
844*04fd306cSNickeau                $startPage = $parentPath->resolveId($startPageName);
845*04fd306cSNickeau                if (!FileSystems::exists($startPage)) {
846*04fd306cSNickeau                    return true;
847*04fd306cSNickeau                }
848*04fd306cSNickeau            }
849*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
850*04fd306cSNickeau            // no parent, no last name, etc
851*04fd306cSNickeau        }
852*04fd306cSNickeau
853*04fd306cSNickeau        return false;
854*04fd306cSNickeau    }
855*04fd306cSNickeau
856*04fd306cSNickeau
857*04fd306cSNickeau    /**
858*04fd306cSNickeau     * @throws ExceptionNotFound
859*04fd306cSNickeau     */
860*04fd306cSNickeau    public
861*04fd306cSNickeau    function getPublishedTime(): DateTime
862*04fd306cSNickeau    {
863*04fd306cSNickeau        return $this->publishedDate->getValueFromStore();
864*04fd306cSNickeau    }
865*04fd306cSNickeau
866*04fd306cSNickeau    /**
867*04fd306cSNickeau     * @return bool
868*04fd306cSNickeau     * @deprecated for {@link FileSystems::exists()}
869*04fd306cSNickeau     */
870*04fd306cSNickeau    public function exists(): bool
871*04fd306cSNickeau    {
872*04fd306cSNickeau        return FileSystems::exists($this);
873*04fd306cSNickeau    }
874*04fd306cSNickeau
875*04fd306cSNickeau    /**
876*04fd306cSNickeau     * @return DateTime
877*04fd306cSNickeau     * @throws ExceptionNotFound
878*04fd306cSNickeau     */
879*04fd306cSNickeau    public
880*04fd306cSNickeau    function getPublishedElseCreationTime(): DateTime
881*04fd306cSNickeau    {
882*04fd306cSNickeau        return $this->publishedDate->getValueFromStoreOrDefault();
883*04fd306cSNickeau    }
884*04fd306cSNickeau
885*04fd306cSNickeau
886*04fd306cSNickeau    public
887*04fd306cSNickeau    function isLatePublication(): bool
888*04fd306cSNickeau    {
889*04fd306cSNickeau        try {
890*04fd306cSNickeau            $dateTime = $this->getPublishedElseCreationTime();
891*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
892*04fd306cSNickeau            return false;
893*04fd306cSNickeau        }
894*04fd306cSNickeau        return $dateTime > new DateTime('now');
895*04fd306cSNickeau    }
896*04fd306cSNickeau
897*04fd306cSNickeau    /**
898*04fd306cSNickeau     * The unique page Url (also known as Canonical URL) used:
899*04fd306cSNickeau     *   * in the link
900*04fd306cSNickeau     *   * in the canonical ref
901*04fd306cSNickeau     *   * in the site map
902*04fd306cSNickeau     * @return Url
903*04fd306cSNickeau     */
904*04fd306cSNickeau    public
905*04fd306cSNickeau    function getCanonicalUrl(): Url
906*04fd306cSNickeau    {
907*04fd306cSNickeau
908*04fd306cSNickeau        /**
909*04fd306cSNickeau         * Dokuwiki Methodology Taken from {@link tpl_metaheaders()}
910*04fd306cSNickeau         */
911*04fd306cSNickeau        if ($this->isRootHomePage()) {
912*04fd306cSNickeau            return UrlEndpoint::createBaseUrl();
913*04fd306cSNickeau        }
914*04fd306cSNickeau
915*04fd306cSNickeau        try {
916*04fd306cSNickeau            return UrlEndpoint::createDokuUrl()
917*04fd306cSNickeau                ->setQueryParameter(DokuwikiId::DOKUWIKI_ID_ATTRIBUTE, $this->getWikiId());
918*04fd306cSNickeau        } catch (ExceptionBadArgument $e) {
919*04fd306cSNickeau            LogUtility::error("This markup path ($this) can not be accessed externaly");
920*04fd306cSNickeau            return UrlEndpoint::createBaseUrl();
921*04fd306cSNickeau        }
922*04fd306cSNickeau
923*04fd306cSNickeau
924*04fd306cSNickeau    }
925*04fd306cSNickeau
926*04fd306cSNickeau
927*04fd306cSNickeau
928*04fd306cSNickeau    /**
929*04fd306cSNickeau     *
930*04fd306cSNickeau     * @return string|null - the locale facebook way
931*04fd306cSNickeau     * @throws ExceptionNotFound
932*04fd306cSNickeau     * @deprecated for {@link Locale}
933*04fd306cSNickeau     */
934*04fd306cSNickeau    public
935*04fd306cSNickeau    function getLocale($default = null): ?string
936*04fd306cSNickeau    {
937*04fd306cSNickeau        $value = $this->locale->getValueFromStore();
938*04fd306cSNickeau        if ($value === null) {
939*04fd306cSNickeau            return $default;
940*04fd306cSNickeau        }
941*04fd306cSNickeau        return $value;
942*04fd306cSNickeau    }
943*04fd306cSNickeau
944*04fd306cSNickeau
945*04fd306cSNickeau    /**
946*04fd306cSNickeau     *
947*04fd306cSNickeau     * @deprecated use a {@link FetcherMarkup::getFetchString()} instead
948*04fd306cSNickeau     */
949*04fd306cSNickeau    public function toXhtml(): string
950*04fd306cSNickeau    {
951*04fd306cSNickeau
952*04fd306cSNickeau        $fetcherMarkup = $this->createHtmlFetcherWithItselfAsContextPath();
953*04fd306cSNickeau        return $fetcherMarkup->getFetchString();
954*04fd306cSNickeau
955*04fd306cSNickeau
956*04fd306cSNickeau    }
957*04fd306cSNickeau
958*04fd306cSNickeau
959*04fd306cSNickeau    public
960*04fd306cSNickeau    function getHtmlAnchorLink($logicalTag = null): string
961*04fd306cSNickeau    {
962*04fd306cSNickeau        $id = $this->getPathObject()->getWikiId();
963*04fd306cSNickeau        try {
964*04fd306cSNickeau            return LinkMarkup::createFromPageIdOrPath($id)
965*04fd306cSNickeau                    ->toAttributes($logicalTag)
966*04fd306cSNickeau                    ->toHtmlEnterTag("a")
967*04fd306cSNickeau                . $this->getNameOrDefault()
968*04fd306cSNickeau                . "</a>";
969*04fd306cSNickeau        } catch (ExceptionCompile $e) {
970*04fd306cSNickeau            LogUtility::msg("The markup ref returns an error for the creation of the page anchor html link ($this). Error: {$e->getMessage()}");
971*04fd306cSNickeau            return "<a href=\"{$this->getCanonicalUrl()}\" data-wiki-id=\"$id\">{$this->getNameOrDefault()}</a>";
972*04fd306cSNickeau        }
973*04fd306cSNickeau    }
974*04fd306cSNickeau
975*04fd306cSNickeau
976*04fd306cSNickeau    /**
977*04fd306cSNickeau     * Without the `:` at the end
978*04fd306cSNickeau     * @return string
979*04fd306cSNickeau     * @throws ExceptionNotFound
980*04fd306cSNickeau     * @deprecated / shortcut for {@link WikiPath::getParent()}
981*04fd306cSNickeau     * Because a page has always a parent, the string is never null.
982*04fd306cSNickeau     */
983*04fd306cSNickeau    public function getNamespacePath(): string
984*04fd306cSNickeau    {
985*04fd306cSNickeau
986*04fd306cSNickeau        return $this->getParent()->toAbsoluteId();
987*04fd306cSNickeau
988*04fd306cSNickeau    }
989*04fd306cSNickeau
990*04fd306cSNickeau
991*04fd306cSNickeau    /**
992*04fd306cSNickeau     * @return $this
993*04fd306cSNickeau     * @deprecated use {@link MetadataDokuWikiStore::deleteAndFlush()}
994*04fd306cSNickeau     */
995*04fd306cSNickeau    public
996*04fd306cSNickeau    function deleteMetadatasAndFlush(): MarkupPath
997*04fd306cSNickeau    {
998*04fd306cSNickeau        MetadataDokuWikiStore::getOrCreateFromResource($this)
999*04fd306cSNickeau            ->deleteAndFlush();
1000*04fd306cSNickeau        return $this;
1001*04fd306cSNickeau    }
1002*04fd306cSNickeau
1003*04fd306cSNickeau    /**
1004*04fd306cSNickeau     * @throws ExceptionNotFound
1005*04fd306cSNickeau     */
1006*04fd306cSNickeau    public
1007*04fd306cSNickeau    function getName(): string
1008*04fd306cSNickeau    {
1009*04fd306cSNickeau
1010*04fd306cSNickeau        return $this->pageName->getValue();
1011*04fd306cSNickeau
1012*04fd306cSNickeau    }
1013*04fd306cSNickeau
1014*04fd306cSNickeau    public
1015*04fd306cSNickeau    function getNameOrDefault(): string
1016*04fd306cSNickeau    {
1017*04fd306cSNickeau
1018*04fd306cSNickeau        return ResourceName::createForResource($this)->getValueOrDefault();
1019*04fd306cSNickeau
1020*04fd306cSNickeau
1021*04fd306cSNickeau    }
1022*04fd306cSNickeau
1023*04fd306cSNickeau    /**
1024*04fd306cSNickeau     * @param $property
1025*04fd306cSNickeau     */
1026*04fd306cSNickeau    public
1027*04fd306cSNickeau    function unsetMetadata($property)
1028*04fd306cSNickeau    {
1029*04fd306cSNickeau        $meta = p_read_metadata($this->getPathObject()->getWikiId());
1030*04fd306cSNickeau        if (isset($meta['persistent'][$property])) {
1031*04fd306cSNickeau            unset($meta['persistent'][$property]);
1032*04fd306cSNickeau        }
1033*04fd306cSNickeau        p_save_metadata($this->getPathObject()->getWikiId(), $meta);
1034*04fd306cSNickeau
1035*04fd306cSNickeau    }
1036*04fd306cSNickeau
1037*04fd306cSNickeau    /**
1038*04fd306cSNickeau     * @return array - return the standard / generated metadata
1039*04fd306cSNickeau     * used to create a variable environment (context) in rendering
1040*04fd306cSNickeau     */
1041*04fd306cSNickeau    public
1042*04fd306cSNickeau    function getMetadataForRendering(): array
1043*04fd306cSNickeau    {
1044*04fd306cSNickeau
1045*04fd306cSNickeau        $metadataNames = [
1046*04fd306cSNickeau            PageH1::PROPERTY_NAME,
1047*04fd306cSNickeau            PageTitle::PROPERTY_NAME,
1048*04fd306cSNickeau            Lead::PROPERTY_NAME,
1049*04fd306cSNickeau            Canonical::PROPERTY_NAME,
1050*04fd306cSNickeau            PagePath::PROPERTY_NAME,
1051*04fd306cSNickeau            Label::PROPERTY_NAME,
1052*04fd306cSNickeau            PageDescription::PROPERTY_NAME,
1053*04fd306cSNickeau            ResourceName::PROPERTY_NAME,
1054*04fd306cSNickeau            PageType::PROPERTY_NAME,
1055*04fd306cSNickeau            Slug::PROPERTY_NAME,
1056*04fd306cSNickeau            PageTemplateName::PROPERTY_NAME,
1057*04fd306cSNickeau            DokuwikiId::DOKUWIKI_ID_ATTRIBUTE, // Dokuwiki id is deprecated for path
1058*04fd306cSNickeau            PageLevel::PROPERTY_NAME,
1059*04fd306cSNickeau            PageKeywords::PROPERTY_NAME
1060*04fd306cSNickeau        ];
1061*04fd306cSNickeau
1062*04fd306cSNickeau        /**
1063*04fd306cSNickeau         * The metadata that works only
1064*04fd306cSNickeau         * if the file exists
1065*04fd306cSNickeau         */
1066*04fd306cSNickeau        if (FileSystems::exists($this)) {
1067*04fd306cSNickeau            $metadataThatNeedsExistingFile = [
1068*04fd306cSNickeau                PageId::PROPERTY_NAME,
1069*04fd306cSNickeau                CreationDate::PROPERTY_NAME,
1070*04fd306cSNickeau                ModificationDate::PROPERTY_NAME,
1071*04fd306cSNickeau                PagePublicationDate::PROPERTY_NAME,
1072*04fd306cSNickeau                StartDate::PROPERTY_NAME,
1073*04fd306cSNickeau                EndDate::PROPERTY_NAME,
1074*04fd306cSNickeau            ];
1075*04fd306cSNickeau            $metadataNames = array_merge($metadataNames, $metadataThatNeedsExistingFile);
1076*04fd306cSNickeau        }
1077*04fd306cSNickeau
1078*04fd306cSNickeau
1079*04fd306cSNickeau        foreach ($metadataNames as $metadataName) {
1080*04fd306cSNickeau            try {
1081*04fd306cSNickeau                $metadata = Meta\Api\MetadataSystem::getForName($metadataName);
1082*04fd306cSNickeau            } catch (ExceptionNotFound $e) {
1083*04fd306cSNickeau                LogUtility::msg("The metadata ($metadataName) should be defined");
1084*04fd306cSNickeau                continue;
1085*04fd306cSNickeau            }
1086*04fd306cSNickeau            /**
1087*04fd306cSNickeau             * The Value or Default is returned
1088*04fd306cSNickeau             *
1089*04fd306cSNickeau             * Because the title/h1 should never be null
1090*04fd306cSNickeau             * otherwise a template link such as [[$path|$title]] will return a link without an description
1091*04fd306cSNickeau             * and therefore will be not visible
1092*04fd306cSNickeau             *
1093*04fd306cSNickeau             * ToStoreValue to get the string format of date/boolean in the {@link PipelineUtility}
1094*04fd306cSNickeau             * If we want the native value, we need to change the pipeline
1095*04fd306cSNickeau             */
1096*04fd306cSNickeau            $value = $metadata
1097*04fd306cSNickeau                ->setResource($this)
1098*04fd306cSNickeau                ->setReadStore(MetadataDokuWikiStore::class)
1099*04fd306cSNickeau                ->setWriteStore(TemplateStore::class)
1100*04fd306cSNickeau                ->buildFromReadStore()
1101*04fd306cSNickeau                ->toStoreValueOrDefault();
1102*04fd306cSNickeau            $array[$metadataName] = $value;
1103*04fd306cSNickeau        }
1104*04fd306cSNickeau
1105*04fd306cSNickeau        $array["url"] = $this->getCanonicalUrl()->toAbsoluteUrl()->toString();
1106*04fd306cSNickeau        $array["now"] = Iso8601Date::createFromNow()->toString();
1107*04fd306cSNickeau        return $array;
1108*04fd306cSNickeau
1109*04fd306cSNickeau    }
1110*04fd306cSNickeau
1111*04fd306cSNickeau
1112*04fd306cSNickeau    public
1113*04fd306cSNickeau    function getPublishedTimeAsString(): ?string
1114*04fd306cSNickeau    {
1115*04fd306cSNickeau        return $this->getPublishedTime() !== null ? $this->getPublishedTime()->format(Iso8601Date::getFormat()) : null;
1116*04fd306cSNickeau    }
1117*04fd306cSNickeau
1118*04fd306cSNickeau
1119*04fd306cSNickeau    /**
1120*04fd306cSNickeau     * @throws ExceptionNotFound
1121*04fd306cSNickeau     */
1122*04fd306cSNickeau    public
1123*04fd306cSNickeau    function getEndDate(): DateTime
1124*04fd306cSNickeau    {
1125*04fd306cSNickeau        return $this->endDate->getValue();
1126*04fd306cSNickeau    }
1127*04fd306cSNickeau
1128*04fd306cSNickeau
1129*04fd306cSNickeau
1130*04fd306cSNickeau    /**
1131*04fd306cSNickeau     * @throws ExceptionNotFound
1132*04fd306cSNickeau     */
1133*04fd306cSNickeau    public
1134*04fd306cSNickeau    function getStartDate(): DateTime
1135*04fd306cSNickeau    {
1136*04fd306cSNickeau        return $this->startDate->getValue();
1137*04fd306cSNickeau    }
1138*04fd306cSNickeau
1139*04fd306cSNickeau    /**
1140*04fd306cSNickeau     * A page id
1141*04fd306cSNickeau     * @return string
1142*04fd306cSNickeau     * @throws ExceptionNotFound - when the page does not exist
1143*04fd306cSNickeau     */
1144*04fd306cSNickeau    public
1145*04fd306cSNickeau    function getPageId(): string
1146*04fd306cSNickeau    {
1147*04fd306cSNickeau        return PageId::createForPage($this)->getValue();
1148*04fd306cSNickeau    }
1149*04fd306cSNickeau
1150*04fd306cSNickeau
1151*04fd306cSNickeau    /**
1152*04fd306cSNickeau     * @throws ExceptionNotExists
1153*04fd306cSNickeau     */
1154*04fd306cSNickeau    public
1155*04fd306cSNickeau    function fetchAnalyticsDocument(): FetcherMarkup
1156*04fd306cSNickeau    {
1157*04fd306cSNickeau        return renderer_plugin_combo_analytics::createAnalyticsFetcherForPageFragment($this);
1158*04fd306cSNickeau    }
1159*04fd306cSNickeau
1160*04fd306cSNickeau    /**
1161*04fd306cSNickeau     * @throws ExceptionCompile
1162*04fd306cSNickeau     * @throws ExceptionNotExists
1163*04fd306cSNickeau     */
1164*04fd306cSNickeau    public
1165*04fd306cSNickeau    function fetchAnalyticsPath(): Path
1166*04fd306cSNickeau    {
1167*04fd306cSNickeau        $fetcher = renderer_plugin_combo_analytics::createAnalyticsFetcherForPageFragment($this);
1168*04fd306cSNickeau        return $fetcher->processIfNeededAndGetFetchPath();
1169*04fd306cSNickeau
1170*04fd306cSNickeau    }
1171*04fd306cSNickeau
1172*04fd306cSNickeau    /**
1173*04fd306cSNickeau     */
1174*04fd306cSNickeau    public
1175*04fd306cSNickeau    function getDatabasePage(): DatabasePageRow
1176*04fd306cSNickeau    {
1177*04fd306cSNickeau
1178*04fd306cSNickeau        return DatabasePageRow::getFromPageObject($this);
1179*04fd306cSNickeau
1180*04fd306cSNickeau    }
1181*04fd306cSNickeau
1182*04fd306cSNickeau    /**
1183*04fd306cSNickeau     * @throws ExceptionSqliteNotAvailable
1184*04fd306cSNickeau     */
1185*04fd306cSNickeau    public
1186*04fd306cSNickeau    function getOrCreateDatabasePage(): DatabasePageRow
1187*04fd306cSNickeau    {
1188*04fd306cSNickeau
1189*04fd306cSNickeau        return DatabasePageRow::getOrCreateFromPageObject($this);
1190*04fd306cSNickeau
1191*04fd306cSNickeau    }
1192*04fd306cSNickeau
1193*04fd306cSNickeau    public
1194*04fd306cSNickeau    function canBeUpdatedByCurrentUser(): bool
1195*04fd306cSNickeau    {
1196*04fd306cSNickeau        return Identity::isWriter($this->getWikiId());
1197*04fd306cSNickeau    }
1198*04fd306cSNickeau
1199*04fd306cSNickeau
1200*04fd306cSNickeau    public
1201*04fd306cSNickeau    function isRootHomePage(): bool
1202*04fd306cSNickeau    {
1203*04fd306cSNickeau        global $conf;
1204*04fd306cSNickeau        $startPageName = $conf['start'];
1205*04fd306cSNickeau        return $this->getPathObject()->toAbsoluteId() === ":$startPageName";
1206*04fd306cSNickeau
1207*04fd306cSNickeau    }
1208*04fd306cSNickeau
1209*04fd306cSNickeau
1210*04fd306cSNickeau    /**
1211*04fd306cSNickeau     * @throws ExceptionNotFound
1212*04fd306cSNickeau     */
1213*04fd306cSNickeau    public
1214*04fd306cSNickeau    function getPageType(): string
1215*04fd306cSNickeau    {
1216*04fd306cSNickeau        return $this->type->getValueFromStore();
1217*04fd306cSNickeau    }
1218*04fd306cSNickeau
1219*04fd306cSNickeau    /**
1220*04fd306cSNickeau     * @throws ExceptionNotFound
1221*04fd306cSNickeau     * @deprecated
1222*04fd306cSNickeau     */
1223*04fd306cSNickeau    public
1224*04fd306cSNickeau    function getCanonical(): WikiPath
1225*04fd306cSNickeau    {
1226*04fd306cSNickeau        return $this->canonical->getValue();
1227*04fd306cSNickeau    }
1228*04fd306cSNickeau
1229*04fd306cSNickeau    /**
1230*04fd306cSNickeau     * Create a canonical from the last page path part.
1231*04fd306cSNickeau     *
1232*04fd306cSNickeau     * @return string|null
1233*04fd306cSNickeau     * @throws ExceptionNotFound
1234*04fd306cSNickeau     */
1235*04fd306cSNickeau    public
1236*04fd306cSNickeau    function getDefaultCanonical(): ?string
1237*04fd306cSNickeau    {
1238*04fd306cSNickeau        return $this->canonical->getDefaultValue();
1239*04fd306cSNickeau    }
1240*04fd306cSNickeau
1241*04fd306cSNickeau    /**
1242*04fd306cSNickeau     * @throws ExceptionNotFound
1243*04fd306cSNickeau     */
1244*04fd306cSNickeau    public
1245*04fd306cSNickeau    function getLayout()
1246*04fd306cSNickeau    {
1247*04fd306cSNickeau        return $this->layout->getValueFromStore();
1248*04fd306cSNickeau    }
1249*04fd306cSNickeau
1250*04fd306cSNickeau    /**
1251*04fd306cSNickeau     * @throws ExceptionNotFound
1252*04fd306cSNickeau     */
1253*04fd306cSNickeau    public
1254*04fd306cSNickeau    function getDefaultPageName(): string
1255*04fd306cSNickeau    {
1256*04fd306cSNickeau        return $this->pageName->getDefaultValue();
1257*04fd306cSNickeau    }
1258*04fd306cSNickeau
1259*04fd306cSNickeau    public
1260*04fd306cSNickeau    function getDefaultTitle(): string
1261*04fd306cSNickeau    {
1262*04fd306cSNickeau        return $this->title->getDefaultValue();
1263*04fd306cSNickeau    }
1264*04fd306cSNickeau
1265*04fd306cSNickeau    /**
1266*04fd306cSNickeau     * @throws ExceptionNotFound
1267*04fd306cSNickeau     */
1268*04fd306cSNickeau    public
1269*04fd306cSNickeau    function getDefaultH1()
1270*04fd306cSNickeau    {
1271*04fd306cSNickeau        return $this->h1->getValueOrDefault();
1272*04fd306cSNickeau    }
1273*04fd306cSNickeau
1274*04fd306cSNickeau    public
1275*04fd306cSNickeau    function getDefaultType(): string
1276*04fd306cSNickeau    {
1277*04fd306cSNickeau        return $this->type->getDefaultValue();
1278*04fd306cSNickeau    }
1279*04fd306cSNickeau
1280*04fd306cSNickeau    public
1281*04fd306cSNickeau    function getDefaultLayout(): string
1282*04fd306cSNickeau    {
1283*04fd306cSNickeau        return $this->layout->getDefaultValue();
1284*04fd306cSNickeau    }
1285*04fd306cSNickeau
1286*04fd306cSNickeau
1287*04fd306cSNickeau    /**
1288*04fd306cSNickeau     *
1289*04fd306cSNickeau     * @throws ExceptionCompile
1290*04fd306cSNickeau     */
1291*04fd306cSNickeau    public
1292*04fd306cSNickeau    function setLowQualityIndicatorCalculation($bool): MarkupPath
1293*04fd306cSNickeau    {
1294*04fd306cSNickeau        return $this->setQualityIndicatorAndDeleteCacheIfNeeded($this->lowQualityIndicatorCalculated, $bool);
1295*04fd306cSNickeau    }
1296*04fd306cSNickeau
1297*04fd306cSNickeau
1298*04fd306cSNickeau    /**
1299*04fd306cSNickeau     * Change the quality indicator
1300*04fd306cSNickeau     * and if the quality level has become low
1301*04fd306cSNickeau     * and that the protection is on, delete the cache
1302*04fd306cSNickeau     * @param MetadataBoolean $lowQualityAttributeName
1303*04fd306cSNickeau     * @param bool $value
1304*04fd306cSNickeau     * @return MarkupPath
1305*04fd306cSNickeau     * @throws ExceptionBadArgument - if the value cannot be persisted
1306*04fd306cSNickeau     */
1307*04fd306cSNickeau    private
1308*04fd306cSNickeau    function setQualityIndicatorAndDeleteCacheIfNeeded(MetadataBoolean $lowQualityAttributeName, bool $value): MarkupPath
1309*04fd306cSNickeau    {
1310*04fd306cSNickeau        try {
1311*04fd306cSNickeau            $actualValue = $lowQualityAttributeName->getValue();
1312*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
1313*04fd306cSNickeau            $actualValue = null;
1314*04fd306cSNickeau        }
1315*04fd306cSNickeau        if ($value !== $actualValue) {
1316*04fd306cSNickeau            $lowQualityAttributeName
1317*04fd306cSNickeau                ->setValue($value)
1318*04fd306cSNickeau                ->persist();
1319*04fd306cSNickeau        }
1320*04fd306cSNickeau        return $this;
1321*04fd306cSNickeau    }
1322*04fd306cSNickeau
1323*04fd306cSNickeau
1324*04fd306cSNickeau    /**
1325*04fd306cSNickeau     * @throws ExceptionNotFound
1326*04fd306cSNickeau     */
1327*04fd306cSNickeau    public
1328*04fd306cSNickeau    function getLowQualityIndicatorCalculated()
1329*04fd306cSNickeau    {
1330*04fd306cSNickeau
1331*04fd306cSNickeau        return $this->lowQualityIndicatorCalculated->getValueOrDefault();
1332*04fd306cSNickeau
1333*04fd306cSNickeau    }
1334*04fd306cSNickeau
1335*04fd306cSNickeau    /**
1336*04fd306cSNickeau     * @return PageImage[]
1337*04fd306cSNickeau     * @deprecated
1338*04fd306cSNickeau     */
1339*04fd306cSNickeau    public
1340*04fd306cSNickeau    function getPageMetadataImages(): array
1341*04fd306cSNickeau    {
1342*04fd306cSNickeau        return $this->pageImages->getValueAsPageImages();
1343*04fd306cSNickeau    }
1344*04fd306cSNickeau
1345*04fd306cSNickeau
1346*04fd306cSNickeau    /**
1347*04fd306cSNickeau     * @param array|string $jsonLd
1348*04fd306cSNickeau     * @return $this
1349*04fd306cSNickeau     * @throws ExceptionCompile
1350*04fd306cSNickeau     * @deprecated for {@link LdJson}
1351*04fd306cSNickeau     */
1352*04fd306cSNickeau    public
1353*04fd306cSNickeau    function setJsonLd($jsonLd): MarkupPath
1354*04fd306cSNickeau    {
1355*04fd306cSNickeau        $this->ldJson
1356*04fd306cSNickeau            ->setValue($jsonLd)
1357*04fd306cSNickeau            ->sendToWriteStore();
1358*04fd306cSNickeau        return $this;
1359*04fd306cSNickeau    }
1360*04fd306cSNickeau
1361*04fd306cSNickeau    /**
1362*04fd306cSNickeau     * @throws ExceptionCompile
1363*04fd306cSNickeau     */
1364*04fd306cSNickeau    public
1365*04fd306cSNickeau    function setPageType(string $value): MarkupPath
1366*04fd306cSNickeau    {
1367*04fd306cSNickeau        $this->type
1368*04fd306cSNickeau            ->setValue($value)
1369*04fd306cSNickeau            ->sendToWriteStore();
1370*04fd306cSNickeau        return $this;
1371*04fd306cSNickeau    }
1372*04fd306cSNickeau
1373*04fd306cSNickeau
1374*04fd306cSNickeau    /**
1375*04fd306cSNickeau     * @param $aliasPath
1376*04fd306cSNickeau     * @param string $aliasType
1377*04fd306cSNickeau     * @return Alias
1378*04fd306cSNickeau     * @deprecated for {@link Aliases}
1379*04fd306cSNickeau     */
1380*04fd306cSNickeau    public
1381*04fd306cSNickeau    function addAndGetAlias($aliasPath, string $aliasType = AliasType::REDIRECT): Alias
1382*04fd306cSNickeau    {
1383*04fd306cSNickeau
1384*04fd306cSNickeau        return $this->aliases->addAndGetAlias($aliasPath, $aliasType);
1385*04fd306cSNickeau
1386*04fd306cSNickeau    }
1387*04fd306cSNickeau
1388*04fd306cSNickeau
1389*04fd306cSNickeau    /**
1390*04fd306cSNickeau     * @return Alias[]
1391*04fd306cSNickeau     * @throws ExceptionNotFound
1392*04fd306cSNickeau     */
1393*04fd306cSNickeau    public
1394*04fd306cSNickeau    function getAliases(): array
1395*04fd306cSNickeau    {
1396*04fd306cSNickeau        return $this->aliases->getValueAsAlias();
1397*04fd306cSNickeau    }
1398*04fd306cSNickeau
1399*04fd306cSNickeau    /**
1400*04fd306cSNickeau     * @return string
1401*04fd306cSNickeau     */
1402*04fd306cSNickeau    public
1403*04fd306cSNickeau    function getSlugOrDefault(): string
1404*04fd306cSNickeau    {
1405*04fd306cSNickeau        try {
1406*04fd306cSNickeau            return $this->getSlug();
1407*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
1408*04fd306cSNickeau            return $this->getDefaultSlug();
1409*04fd306cSNickeau        }
1410*04fd306cSNickeau
1411*04fd306cSNickeau    }
1412*04fd306cSNickeau
1413*04fd306cSNickeau    /**
1414*04fd306cSNickeau     *
1415*04fd306cSNickeau     * @return string
1416*04fd306cSNickeau     *
1417*04fd306cSNickeau     */
1418*04fd306cSNickeau    public
1419*04fd306cSNickeau    function getDefaultSlug(): string
1420*04fd306cSNickeau    {
1421*04fd306cSNickeau        return $this->slug->getDefaultValue();
1422*04fd306cSNickeau    }
1423*04fd306cSNickeau
1424*04fd306cSNickeau    /**
1425*04fd306cSNickeau     * The parent page is the parent in the page tree directory
1426*04fd306cSNickeau     *
1427*04fd306cSNickeau     * If the page is at the root, the parent page is the root home
1428*04fd306cSNickeau     * Only the root home does not have any parent page and return null.
1429*04fd306cSNickeau     *
1430*04fd306cSNickeau     * @return MarkupPath
1431*04fd306cSNickeau     * @throws ExceptionNotFound
1432*04fd306cSNickeau     */
1433*04fd306cSNickeau    public function getParent(): MarkupPath
1434*04fd306cSNickeau    {
1435*04fd306cSNickeau
1436*04fd306cSNickeau        $names = $this->getNames();
1437*04fd306cSNickeau        if (sizeof($names) == 0) {
1438*04fd306cSNickeau            throw new ExceptionNotFound("No parent page");
1439*04fd306cSNickeau        }
1440*04fd306cSNickeau        $slice = 1;
1441*04fd306cSNickeau        if ($this->isIndexPage()) {
1442*04fd306cSNickeau            /**
1443*04fd306cSNickeau             * The parent of a home page
1444*04fd306cSNickeau             * is in the parent directory
1445*04fd306cSNickeau             */
1446*04fd306cSNickeau            $slice = 2;
1447*04fd306cSNickeau        }
1448*04fd306cSNickeau        /**
1449*04fd306cSNickeau         * Delete the last or the two last parts
1450*04fd306cSNickeau         */
1451*04fd306cSNickeau        if (sizeof($names) < $slice) {
1452*04fd306cSNickeau            throw new ExceptionNotFound("No parent page");
1453*04fd306cSNickeau        }
1454*04fd306cSNickeau        /**
1455*04fd306cSNickeau         * Get the actual directory for a page
1456*04fd306cSNickeau         * or the parent directory for a home page
1457*04fd306cSNickeau         */
1458*04fd306cSNickeau        $parentNames = array_slice($names, 0, sizeof($names) - $slice);
1459*04fd306cSNickeau        /**
1460*04fd306cSNickeau         * Create the parent namespace id
1461*04fd306cSNickeau         */
1462*04fd306cSNickeau        $parentNamespaceId = implode(WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT, $parentNames) . WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT;
1463*04fd306cSNickeau        try {
1464*04fd306cSNickeau            return self::getIndexPageFromNamespace($parentNamespaceId);
1465*04fd306cSNickeau        } catch (ExceptionBadSyntax $e) {
1466*04fd306cSNickeau            $message = "Error on getParentPage, null returned - Error: {$e->getMessage()}";
1467*04fd306cSNickeau            LogUtility::internalError($message);
1468*04fd306cSNickeau            throw new ExceptionNotFound($message);
1469*04fd306cSNickeau        }
1470*04fd306cSNickeau
1471*04fd306cSNickeau    }
1472*04fd306cSNickeau
1473*04fd306cSNickeau    /**
1474*04fd306cSNickeau     * @throws ExceptionCompile
1475*04fd306cSNickeau     */
1476*04fd306cSNickeau    public
1477*04fd306cSNickeau    function setDescription($description): MarkupPath
1478*04fd306cSNickeau    {
1479*04fd306cSNickeau
1480*04fd306cSNickeau        $this->description
1481*04fd306cSNickeau            ->setValue($description)
1482*04fd306cSNickeau            ->sendToWriteStore();
1483*04fd306cSNickeau        return $this;
1484*04fd306cSNickeau    }
1485*04fd306cSNickeau
1486*04fd306cSNickeau    /**
1487*04fd306cSNickeau     * @throws ExceptionCompile
1488*04fd306cSNickeau     * @deprecated uses {@link EndDate} instead
1489*04fd306cSNickeau     */
1490*04fd306cSNickeau    public
1491*04fd306cSNickeau    function setEndDate($value): MarkupPath
1492*04fd306cSNickeau    {
1493*04fd306cSNickeau        $this->endDate
1494*04fd306cSNickeau            ->setFromStoreValue($value)
1495*04fd306cSNickeau            ->sendToWriteStore();
1496*04fd306cSNickeau        return $this;
1497*04fd306cSNickeau    }
1498*04fd306cSNickeau
1499*04fd306cSNickeau    /**
1500*04fd306cSNickeau     * @throws ExceptionCompile
1501*04fd306cSNickeau     * @deprecated uses {@link StartDate} instead
1502*04fd306cSNickeau     */
1503*04fd306cSNickeau    public
1504*04fd306cSNickeau    function setStartDate($value): MarkupPath
1505*04fd306cSNickeau    {
1506*04fd306cSNickeau        $this->startDate
1507*04fd306cSNickeau            ->setFromStoreValue($value)
1508*04fd306cSNickeau            ->sendToWriteStore();
1509*04fd306cSNickeau        return $this;
1510*04fd306cSNickeau    }
1511*04fd306cSNickeau
1512*04fd306cSNickeau    /**
1513*04fd306cSNickeau     * @throws ExceptionCompile
1514*04fd306cSNickeau     */
1515*04fd306cSNickeau    public
1516*04fd306cSNickeau    function setPublishedDate($value): MarkupPath
1517*04fd306cSNickeau    {
1518*04fd306cSNickeau        $this->publishedDate
1519*04fd306cSNickeau            ->setFromStoreValue($value)
1520*04fd306cSNickeau            ->sendToWriteStore();
1521*04fd306cSNickeau        return $this;
1522*04fd306cSNickeau    }
1523*04fd306cSNickeau
1524*04fd306cSNickeau    /**
1525*04fd306cSNickeau     * Utility to {@link ResourceName::setValue()}
1526*04fd306cSNickeau     * Used mostly to create page in test
1527*04fd306cSNickeau     * @throws ExceptionCompile
1528*04fd306cSNickeau     * @deprecated use not persist
1529*04fd306cSNickeau     */
1530*04fd306cSNickeau    public
1531*04fd306cSNickeau    function setPageName($value): MarkupPath
1532*04fd306cSNickeau    {
1533*04fd306cSNickeau        $this->pageName
1534*04fd306cSNickeau            ->setValue($value)
1535*04fd306cSNickeau            ->sendToWriteStore();
1536*04fd306cSNickeau        return $this;
1537*04fd306cSNickeau    }
1538*04fd306cSNickeau
1539*04fd306cSNickeau
1540*04fd306cSNickeau    /**
1541*04fd306cSNickeau     * @throws ExceptionCompile
1542*04fd306cSNickeau     */
1543*04fd306cSNickeau    public
1544*04fd306cSNickeau    function setTitle($value): MarkupPath
1545*04fd306cSNickeau    {
1546*04fd306cSNickeau        $this->title
1547*04fd306cSNickeau            ->setValue($value)
1548*04fd306cSNickeau            ->sendToWriteStore();
1549*04fd306cSNickeau        return $this;
1550*04fd306cSNickeau    }
1551*04fd306cSNickeau
1552*04fd306cSNickeau    /**
1553*04fd306cSNickeau     * @throws ExceptionCompile
1554*04fd306cSNickeau     */
1555*04fd306cSNickeau    public
1556*04fd306cSNickeau    function setH1($value): MarkupPath
1557*04fd306cSNickeau    {
1558*04fd306cSNickeau        $this->h1
1559*04fd306cSNickeau            ->setValue($value)
1560*04fd306cSNickeau            ->sendToWriteStore();
1561*04fd306cSNickeau        return $this;
1562*04fd306cSNickeau    }
1563*04fd306cSNickeau
1564*04fd306cSNickeau    /**
1565*04fd306cSNickeau     * @throws Exception
1566*04fd306cSNickeau     */
1567*04fd306cSNickeau    public
1568*04fd306cSNickeau    function setRegion($value): MarkupPath
1569*04fd306cSNickeau    {
1570*04fd306cSNickeau        $this->region
1571*04fd306cSNickeau            ->setFromStoreValue($value)
1572*04fd306cSNickeau            ->sendToWriteStore();
1573*04fd306cSNickeau        return $this;
1574*04fd306cSNickeau    }
1575*04fd306cSNickeau
1576*04fd306cSNickeau    /**
1577*04fd306cSNickeau     * @throws ExceptionCompile
1578*04fd306cSNickeau     */
1579*04fd306cSNickeau    public
1580*04fd306cSNickeau    function setLang($value): MarkupPath
1581*04fd306cSNickeau    {
1582*04fd306cSNickeau
1583*04fd306cSNickeau        $this->lang
1584*04fd306cSNickeau            ->setFromStoreValue($value)
1585*04fd306cSNickeau            ->sendToWriteStore();
1586*04fd306cSNickeau        return $this;
1587*04fd306cSNickeau    }
1588*04fd306cSNickeau
1589*04fd306cSNickeau    /**
1590*04fd306cSNickeau     * @throws ExceptionCompile
1591*04fd306cSNickeau     */
1592*04fd306cSNickeau    public
1593*04fd306cSNickeau    function setLayout($value): MarkupPath
1594*04fd306cSNickeau    {
1595*04fd306cSNickeau        $this->layout
1596*04fd306cSNickeau            ->setValue($value)
1597*04fd306cSNickeau            ->sendToWriteStore();
1598*04fd306cSNickeau        return $this;
1599*04fd306cSNickeau    }
1600*04fd306cSNickeau
1601*04fd306cSNickeau
1602*04fd306cSNickeau    /**
1603*04fd306cSNickeau     *
1604*04fd306cSNickeau     * We manage the properties by setter and getter
1605*04fd306cSNickeau     *
1606*04fd306cSNickeau     * Why ?
1607*04fd306cSNickeau     *   * Because we can capture the updates
1608*04fd306cSNickeau     *   * Because setter are the entry point to good quality data
1609*04fd306cSNickeau     *   * Because dokuwiki may cache the metadata (see below)
1610*04fd306cSNickeau     *
1611*04fd306cSNickeau     * Note all properties have been migrated
1612*04fd306cSNickeau     * but they should be initialized below
1613*04fd306cSNickeau     *
1614*04fd306cSNickeau     * Dokuwiki cache: the data may be cached without our consent
1615*04fd306cSNickeau     * The method {@link p_get_metadata()} does it with this logic
1616*04fd306cSNickeau     * ```
1617*04fd306cSNickeau     * $cache = ($ID == $id);
1618*04fd306cSNickeau     * $meta = p_read_metadata($id, $cache);
1619*04fd306cSNickeau     * ```
1620*04fd306cSNickeau     */
1621*04fd306cSNickeau    private
1622*04fd306cSNickeau    function buildPropertiesFromFileSystem()
1623*04fd306cSNickeau    {
1624*04fd306cSNickeau
1625*04fd306cSNickeau        /**
1626*04fd306cSNickeau         * New meta system
1627*04fd306cSNickeau         * Even if it does not exist, the metadata object should be instantiated
1628*04fd306cSNickeau         * otherwise, there is a null exception
1629*04fd306cSNickeau         */
1630*04fd306cSNickeau        $this->cacheExpirationDate = CacheExpirationDate::createForPage($this);
1631*04fd306cSNickeau        $this->aliases = Aliases::createForPage($this);
1632*04fd306cSNickeau        $this->pageImages = PageImages::createForPage($this);
1633*04fd306cSNickeau        $this->pageName = ResourceName::createForResource($this);
1634*04fd306cSNickeau        $this->cacheExpirationFrequency = CacheExpirationFrequency::createForPage($this);
1635*04fd306cSNickeau        $this->ldJson = LdJson::createForPage($this);
1636*04fd306cSNickeau        $this->canonical = Canonical::createForPage($this);
1637*04fd306cSNickeau        $this->description = PageDescription::createForPage($this);
1638*04fd306cSNickeau        $this->h1 = PageH1::createForPage($this);
1639*04fd306cSNickeau        $this->type = PageType::createForPage($this);
1640*04fd306cSNickeau        $this->creationTime = CreationDate::createForPage($this);
1641*04fd306cSNickeau        $this->title = PageTitle::createForMarkup($this);
1642*04fd306cSNickeau        $this->keywords = PageKeywords::createForPage($this);
1643*04fd306cSNickeau        $this->publishedDate = PagePublicationDate::createFromPage($this);
1644*04fd306cSNickeau        $this->startDate = StartDate::createFromPage($this);
1645*04fd306cSNickeau        $this->endDate = EndDate::createFromPage($this);
1646*04fd306cSNickeau        $this->locale = Locale::createForPage($this);
1647*04fd306cSNickeau        $this->lang = Lang::createForMarkup($this);
1648*04fd306cSNickeau        $this->region = Region::createForPage($this);
1649*04fd306cSNickeau        $this->slug = \ComboStrap\Slug::createForPage($this);
1650*04fd306cSNickeau        $this->canBeOfLowQuality = LowQualityPageOverwrite::createForPage($this);
1651*04fd306cSNickeau        $this->lowQualityIndicatorCalculated = LowQualityCalculatedIndicator::createFromPage($this);
1652*04fd306cSNickeau        $this->qualityMonitoringIndicator = QualityDynamicMonitoringOverwrite::createFromPage($this);
1653*04fd306cSNickeau        $this->modifiedTime = ModificationDate::createForPage($this);
1654*04fd306cSNickeau        $this->pageUrlPath = PageUrlPath::createForPage($this);
1655*04fd306cSNickeau        $this->layout = PageTemplateName::createFromPage($this);
1656*04fd306cSNickeau
1657*04fd306cSNickeau    }
1658*04fd306cSNickeau
1659*04fd306cSNickeau
1660*04fd306cSNickeau    function getPageIdAbbr()
1661*04fd306cSNickeau    {
1662*04fd306cSNickeau
1663*04fd306cSNickeau        if ($this->getPageId() === null) return null;
1664*04fd306cSNickeau        return PageId::getAbbreviated($this->getPageId());
1665*04fd306cSNickeau
1666*04fd306cSNickeau    }
1667*04fd306cSNickeau
1668*04fd306cSNickeau    public
1669*04fd306cSNickeau    function setDatabasePage(DatabasePageRow $databasePage): MarkupPath
1670*04fd306cSNickeau    {
1671*04fd306cSNickeau        $this->databasePage = $databasePage;
1672*04fd306cSNickeau        return $this;
1673*04fd306cSNickeau    }
1674*04fd306cSNickeau
1675*04fd306cSNickeau
1676*04fd306cSNickeau    /**
1677*04fd306cSNickeau     * @return string|null
1678*04fd306cSNickeau     *
1679*04fd306cSNickeau     * @throws ExceptionNotFound
1680*04fd306cSNickeau     */
1681*04fd306cSNickeau    public
1682*04fd306cSNickeau    function getSlug(): string
1683*04fd306cSNickeau    {
1684*04fd306cSNickeau        return $this->slug->getValue();
1685*04fd306cSNickeau    }
1686*04fd306cSNickeau
1687*04fd306cSNickeau
1688*04fd306cSNickeau    /**
1689*04fd306cSNickeau     * @throws ExceptionCompile
1690*04fd306cSNickeau     */
1691*04fd306cSNickeau    public
1692*04fd306cSNickeau    function setSlug($slug): MarkupPath
1693*04fd306cSNickeau    {
1694*04fd306cSNickeau        $this->slug
1695*04fd306cSNickeau            ->setFromStoreValue($slug)
1696*04fd306cSNickeau            ->sendToWriteStore();
1697*04fd306cSNickeau        return $this;
1698*04fd306cSNickeau    }
1699*04fd306cSNickeau
1700*04fd306cSNickeau
1701*04fd306cSNickeau    /**
1702*04fd306cSNickeau     * @return string - the id in the Url
1703*04fd306cSNickeau     */
1704*04fd306cSNickeau    public function getUrlId(): string
1705*04fd306cSNickeau    {
1706*04fd306cSNickeau        return $this->pageUrlPath->getValueOrDefaultAsWikiId();
1707*04fd306cSNickeau    }
1708*04fd306cSNickeau
1709*04fd306cSNickeau
1710*04fd306cSNickeau    /**
1711*04fd306cSNickeau     * @throws ExceptionCompile
1712*04fd306cSNickeau     */
1713*04fd306cSNickeau    public
1714*04fd306cSNickeau    function setQualityMonitoringIndicator($boolean): MarkupPath
1715*04fd306cSNickeau    {
1716*04fd306cSNickeau        $this->qualityMonitoringIndicator
1717*04fd306cSNickeau            ->setFromStoreValue($boolean)
1718*04fd306cSNickeau            ->sendToWriteStore();
1719*04fd306cSNickeau        return $this;
1720*04fd306cSNickeau    }
1721*04fd306cSNickeau
1722*04fd306cSNickeau    /**
1723*04fd306cSNickeau     *
1724*04fd306cSNickeau     * @param $aliasPath - third information - the alias used to build this page
1725*04fd306cSNickeau     */
1726*04fd306cSNickeau    public
1727*04fd306cSNickeau    function setBuildAliasPath($aliasPath)
1728*04fd306cSNickeau    {
1729*04fd306cSNickeau        $this->buildAliasPath = $aliasPath;
1730*04fd306cSNickeau    }
1731*04fd306cSNickeau
1732*04fd306cSNickeau    public
1733*04fd306cSNickeau    function getBuildAlias(): ?Alias
1734*04fd306cSNickeau    {
1735*04fd306cSNickeau        if ($this->buildAliasPath === null) return null;
1736*04fd306cSNickeau        try {
1737*04fd306cSNickeau            $aliases = $this->getAliases();
1738*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
1739*04fd306cSNickeau            // should not
1740*04fd306cSNickeau            return null;
1741*04fd306cSNickeau        }
1742*04fd306cSNickeau        foreach ($aliases as $alias) {
1743*04fd306cSNickeau            if ($alias->getPath() === $this->buildAliasPath) {
1744*04fd306cSNickeau                return $alias;
1745*04fd306cSNickeau            }
1746*04fd306cSNickeau        }
1747*04fd306cSNickeau        return null;
1748*04fd306cSNickeau    }
1749*04fd306cSNickeau
1750*04fd306cSNickeau    public
1751*04fd306cSNickeau    function isDynamicQualityMonitored(): bool
1752*04fd306cSNickeau    {
1753*04fd306cSNickeau        if ($this->getQualityMonitoringIndicator() !== null) {
1754*04fd306cSNickeau            return $this->getQualityMonitoringIndicator();
1755*04fd306cSNickeau        }
1756*04fd306cSNickeau        return $this->getDefaultQualityMonitoring();
1757*04fd306cSNickeau    }
1758*04fd306cSNickeau
1759*04fd306cSNickeau    public
1760*04fd306cSNickeau    function getDefaultQualityMonitoring(): bool
1761*04fd306cSNickeau    {
1762*04fd306cSNickeau        if (SiteConfig::getConfValue(QualityMessageHandler::CONF_DISABLE_QUALITY_MONITORING) === 1) {
1763*04fd306cSNickeau            return false;
1764*04fd306cSNickeau        } else {
1765*04fd306cSNickeau            return true;
1766*04fd306cSNickeau        }
1767*04fd306cSNickeau    }
1768*04fd306cSNickeau
1769*04fd306cSNickeau    /**
1770*04fd306cSNickeau     * @param MetadataStore|string $store
1771*04fd306cSNickeau     * @return $this
1772*04fd306cSNickeau     */
1773*04fd306cSNickeau    public
1774*04fd306cSNickeau    function setReadStore($store): MarkupPath
1775*04fd306cSNickeau    {
1776*04fd306cSNickeau        $this->readStore = $store;
1777*04fd306cSNickeau        return $this;
1778*04fd306cSNickeau    }
1779*04fd306cSNickeau
1780*04fd306cSNickeau
1781*04fd306cSNickeau    /**
1782*04fd306cSNickeau     * @param array $usages
1783*04fd306cSNickeau     * @return IFetcherLocalImage[]
1784*04fd306cSNickeau     */
1785*04fd306cSNickeau    public
1786*04fd306cSNickeau    function getImagesForTheFollowingUsages(array $usages): array
1787*04fd306cSNickeau    {
1788*04fd306cSNickeau        $usages = array_merge($usages, [PageImageUsage::ALL]);
1789*04fd306cSNickeau        $images = [];
1790*04fd306cSNickeau        foreach ($this->getPageMetadataImages() as $pageImage) {
1791*04fd306cSNickeau            foreach ($usages as $usage) {
1792*04fd306cSNickeau                if (in_array($usage, $pageImage->getUsages())) {
1793*04fd306cSNickeau                    $path = $pageImage->getImagePath();
1794*04fd306cSNickeau                    try {
1795*04fd306cSNickeau                        $images[] = IFetcherLocalImage::createImageFetchFromPath($path);
1796*04fd306cSNickeau                    } catch (ExceptionBadArgument $e) {
1797*04fd306cSNickeau                        LogUtility::error(`The page image $path of the page $this is not an image`);
1798*04fd306cSNickeau                    } catch (ExceptionBadSyntax $e) {
1799*04fd306cSNickeau                        LogUtility::error(`The page image $path has a bad syntax`);
1800*04fd306cSNickeau                    } catch (ExceptionNotExists $e) {
1801*04fd306cSNickeau                        LogUtility::error(`The page image $path does not exists`);
1802*04fd306cSNickeau                    }
1803*04fd306cSNickeau                    continue 2;
1804*04fd306cSNickeau                }
1805*04fd306cSNickeau            }
1806*04fd306cSNickeau        }
1807*04fd306cSNickeau        return $images;
1808*04fd306cSNickeau
1809*04fd306cSNickeau    }
1810*04fd306cSNickeau
1811*04fd306cSNickeau
1812*04fd306cSNickeau    /**
1813*04fd306cSNickeau     * @throws ExceptionNotFound
1814*04fd306cSNickeau     */
1815*04fd306cSNickeau    public
1816*04fd306cSNickeau    function getKeywords(): array
1817*04fd306cSNickeau    {
1818*04fd306cSNickeau        return $this->keywords->getValue();
1819*04fd306cSNickeau    }
1820*04fd306cSNickeau
1821*04fd306cSNickeau    /**
1822*04fd306cSNickeau     * @throws ExceptionNotFound
1823*04fd306cSNickeau     */
1824*04fd306cSNickeau    public function getKeywordsOrDefault(): array
1825*04fd306cSNickeau    {
1826*04fd306cSNickeau        return $this->keywords->getValueOrDefaults();
1827*04fd306cSNickeau    }
1828*04fd306cSNickeau
1829*04fd306cSNickeau
1830*04fd306cSNickeau    /**
1831*04fd306cSNickeau     * @throws ExceptionCompile
1832*04fd306cSNickeau     */
1833*04fd306cSNickeau    public
1834*04fd306cSNickeau    function setKeywords($value): MarkupPath
1835*04fd306cSNickeau    {
1836*04fd306cSNickeau        $this->keywords
1837*04fd306cSNickeau            ->setFromStoreValue($value)
1838*04fd306cSNickeau            ->sendToWriteStore();
1839*04fd306cSNickeau        return $this;
1840*04fd306cSNickeau    }
1841*04fd306cSNickeau
1842*04fd306cSNickeau    /**
1843*04fd306cSNickeau     * @return DateTime|null
1844*04fd306cSNickeau     * @throws ExceptionNotFound
1845*04fd306cSNickeau     * @deprecated for {@link CacheExpirationDate}
1846*04fd306cSNickeau     */
1847*04fd306cSNickeau    public
1848*04fd306cSNickeau    function getCacheExpirationDate(): ?DateTime
1849*04fd306cSNickeau    {
1850*04fd306cSNickeau        return $this->cacheExpirationDate->getValue();
1851*04fd306cSNickeau    }
1852*04fd306cSNickeau
1853*04fd306cSNickeau    /**
1854*04fd306cSNickeau     * @return DateTime|null
1855*04fd306cSNickeau     * @throws ExceptionNotFound
1856*04fd306cSNickeau     * @deprecated for {@link CacheExpirationDate}
1857*04fd306cSNickeau     */
1858*04fd306cSNickeau    public
1859*04fd306cSNickeau    function getDefaultCacheExpirationDate(): ?DateTime
1860*04fd306cSNickeau    {
1861*04fd306cSNickeau        return $this->cacheExpirationDate->getDefaultValue();
1862*04fd306cSNickeau    }
1863*04fd306cSNickeau
1864*04fd306cSNickeau    /**
1865*04fd306cSNickeau     * @return string|null
1866*04fd306cSNickeau     * @throws ExceptionNotFound
1867*04fd306cSNickeau     * @deprecated for {@link CacheExpirationFrequency}
1868*04fd306cSNickeau     */
1869*04fd306cSNickeau    public
1870*04fd306cSNickeau    function getCacheExpirationFrequency(): string
1871*04fd306cSNickeau    {
1872*04fd306cSNickeau        return $this->cacheExpirationFrequency->getValue();
1873*04fd306cSNickeau    }
1874*04fd306cSNickeau
1875*04fd306cSNickeau
1876*04fd306cSNickeau    /**
1877*04fd306cSNickeau     * @param DateTime $cacheExpirationDate
1878*04fd306cSNickeau     * @return $this
1879*04fd306cSNickeau     * @deprecated for {@link CacheExpirationDate}
1880*04fd306cSNickeau     */
1881*04fd306cSNickeau    public
1882*04fd306cSNickeau    function setCacheExpirationDate(DateTime $cacheExpirationDate): MarkupPath
1883*04fd306cSNickeau    {
1884*04fd306cSNickeau        $this->cacheExpirationDate->setValue($cacheExpirationDate);
1885*04fd306cSNickeau        return $this;
1886*04fd306cSNickeau    }
1887*04fd306cSNickeau
1888*04fd306cSNickeau
1889*04fd306cSNickeau    /**
1890*04fd306cSNickeau     * Utility class
1891*04fd306cSNickeau     * Get the instructions document as if it was the main page.
1892*04fd306cSNickeau     * Ie the context path is:
1893*04fd306cSNickeau     *  * the markup path itself)
1894*04fd306cSNickeau     *  * or the default context path if the path cannot be transformed as wiki path.
1895*04fd306cSNickeau     */
1896*04fd306cSNickeau    public function getInstructionsDocument(): FetcherMarkup
1897*04fd306cSNickeau    {
1898*04fd306cSNickeau
1899*04fd306cSNickeau        $path = $this->getPathObject();
1900*04fd306cSNickeau        try {
1901*04fd306cSNickeau            $contextPath = $path->toWikiPath();
1902*04fd306cSNickeau        } catch (ExceptionCast $e) {
1903*04fd306cSNickeau            $contextPath = ExecutionContext::getActualOrCreateFromEnv()
1904*04fd306cSNickeau                ->getDefaultContextPath();
1905*04fd306cSNickeau        }
1906*04fd306cSNickeau        return FetcherMarkup::confRoot()
1907*04fd306cSNickeau            ->setRequestedExecutingPath($path)
1908*04fd306cSNickeau            ->setRequestedContextPath($contextPath)
1909*04fd306cSNickeau            ->setRequestedMimeToInstructions()
1910*04fd306cSNickeau            ->build();
1911*04fd306cSNickeau
1912*04fd306cSNickeau    }
1913*04fd306cSNickeau
1914*04fd306cSNickeau    public
1915*04fd306cSNickeau    function delete()
1916*04fd306cSNickeau    {
1917*04fd306cSNickeau
1918*04fd306cSNickeau        Index::getOrCreate()->deletePage($this);
1919*04fd306cSNickeau        saveWikiText($this->getWikiId(), "", "Delete");
1920*04fd306cSNickeau
1921*04fd306cSNickeau    }
1922*04fd306cSNickeau
1923*04fd306cSNickeau    /**
1924*04fd306cSNickeau     * @return Url -the absolute canonical url
1925*04fd306cSNickeau     */
1926*04fd306cSNickeau    public
1927*04fd306cSNickeau    function getAbsoluteCanonicalUrl(): Url
1928*04fd306cSNickeau    {
1929*04fd306cSNickeau        return $this->getCanonicalUrl()->toAbsoluteUrl();
1930*04fd306cSNickeau    }
1931*04fd306cSNickeau
1932*04fd306cSNickeau
1933*04fd306cSNickeau    public
1934*04fd306cSNickeau    function getReadStoreOrDefault(): MetadataStore
1935*04fd306cSNickeau    {
1936*04fd306cSNickeau        if ($this->readStore === null) {
1937*04fd306cSNickeau            /**
1938*04fd306cSNickeau             * No cache please if not set
1939*04fd306cSNickeau             * Cache should be in the MetadataDokuWikiStore
1940*04fd306cSNickeau             * that is page requested scoped and not by slot
1941*04fd306cSNickeau             */
1942*04fd306cSNickeau            return MetadataDokuWikiStore::getOrCreateFromResource($this);
1943*04fd306cSNickeau        }
1944*04fd306cSNickeau        if (!($this->readStore instanceof MetadataStore)) {
1945*04fd306cSNickeau            $this->readStore = MetadataStoreAbs::toMetadataStore($this->readStore, $this);
1946*04fd306cSNickeau        }
1947*04fd306cSNickeau        return $this->readStore;
1948*04fd306cSNickeau    }
1949*04fd306cSNickeau
1950*04fd306cSNickeau    /**
1951*04fd306cSNickeau     * @return Path
1952*04fd306cSNickeau     * A markup path wraps a path
1953*04fd306cSNickeau     */
1954*04fd306cSNickeau    public function getPathObject(): Path
1955*04fd306cSNickeau    {
1956*04fd306cSNickeau        return $this->path;
1957*04fd306cSNickeau    }
1958*04fd306cSNickeau
1959*04fd306cSNickeau
1960*04fd306cSNickeau    /**
1961*04fd306cSNickeau     * A shortcut for {@link MarkupPath::getPathObject()::getDokuwikiId()}
1962*04fd306cSNickeau     *
1963*04fd306cSNickeau     * @throws ExceptionBadArgument - if the markup path is not a {@link WikiPath}
1964*04fd306cSNickeau     */
1965*04fd306cSNickeau    public
1966*04fd306cSNickeau    function getWikiId(): string
1967*04fd306cSNickeau    {
1968*04fd306cSNickeau        $path = $this->getPathObject();
1969*04fd306cSNickeau        return WikiPath::createFromPathObject($path)->getWikiId();
1970*04fd306cSNickeau    }
1971*04fd306cSNickeau
1972*04fd306cSNickeau    public
1973*04fd306cSNickeau    function getUid(): Metadata
1974*04fd306cSNickeau    {
1975*04fd306cSNickeau        return PageId::createForPage($this);
1976*04fd306cSNickeau    }
1977*04fd306cSNickeau
1978*04fd306cSNickeau
1979*04fd306cSNickeau    public
1980*04fd306cSNickeau    function getAbsolutePath(): string
1981*04fd306cSNickeau    {
1982*04fd306cSNickeau        return WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT . $this->getWikiId();
1983*04fd306cSNickeau    }
1984*04fd306cSNickeau
1985*04fd306cSNickeau    /**
1986*04fd306cSNickeau     * Todo, it should be a property of the markup not every markup file are main page markup.
1987*04fd306cSNickeau     * @return string
1988*04fd306cSNickeau     */
1989*04fd306cSNickeau    function getType(): string
1990*04fd306cSNickeau    {
1991*04fd306cSNickeau        return self::TYPE;
1992*04fd306cSNickeau    }
1993*04fd306cSNickeau
1994*04fd306cSNickeau    /**
1995*04fd306cSNickeau     * @return PageUrlPath
1996*04fd306cSNickeau     * @deprecated use {@link PageUrlPath} instead
1997*04fd306cSNickeau     */
1998*04fd306cSNickeau    public
1999*04fd306cSNickeau    function getUrlPathObject(): PageUrlPath
2000*04fd306cSNickeau    {
2001*04fd306cSNickeau        return $this->pageUrlPath;
2002*04fd306cSNickeau    }
2003*04fd306cSNickeau
2004*04fd306cSNickeau
2005*04fd306cSNickeau    public function getSideSlot(): ?MarkupPath
2006*04fd306cSNickeau    {
2007*04fd306cSNickeau
2008*04fd306cSNickeau        /**
2009*04fd306cSNickeau         * Only primary slot have a side slot
2010*04fd306cSNickeau         * Root Home page does not have one either
2011*04fd306cSNickeau         */
2012*04fd306cSNickeau        if ($this->isSlot()) {
2013*04fd306cSNickeau            return null;
2014*04fd306cSNickeau        }
2015*04fd306cSNickeau
2016*04fd306cSNickeau        $nearestMainFooter = $this->findNearest(SlotSystem::getSidebarName());
2017*04fd306cSNickeau        if ($nearestMainFooter === false) {
2018*04fd306cSNickeau            return null;
2019*04fd306cSNickeau        }
2020*04fd306cSNickeau        return MarkupPath::createMarkupFromId($nearestMainFooter);
2021*04fd306cSNickeau
2022*04fd306cSNickeau
2023*04fd306cSNickeau    }
2024*04fd306cSNickeau
2025*04fd306cSNickeau    /**
2026*04fd306cSNickeau     * @param $pageName
2027*04fd306cSNickeau     * @return false|string
2028*04fd306cSNickeau     */
2029*04fd306cSNickeau    private function findNearest($pageName)
2030*04fd306cSNickeau    {
2031*04fd306cSNickeau        global $ID;
2032*04fd306cSNickeau        $keep = $ID;
2033*04fd306cSNickeau        try {
2034*04fd306cSNickeau            $ID = $this->getWikiId();
2035*04fd306cSNickeau            return page_findnearest($pageName);
2036*04fd306cSNickeau        } finally {
2037*04fd306cSNickeau            $ID = $keep;
2038*04fd306cSNickeau        }
2039*04fd306cSNickeau
2040*04fd306cSNickeau    }
2041*04fd306cSNickeau
2042*04fd306cSNickeau    /**
2043*04fd306cSNickeau     * The slots that are independent from the primary slot
2044*04fd306cSNickeau     *
2045*04fd306cSNickeau     * @return MarkupPath[]
2046*04fd306cSNickeau     * @deprecated should be {@link TemplateForWebPage} based
2047*04fd306cSNickeau     */
2048*04fd306cSNickeau    public function getPrimaryIndependentSlots(): array
2049*04fd306cSNickeau    {
2050*04fd306cSNickeau        $secondarySlots = [];
2051*04fd306cSNickeau        $sideSlot = $this->getSideSlot();
2052*04fd306cSNickeau        if ($sideSlot !== null) {
2053*04fd306cSNickeau            $secondarySlots[] = $sideSlot;
2054*04fd306cSNickeau        }
2055*04fd306cSNickeau        return $secondarySlots;
2056*04fd306cSNickeau    }
2057*04fd306cSNickeau
2058*04fd306cSNickeau
2059*04fd306cSNickeau    public function isHidden(): bool
2060*04fd306cSNickeau    {
2061*04fd306cSNickeau        return isHiddenPage($this->getWikiId());
2062*04fd306cSNickeau    }
2063*04fd306cSNickeau
2064*04fd306cSNickeau
2065*04fd306cSNickeau    public function getPrimaryHeaderPage(): ?MarkupPath
2066*04fd306cSNickeau    {
2067*04fd306cSNickeau        $nearest = page_findnearest(SlotSystem::getMainHeaderSlotName());
2068*04fd306cSNickeau        if ($nearest === false) {
2069*04fd306cSNickeau            return null;
2070*04fd306cSNickeau        }
2071*04fd306cSNickeau        return MarkupPath::createMarkupFromId($nearest);
2072*04fd306cSNickeau    }
2073*04fd306cSNickeau
2074*04fd306cSNickeau    public function createPageFetcherHtml(): FetcherPage
2075*04fd306cSNickeau    {
2076*04fd306cSNickeau        return FetcherPage::createPageFetcherFromMarkupPath($this);
2077*04fd306cSNickeau    }
2078*04fd306cSNickeau
2079*04fd306cSNickeau    public function getHttpResponse(): HttpResponse
2080*04fd306cSNickeau    {
2081*04fd306cSNickeau        return HttpRequest::fetchXhtmlPageResponse($this->getWikiId());
2082*04fd306cSNickeau    }
2083*04fd306cSNickeau
2084*04fd306cSNickeau    /**
2085*04fd306cSNickeau     * @return Outline
2086*04fd306cSNickeau     * @deprecated uses {@link FetcherMarkup::getOutline()} instead
2087*04fd306cSNickeau     */
2088*04fd306cSNickeau    public function getOutline(): Outline
2089*04fd306cSNickeau    {
2090*04fd306cSNickeau
2091*04fd306cSNickeau        return $this->getInstructionsDocument()->getOutline();
2092*04fd306cSNickeau
2093*04fd306cSNickeau    }
2094*04fd306cSNickeau
2095*04fd306cSNickeau
2096*04fd306cSNickeau    public function persistToDefaultMetaStore(): MarkupPath
2097*04fd306cSNickeau    {
2098*04fd306cSNickeau        $this->getReadStoreOrDefault()->persist();
2099*04fd306cSNickeau        return $this;
2100*04fd306cSNickeau    }
2101*04fd306cSNickeau
2102*04fd306cSNickeau    public function getInstructionsPath(): LocalPath
2103*04fd306cSNickeau    {
2104*04fd306cSNickeau
2105*04fd306cSNickeau        $instructionsDocument = $this->getInstructionsDocument();
2106*04fd306cSNickeau        return $instructionsDocument->getInstructionsPath();
2107*04fd306cSNickeau
2108*04fd306cSNickeau    }
2109*04fd306cSNickeau
2110*04fd306cSNickeau    public function setContent(string $textContent): MarkupPath
2111*04fd306cSNickeau    {
2112*04fd306cSNickeau        FileSystems::setContent($this, $textContent);
2113*04fd306cSNickeau        return $this;
2114*04fd306cSNickeau    }
2115*04fd306cSNickeau
2116*04fd306cSNickeau    /**
2117*04fd306cSNickeau     * @throws ExceptionNotExists - if the path does not exist
2118*04fd306cSNickeau     */
2119*04fd306cSNickeau    public function createHtmlFetcherWithRequestedPathAsContextPath(): FetcherMarkup
2120*04fd306cSNickeau    {
2121*04fd306cSNickeau        $executionContext = ExecutionContext::getActualOrCreateFromEnv();
2122*04fd306cSNickeau        $executingPath = $this->getPathObject();
2123*04fd306cSNickeau        $requestedPath = $executionContext->getRequestedPath();
2124*04fd306cSNickeau        $requestedMarkupPath = MarkupPath::createPageFromPathObject($requestedPath);
2125*04fd306cSNickeau
2126*04fd306cSNickeau        if ($requestedMarkupPath->isSlot()) {
2127*04fd306cSNickeau            try {
2128*04fd306cSNickeau                $markupContextPath = SlotSystem::getContextPath();
2129*04fd306cSNickeau                SlotSystem::sendContextPathMessage($markupContextPath);
2130*04fd306cSNickeau                $requestedPath = $markupContextPath->toWikiPath();
2131*04fd306cSNickeau            } catch (\Exception $e) {
2132*04fd306cSNickeau                // should not
2133*04fd306cSNickeau            }
2134*04fd306cSNickeau        }
2135*04fd306cSNickeau        return FetcherMarkup::confRoot()
2136*04fd306cSNickeau            ->setRequestedMimeToXhtml()
2137*04fd306cSNickeau            ->setRequestedContextPath($requestedPath)
2138*04fd306cSNickeau            ->setRequestedExecutingPath($executingPath)
2139*04fd306cSNickeau            ->build();
2140*04fd306cSNickeau    }
2141*04fd306cSNickeau
2142*04fd306cSNickeau    public
2143*04fd306cSNickeau    function isRootItemPage(): bool
2144*04fd306cSNickeau    {
2145*04fd306cSNickeau        try {
2146*04fd306cSNickeau            if ($this->isIndexPage()) {
2147*04fd306cSNickeau                return false;
2148*04fd306cSNickeau            }
2149*04fd306cSNickeau            $parent = $this->getParent();
2150*04fd306cSNickeau            if ($parent->isRootHomePage()) {
2151*04fd306cSNickeau                return true;
2152*04fd306cSNickeau            }
2153*04fd306cSNickeau            return false;
2154*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
2155*04fd306cSNickeau            return false;
2156*04fd306cSNickeau        }
2157*04fd306cSNickeau    }
2158*04fd306cSNickeau
2159*04fd306cSNickeau    private
2160*04fd306cSNickeau    function getPrimaryFooterPage(): ?MarkupPath
2161*04fd306cSNickeau    {
2162*04fd306cSNickeau        $nearest = page_findnearest(SlotSystem::getMainFooterSlotName());
2163*04fd306cSNickeau        if ($nearest === false) {
2164*04fd306cSNickeau            return null;
2165*04fd306cSNickeau        }
2166*04fd306cSNickeau        return MarkupPath::createMarkupFromId($nearest);
2167*04fd306cSNickeau    }
2168*04fd306cSNickeau
2169*04fd306cSNickeau    /**
2170*04fd306cSNickeau     * Set the page path to an index page for a directory path
2171*04fd306cSNickeau     * @return void
2172*04fd306cSNickeau     */
2173*04fd306cSNickeau    private
2174*04fd306cSNickeau    function setCorrectPathForDirectoryToIndexPage(): void
2175*04fd306cSNickeau    {
2176*04fd306cSNickeau
2177*04fd306cSNickeau
2178*04fd306cSNickeau        if (!($this->path instanceof WikiPath)) {
2179*04fd306cSNickeau            return;
2180*04fd306cSNickeau        }
2181*04fd306cSNickeau        /**
2182*04fd306cSNickeau         * @var $path WikiPath
2183*04fd306cSNickeau         */
2184*04fd306cSNickeau        $path = $this->path;
2185*04fd306cSNickeau
2186*04fd306cSNickeau        /**
2187*04fd306cSNickeau         * We correct the path
2188*04fd306cSNickeau         * We don't return a page because it does not work in a constructor
2189*04fd306cSNickeau         */
2190*04fd306cSNickeau        $startPageName = Site::getIndexPageName();
2191*04fd306cSNickeau        $indexPath = $path->resolveId($startPageName);
2192*04fd306cSNickeau        if (FileSystems::exists($indexPath)) {
2193*04fd306cSNickeau            // start page inside namespace
2194*04fd306cSNickeau            $this->path = $indexPath;
2195*04fd306cSNickeau            return;
2196*04fd306cSNickeau        }
2197*04fd306cSNickeau
2198*04fd306cSNickeau        // page named like the NS inside the NS
2199*04fd306cSNickeau        try {
2200*04fd306cSNickeau            $parentName = $this->getLastNameWithoutExtension();
2201*04fd306cSNickeau            $nsInsideNsIndex = $this->path->resolveId($parentName);
2202*04fd306cSNickeau            if (FileSystems::exists($nsInsideNsIndex)) {
2203*04fd306cSNickeau                $this->path = $nsInsideNsIndex;
2204*04fd306cSNickeau                return;
2205*04fd306cSNickeau            }
2206*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
2207*04fd306cSNickeau            // no last name
2208*04fd306cSNickeau        }
2209*04fd306cSNickeau
2210*04fd306cSNickeau        // We don't support the child page
2211*04fd306cSNickeau        // Does not exist but can be used by hierarchical function
2212*04fd306cSNickeau        $this->path = $indexPath;
2213*04fd306cSNickeau    }
2214*04fd306cSNickeau
2215*04fd306cSNickeau
2216*04fd306cSNickeau    public
2217*04fd306cSNickeau    function getUidObject(): Metadata
2218*04fd306cSNickeau    {
2219*04fd306cSNickeau        if ($this->uidObject === null) {
2220*04fd306cSNickeau            try {
2221*04fd306cSNickeau                $this->uidObject = Meta\Api\MetadataSystem::toMetadataObject($this->getUid())
2222*04fd306cSNickeau                    ->setResource($this);
2223*04fd306cSNickeau            } catch (ExceptionBadArgument $e) {
2224*04fd306cSNickeau                throw new ExceptionRuntimeInternal("Uid object is a metadata object. It should not happen.", self::CANONICAL_PAGE, 1, $e);
2225*04fd306cSNickeau            }
2226*04fd306cSNickeau        }
2227*04fd306cSNickeau
2228*04fd306cSNickeau        return $this->uidObject;
2229*04fd306cSNickeau    }
2230*04fd306cSNickeau
2231*04fd306cSNickeau    function getExtension(): string
2232*04fd306cSNickeau    {
2233*04fd306cSNickeau        return $this->path->getExtension();
2234*04fd306cSNickeau    }
2235*04fd306cSNickeau
2236*04fd306cSNickeau    function getLastNameWithoutExtension(): string
2237*04fd306cSNickeau    {
2238*04fd306cSNickeau        return $this->path->getLastNameWithoutExtension();
2239*04fd306cSNickeau    }
2240*04fd306cSNickeau
2241*04fd306cSNickeau    function getScheme(): string
2242*04fd306cSNickeau    {
2243*04fd306cSNickeau        return MarkupFileSystem::SCHEME;
2244*04fd306cSNickeau    }
2245*04fd306cSNickeau
2246*04fd306cSNickeau    function getLastName(): string
2247*04fd306cSNickeau    {
2248*04fd306cSNickeau        return $this->path->getLastName();
2249*04fd306cSNickeau    }
2250*04fd306cSNickeau
2251*04fd306cSNickeau    function getNames()
2252*04fd306cSNickeau    {
2253*04fd306cSNickeau        return $this->path->getNames();
2254*04fd306cSNickeau    }
2255*04fd306cSNickeau
2256*04fd306cSNickeau    function toAbsoluteId(): string
2257*04fd306cSNickeau    {
2258*04fd306cSNickeau        return $this->path->toAbsoluteId();
2259*04fd306cSNickeau    }
2260*04fd306cSNickeau
2261*04fd306cSNickeau    function toUriString(): string
2262*04fd306cSNickeau    {
2263*04fd306cSNickeau        return $this->path->toUriString();
2264*04fd306cSNickeau    }
2265*04fd306cSNickeau
2266*04fd306cSNickeau    function toAbsolutePath(): Path
2267*04fd306cSNickeau    {
2268*04fd306cSNickeau        return $this->path->toAbsolutePath();
2269*04fd306cSNickeau    }
2270*04fd306cSNickeau
2271*04fd306cSNickeau
2272*04fd306cSNickeau    function resolve(string $name): Path
2273*04fd306cSNickeau    {
2274*04fd306cSNickeau        return $this->path->resolve($name);
2275*04fd306cSNickeau    }
2276*04fd306cSNickeau
2277*04fd306cSNickeau
2278*04fd306cSNickeau    function getUrl(): Url
2279*04fd306cSNickeau    {
2280*04fd306cSNickeau        return FetcherPage::createPageFetcherFromMarkupPath($this)
2281*04fd306cSNickeau            ->getFetchUrl();
2282*04fd306cSNickeau    }
2283*04fd306cSNickeau
2284*04fd306cSNickeau    function getHost(): string
2285*04fd306cSNickeau    {
2286*04fd306cSNickeau        return $this->path->getHost();
2287*04fd306cSNickeau    }
2288*04fd306cSNickeau
2289*04fd306cSNickeau    public
2290*04fd306cSNickeau    function __toString(): string
2291*04fd306cSNickeau    {
2292*04fd306cSNickeau        return $this->path->__toString();
2293*04fd306cSNickeau    }
2294*04fd306cSNickeau
2295*04fd306cSNickeau    /**
2296*04fd306cSNickeau     * @throws ExceptionBadSyntax
2297*04fd306cSNickeau     * @throws ExceptionBadArgument
2298*04fd306cSNickeau     */
2299*04fd306cSNickeau    public
2300*04fd306cSNickeau    static function createFromUri(string $uri): MarkupPath
2301*04fd306cSNickeau    {
2302*04fd306cSNickeau        $path = FileSystems::createPathFromUri($uri);
2303*04fd306cSNickeau        return new MarkupPath($path);
2304*04fd306cSNickeau    }
2305*04fd306cSNickeau
2306*04fd306cSNickeau
2307*04fd306cSNickeau}
2308