xref: /plugin/combo/ComboStrap/Meta/Api/Metadata.php (revision 04fd306c7c155fa133ebb3669986875d65988276)
1*04fd306cSNickeau<?php
2*04fd306cSNickeau
3*04fd306cSNickeau
4*04fd306cSNickeaunamespace ComboStrap\Meta\Api;
5*04fd306cSNickeau
6*04fd306cSNickeau
7*04fd306cSNickeauuse action_plugin_combo_metadescription;
8*04fd306cSNickeauuse ComboStrap\CacheExpirationDate;
9*04fd306cSNickeauuse ComboStrap\CacheExpirationFrequency;
10*04fd306cSNickeauuse ComboStrap\Canonical;
11*04fd306cSNickeauuse ComboStrap\DataType;
12*04fd306cSNickeauuse ComboStrap\ExceptionRuntimeInternal;
13*04fd306cSNickeauuse ComboStrap\ExecutionContext;
14*04fd306cSNickeauuse ComboStrap\Label;
15*04fd306cSNickeauuse ComboStrap\DisqusIdentifier;
16*04fd306cSNickeauuse ComboStrap\DokuwikiId;
17*04fd306cSNickeauuse ComboStrap\EndDate;
18*04fd306cSNickeauuse ComboStrap\ExceptionBadArgument;
19*04fd306cSNickeauuse ComboStrap\ExceptionCompile;
20*04fd306cSNickeauuse ComboStrap\ExceptionNotFound;
21*04fd306cSNickeauuse ComboStrap\ExceptionRuntime;
22*04fd306cSNickeauuse ComboStrap\FirstImage;
23*04fd306cSNickeauuse ComboStrap\FirstRasterImage;
24*04fd306cSNickeauuse ComboStrap\FirstSvgIllustration;
25*04fd306cSNickeauuse ComboStrap\FeaturedIcon;
26*04fd306cSNickeauuse ComboStrap\Lang;
27*04fd306cSNickeauuse ComboStrap\LdJson;
28*04fd306cSNickeauuse ComboStrap\Lead;
29*04fd306cSNickeauuse ComboStrap\Locale;
30*04fd306cSNickeauuse ComboStrap\LogUtility;
31*04fd306cSNickeauuse ComboStrap\LowQualityCalculatedIndicator;
32*04fd306cSNickeauuse ComboStrap\LowQualityPageOverwrite;
33*04fd306cSNickeauuse ComboStrap\Meta\Field\Aliases;
34*04fd306cSNickeauuse ComboStrap\Meta\Field\AliasPath;
35*04fd306cSNickeauuse ComboStrap\Meta\Field\AliasType;
36*04fd306cSNickeauuse ComboStrap\Meta\Field\AncestorImage;
37*04fd306cSNickeauuse ComboStrap\Meta\Field\FacebookImage;
38*04fd306cSNickeauuse ComboStrap\Meta\Field\FeaturedImage;
39*04fd306cSNickeauuse ComboStrap\Meta\Field\SocialCardImage;
40*04fd306cSNickeauuse ComboStrap\Meta\Field\FeaturedRasterImage;
41*04fd306cSNickeauuse ComboStrap\Meta\Field\FeaturedSvgImage;
42*04fd306cSNickeauuse ComboStrap\Meta\Field\PageH1;
43*04fd306cSNickeauuse ComboStrap\Meta\Field\PageTemplateName;
44*04fd306cSNickeauuse ComboStrap\Meta\Field\TwitterImage;
45*04fd306cSNickeauuse ComboStrap\Meta\Store\MetadataDokuWikiStore;
46*04fd306cSNickeauuse ComboStrap\MetadataMutation;
47*04fd306cSNickeauuse ComboStrap\ModificationDate;
48*04fd306cSNickeauuse ComboStrap\CreationDate;
49*04fd306cSNickeauuse ComboStrap\PageDescription;
50*04fd306cSNickeauuse ComboStrap\PageId;
51*04fd306cSNickeauuse ComboStrap\Meta\Field\PageImagePath;
52*04fd306cSNickeauuse ComboStrap\Meta\Field\PageImages;
53*04fd306cSNickeauuse ComboStrap\PageImageUsage;
54*04fd306cSNickeauuse ComboStrap\PageKeywords;
55*04fd306cSNickeauuse ComboStrap\PageLevel;
56*04fd306cSNickeauuse ComboStrap\PagePath;
57*04fd306cSNickeauuse ComboStrap\PagePublicationDate;
58*04fd306cSNickeauuse ComboStrap\PageTitle;
59*04fd306cSNickeauuse ComboStrap\PageType;
60*04fd306cSNickeauuse ComboStrap\PageUrlPath;
61*04fd306cSNickeauuse ComboStrap\PluginUtility;
62*04fd306cSNickeauuse ComboStrap\QualityDynamicMonitoringOverwrite;
63*04fd306cSNickeauuse ComboStrap\References;
64*04fd306cSNickeauuse ComboStrap\Meta\Field\Region;
65*04fd306cSNickeauuse ComboStrap\ReplicationDate;
66*04fd306cSNickeauuse ComboStrap\ResourceCombo;
67*04fd306cSNickeauuse ComboStrap\ResourceName;
68*04fd306cSNickeauuse ComboStrap\Slug;
69*04fd306cSNickeauuse ComboStrap\StartDate;
70*04fd306cSNickeauuse ComboStrap\Web\UrlEndpoint;
71*04fd306cSNickeau
72*04fd306cSNickeauabstract class Metadata
73*04fd306cSNickeau{
74*04fd306cSNickeau    const CANONICAL = "metadata";
75*04fd306cSNickeau
76*04fd306cSNickeau    public const NOT_MODIFIABLE_METAS = [
77*04fd306cSNickeau        "date",
78*04fd306cSNickeau        "user",
79*04fd306cSNickeau        "last_change",
80*04fd306cSNickeau        "creator",
81*04fd306cSNickeau        "contributor"
82*04fd306cSNickeau    ];
83*04fd306cSNickeau
84*04fd306cSNickeau
85*04fd306cSNickeau    /**
86*04fd306cSNickeau     * @var bool
87*04fd306cSNickeau     */
88*04fd306cSNickeau    protected bool $wasBuild = false;
89*04fd306cSNickeau
90*04fd306cSNickeau    /**
91*04fd306cSNickeau     * The metadata is for this resource
92*04fd306cSNickeau     * @var ResourceCombo $resource
93*04fd306cSNickeau     */
94*04fd306cSNickeau    private $resource;
95*04fd306cSNickeau
96*04fd306cSNickeau    /**
97*04fd306cSNickeau     * @var MetadataStore|string - string is the class
98*04fd306cSNickeau     */
99*04fd306cSNickeau    private $readStore;
100*04fd306cSNickeau
101*04fd306cSNickeau    /**
102*04fd306cSNickeau     * @var MetadataStore|string
103*04fd306cSNickeau     */
104*04fd306cSNickeau    private $writeStore;
105*04fd306cSNickeau    /**
106*04fd306cSNickeau     * @var Metadata
107*04fd306cSNickeau     */
108*04fd306cSNickeau    private $uidObject;
109*04fd306cSNickeau
110*04fd306cSNickeau    /**
111*04fd306cSNickeau     * @var Metadata[] - the runtime children
112*04fd306cSNickeau     */
113*04fd306cSNickeau    private array $childrenObject;
114*04fd306cSNickeau
115*04fd306cSNickeau    /**
116*04fd306cSNickeau     * @var Metadata|null - the parent with runtime metadata
117*04fd306cSNickeau     */
118*04fd306cSNickeau    private ?Metadata $parent;
119*04fd306cSNickeau
120*04fd306cSNickeau
121*04fd306cSNickeau    /**
122*04fd306cSNickeau     * The metadata may be just not stored
123*04fd306cSNickeau     * The page is just the scope
124*04fd306cSNickeau     */
125*04fd306cSNickeau    public function __construct(Metadata $parent = null)
126*04fd306cSNickeau    {
127*04fd306cSNickeau        $this->parent = $parent;
128*04fd306cSNickeau    }
129*04fd306cSNickeau
130*04fd306cSNickeau    /**
131*04fd306cSNickeau     * @throws ExceptionNotFound
132*04fd306cSNickeau     */
133*04fd306cSNickeau    public function getParent(): Metadata
134*04fd306cSNickeau    {
135*04fd306cSNickeau        if ($this->parent === null) {
136*04fd306cSNickeau            throw new ExceptionNotFound("No parent");
137*04fd306cSNickeau        }
138*04fd306cSNickeau        return $this->parent;
139*04fd306cSNickeau    }
140*04fd306cSNickeau
141*04fd306cSNickeau    /**
142*04fd306cSNickeau     * The class string of the child/columns metadata
143*04fd306cSNickeau     * @return string[];
144*04fd306cSNickeau     */
145*04fd306cSNickeau    public static function getChildrenClass(): array
146*04fd306cSNickeau    {
147*04fd306cSNickeau        return [];
148*04fd306cSNickeau    }
149*04fd306cSNickeau
150*04fd306cSNickeau
151*04fd306cSNickeau    /**
152*04fd306cSNickeau     * @return array|mixed|string|null
153*04fd306cSNickeau     * Store value may returns null as they may be stored
154*04fd306cSNickeau     * Be careful
155*04fd306cSNickeau     */
156*04fd306cSNickeau    public
157*04fd306cSNickeau    function toStoreValueOrDefault()
158*04fd306cSNickeau    {
159*04fd306cSNickeau
160*04fd306cSNickeau        $value = $this->toStoreValue();
161*04fd306cSNickeau        if ($value !== null) {
162*04fd306cSNickeau            return $value;
163*04fd306cSNickeau        }
164*04fd306cSNickeau        return $this->toStoreDefaultValue();
165*04fd306cSNickeau
166*04fd306cSNickeau    }
167*04fd306cSNickeau
168*04fd306cSNickeau    /**
169*04fd306cSNickeau     * @return Metadata[]
170*04fd306cSNickeau     */
171*04fd306cSNickeau    public function getChildrenObject(): array
172*04fd306cSNickeau    {
173*04fd306cSNickeau        if (static::getChildrenClass() === []) {
174*04fd306cSNickeau            return [];
175*04fd306cSNickeau        }
176*04fd306cSNickeau        if (isset($this->childrenObject)) {
177*04fd306cSNickeau            return $this->childrenObject;
178*04fd306cSNickeau        }
179*04fd306cSNickeau        foreach (static::getChildrenClass() as $childrenClass) {
180*04fd306cSNickeau            try {
181*04fd306cSNickeau                $this->childrenObject[] = MetadataSystem::toMetadataObject($childrenClass)
182*04fd306cSNickeau                    ->setResource($this->getResource());
183*04fd306cSNickeau            } catch (ExceptionCompile $e) {
184*04fd306cSNickeau                LogUtility::msg("Unable to build the metadata children object: " . $e->getMessage());
185*04fd306cSNickeau            }
186*04fd306cSNickeau        }
187*04fd306cSNickeau        return $this->childrenObject;
188*04fd306cSNickeau
189*04fd306cSNickeau    }
190*04fd306cSNickeau
191*04fd306cSNickeau    /**
192*04fd306cSNickeau     * @return bool - true if single value, false if an array
193*04fd306cSNickeau     */
194*04fd306cSNickeau    public
195*04fd306cSNickeau    function isScalar(): bool
196*04fd306cSNickeau    {
197*04fd306cSNickeau
198*04fd306cSNickeau        try {
199*04fd306cSNickeau            $parent = $this->getParent();
200*04fd306cSNickeau            if ($parent::getDataType() === DataType::TABULAR_TYPE_VALUE) {
201*04fd306cSNickeau                return false;
202*04fd306cSNickeau            }
203*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
204*04fd306cSNickeau            // no parent
205*04fd306cSNickeau        }
206*04fd306cSNickeau        return true;
207*04fd306cSNickeau
208*04fd306cSNickeau    }
209*04fd306cSNickeau
210*04fd306cSNickeau
211*04fd306cSNickeau
212*04fd306cSNickeau
213*04fd306cSNickeau    /**
214*04fd306cSNickeau     * @param $store
215*04fd306cSNickeau     * @return $this
216*04fd306cSNickeau     */
217*04fd306cSNickeau    public
218*04fd306cSNickeau    function setReadStore($store): Metadata
219*04fd306cSNickeau    {
220*04fd306cSNickeau        if (isset($this->readStore)) {
221*04fd306cSNickeau            LogUtility::msg("The read store was already set.");
222*04fd306cSNickeau        }
223*04fd306cSNickeau        if (is_string($store) && !is_subclass_of($store, MetadataStore::class)) {
224*04fd306cSNickeau            throw new ExceptionRuntime("The store class ($store) is not a metadata store class");
225*04fd306cSNickeau        }
226*04fd306cSNickeau        $this->readStore = $store;
227*04fd306cSNickeau        return $this;
228*04fd306cSNickeau    }
229*04fd306cSNickeau
230*04fd306cSNickeau    /**
231*04fd306cSNickeau     * @param MetadataStore|string $store
232*04fd306cSNickeau     * @return $this
233*04fd306cSNickeau     */
234*04fd306cSNickeau    public
235*04fd306cSNickeau    function setWriteStore($store): Metadata
236*04fd306cSNickeau    {
237*04fd306cSNickeau        $this->writeStore = $store;
238*04fd306cSNickeau        return $this;
239*04fd306cSNickeau    }
240*04fd306cSNickeau
241*04fd306cSNickeau    /**
242*04fd306cSNickeau     * @param mixed $value
243*04fd306cSNickeau     * @return Metadata
244*04fd306cSNickeau     */
245*04fd306cSNickeau    public
246*04fd306cSNickeau
247*04fd306cSNickeau    abstract function setValue($value): Metadata;
248*04fd306cSNickeau
249*04fd306cSNickeau    /**
250*04fd306cSNickeau     * @return bool
251*04fd306cSNickeau     * Used in the {@link Metadata::buildCheck()} function
252*04fd306cSNickeau     * If the value is null, the {@link Metadata::buildFromReadStore()} will be performed
253*04fd306cSNickeau     * otherwise, it will not.
254*04fd306cSNickeau     * Why ? because the value may have been set before building.
255*04fd306cSNickeau     */
256*04fd306cSNickeau    public abstract function valueIsNotNull(): bool;
257*04fd306cSNickeau
258*04fd306cSNickeau    /**
259*04fd306cSNickeau     * If the {@link Metadata::getValue()} is null and if the object was not already build
260*04fd306cSNickeau     * this function will call the function {@link Metadata::buildFromReadStore()}
261*04fd306cSNickeau     */
262*04fd306cSNickeau    protected function buildCheck()
263*04fd306cSNickeau    {
264*04fd306cSNickeau        if (!$this->wasBuild && !$this->valueIsNotNull()) {
265*04fd306cSNickeau            $this->wasBuild = true;
266*04fd306cSNickeau            $this->buildFromReadStore();
267*04fd306cSNickeau        }
268*04fd306cSNickeau    }
269*04fd306cSNickeau
270*04fd306cSNickeau    /**
271*04fd306cSNickeau     * Return the store for this metadata
272*04fd306cSNickeau     * By default, this is the {@link ResourceCombo::getReadStoreOrDefault() default resource metadata store}
273*04fd306cSNickeau     *
274*04fd306cSNickeau     * @return MetadataStore
275*04fd306cSNickeau     */
276*04fd306cSNickeau    public function getReadStore(): ?MetadataStore
277*04fd306cSNickeau    {
278*04fd306cSNickeau        if (!isset($this->readStore)) {
279*04fd306cSNickeau            return $this->getResource()->getReadStoreOrDefault();
280*04fd306cSNickeau        }
281*04fd306cSNickeau        if (!$this->readStore instanceof MetadataStore) {
282*04fd306cSNickeau            $this->readStore = MetadataStoreAbs::toMetadataStore($this->readStore, $this->getResource());
283*04fd306cSNickeau        }
284*04fd306cSNickeau        return $this->readStore;
285*04fd306cSNickeau    }
286*04fd306cSNickeau
287*04fd306cSNickeau    public static function getTab(): ?string
288*04fd306cSNickeau    {
289*04fd306cSNickeau        return static::getName();
290*04fd306cSNickeau    }
291*04fd306cSNickeau
292*04fd306cSNickeau    /**
293*04fd306cSNickeau     * This function sends the object value to the memory {@link Metadata::getWriteStore() store}
294*04fd306cSNickeau     *
295*04fd306cSNickeau     * This function should be used at the end of each setter/adder function
296*04fd306cSNickeau     *
297*04fd306cSNickeau     * To persist or commit on disk, you use the {@link MetadataStore::persist()}
298*04fd306cSNickeau     * Because the metadata is stored by resource, the persist function is
299*04fd306cSNickeau     * also made available on the resource level
300*04fd306cSNickeau     *
301*04fd306cSNickeau     * @throws ExceptionBadArgument - if the value cannot be persisted
302*04fd306cSNickeau     */
303*04fd306cSNickeau    public function sendToWriteStore(): Metadata
304*04fd306cSNickeau    {
305*04fd306cSNickeau        $this->getWriteStore()->set($this);
306*04fd306cSNickeau        return $this;
307*04fd306cSNickeau    }
308*04fd306cSNickeau
309*04fd306cSNickeau    /**
310*04fd306cSNickeau     * @return string - the name to lookup the value
311*04fd306cSNickeau     * This is the column name in a database or the property name in a key value store
312*04fd306cSNickeau     * It should be unique over all metadata
313*04fd306cSNickeau     */
314*04fd306cSNickeau    public function __toString()
315*04fd306cSNickeau    {
316*04fd306cSNickeau        return $this->getName();
317*04fd306cSNickeau    }
318*04fd306cSNickeau
319*04fd306cSNickeau    /** @noinspection PhpMissingReturnTypeInspection */
320*04fd306cSNickeau    public function buildFromReadStore()
321*04fd306cSNickeau    {
322*04fd306cSNickeau        $this->wasBuild = true;
323*04fd306cSNickeau        $metadataStore = $this->getReadStore();
324*04fd306cSNickeau        if ($metadataStore === null) {
325*04fd306cSNickeau            LogUtility::msg("The metadata store is unknown. You need to define a resource or a store to build from it");
326*04fd306cSNickeau            return $this;
327*04fd306cSNickeau        }
328*04fd306cSNickeau        $value = $metadataStore->get($this);
329*04fd306cSNickeau        $this->setFromStoreValueWithoutException($value);
330*04fd306cSNickeau        return $this;
331*04fd306cSNickeau    }
332*04fd306cSNickeau
333*04fd306cSNickeau
334*04fd306cSNickeau    /**
335*04fd306cSNickeau     * @return string - the data type
336*04fd306cSNickeau     * used:
337*04fd306cSNickeau     *   * to store the data in the database
338*04fd306cSNickeau     *   * to select the type of input in a HTML form
339*04fd306cSNickeau     */
340*04fd306cSNickeau    public static abstract function getDataType(): string;
341*04fd306cSNickeau
342*04fd306cSNickeau    /**
343*04fd306cSNickeau     * @return string - the description (used in tooltip)
344*04fd306cSNickeau     */
345*04fd306cSNickeau    public static abstract function getDescription(): string;
346*04fd306cSNickeau
347*04fd306cSNickeau    /**
348*04fd306cSNickeau     * @return string - the label used in a form or log
349*04fd306cSNickeau     */
350*04fd306cSNickeau    public static abstract function getLabel(): string;
351*04fd306cSNickeau
352*04fd306cSNickeau    /**
353*04fd306cSNickeau     * @return string - the short link name
354*04fd306cSNickeau     */
355*04fd306cSNickeau    public static function getCanonical(): string
356*04fd306cSNickeau    {
357*04fd306cSNickeau        return static::getName();
358*04fd306cSNickeau    }
359*04fd306cSNickeau
360*04fd306cSNickeau    /**
361*04fd306cSNickeau     * @return ResourceCombo - The resource
362*04fd306cSNickeau     */
363*04fd306cSNickeau    public function getResource(): ?ResourceCombo
364*04fd306cSNickeau    {
365*04fd306cSNickeau        if ($this->resource !== null) {
366*04fd306cSNickeau            return $this->resource;
367*04fd306cSNickeau        }
368*04fd306cSNickeau        if ($this->parent !== null) {
369*04fd306cSNickeau            return $this->parent->getResource();
370*04fd306cSNickeau        }
371*04fd306cSNickeau        return null;
372*04fd306cSNickeau    }
373*04fd306cSNickeau
374*04fd306cSNickeau    /**
375*04fd306cSNickeau     * For which resources is the metadata for
376*04fd306cSNickeau     * @param ResourceCombo $resource
377*04fd306cSNickeau     * @return $this
378*04fd306cSNickeau     */
379*04fd306cSNickeau    public function setResource(ResourceCombo $resource): Metadata
380*04fd306cSNickeau    {
381*04fd306cSNickeau        $this->resource = $resource;
382*04fd306cSNickeau        return $this;
383*04fd306cSNickeau    }
384*04fd306cSNickeau
385*04fd306cSNickeau    /**
386*04fd306cSNickeau     * @return string - the storage name use in the store
387*04fd306cSNickeau     *
388*04fd306cSNickeau     * For instance, a {@link PageImagePath} has a unique name of `page-image-path`
389*04fd306cSNickeau     * but when we store it hierarchically, the prefix `page-image` is not needed
390*04fd306cSNickeau     * and becomes simple `path`
391*04fd306cSNickeau     * The metadata is stored in a table `page-image` with the column `path`.
392*04fd306cSNickeau     */
393*04fd306cSNickeau    public static function getPersistentName(): string
394*04fd306cSNickeau    {
395*04fd306cSNickeau        return static::getName();
396*04fd306cSNickeau    }
397*04fd306cSNickeau
398*04fd306cSNickeau    /**
399*04fd306cSNickeau     * @return string the unique name of the metadata (property)
400*04fd306cSNickeau     *
401*04fd306cSNickeau     *
402*04fd306cSNickeau     * It's the hierachical representation of the {@link self::getPersistentName()}
403*04fd306cSNickeau     *
404*04fd306cSNickeau     */
405*04fd306cSNickeau    public static abstract function getName(): string;
406*04fd306cSNickeau
407*04fd306cSNickeau
408*04fd306cSNickeau    /**
409*04fd306cSNickeau     * @return null|string|array the value to be persisted to the {@link self::setWriteStore()}
410*04fd306cSNickeau     * the reverse action is {@link Metadata::setFromStoreValue()}
411*04fd306cSNickeau     *
412*04fd306cSNickeau     * Null may be returned (no exception is thrown)
413*04fd306cSNickeau     * as this is a possible storage value
414*04fd306cSNickeau     */
415*04fd306cSNickeau    public function toStoreValue()
416*04fd306cSNickeau    {
417*04fd306cSNickeau        try {
418*04fd306cSNickeau            return $this->getValue();
419*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
420*04fd306cSNickeau            /**
421*04fd306cSNickeau             * The only case when we return null
422*04fd306cSNickeau             * and not throw an exception
423*04fd306cSNickeau             * because it may be stored
424*04fd306cSNickeau             */
425*04fd306cSNickeau            return null;
426*04fd306cSNickeau        }
427*04fd306cSNickeau    }
428*04fd306cSNickeau
429*04fd306cSNickeau
430*04fd306cSNickeau    /**
431*04fd306cSNickeau     * @return mixed
432*04fd306cSNickeau     * The store default value is used to
433*04fd306cSNickeau     * see if the value set is the same than the default one
434*04fd306cSNickeau     * It this is the case, the data is not stored
435*04fd306cSNickeau     */
436*04fd306cSNickeau    public function toStoreDefaultValue()
437*04fd306cSNickeau    {
438*04fd306cSNickeau        try {
439*04fd306cSNickeau            return $this->getDefaultValue();
440*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
441*04fd306cSNickeau            /**
442*04fd306cSNickeau             * We don't throw an null exception here because
443*04fd306cSNickeau             * null may be stored
444*04fd306cSNickeau             */
445*04fd306cSNickeau            return null;
446*04fd306cSNickeau        }
447*04fd306cSNickeau    }
448*04fd306cSNickeau
449*04fd306cSNickeau    /**
450*04fd306cSNickeau     * Data that should persist (this data should be in a backup)
451*04fd306cSNickeau     */
452*04fd306cSNickeau    public const PERSISTENT_METADATA = "persistent";
453*04fd306cSNickeau    /**
454*04fd306cSNickeau     * Data that are derived from other and stored
455*04fd306cSNickeau     */
456*04fd306cSNickeau    public const DERIVED_METADATA = "derived";
457*04fd306cSNickeau    /**
458*04fd306cSNickeau     * A runtime metadata is created for the purpose of a process
459*04fd306cSNickeau     * Example {@link CacheRuntimeDependencies2}
460*04fd306cSNickeau     */
461*04fd306cSNickeau    const RUNTIME_METADATA = "runtime";
462*04fd306cSNickeau
463*04fd306cSNickeau
464*04fd306cSNickeau    /**
465*04fd306cSNickeau     *
466*04fd306cSNickeau     * Return the type of metadata.
467*04fd306cSNickeau     *   * {@link Metadata::PERSISTENT_METADATA}
468*04fd306cSNickeau     *   * {@link Metadata::DERIVED_METADATA}
469*04fd306cSNickeau     *   * {@link Metadata::RUNTIME_METADATA}
470*04fd306cSNickeau     *   *
471*04fd306cSNickeau     *
472*04fd306cSNickeau     * Backup: Only the {@link Metadata::PERSISTENT_METADATA} got a backup
473*04fd306cSNickeau     *
474*04fd306cSNickeau     * @return string
475*04fd306cSNickeau     *
476*04fd306cSNickeau     * Unfortunately, Dokuwiki makes this distinction only in rendering
477*04fd306cSNickeau     * https://forum.dokuwiki.org/d/19764-how-to-test-a-current-metadata-setting
478*04fd306cSNickeau     * Therefore all metadata are persistent
479*04fd306cSNickeau     *
480*04fd306cSNickeau     * Ie a {@link MetadataDokuWikiStore::CURRENT_METADATA} is only derived
481*04fd306cSNickeau     * in a rendering context. A {@link MetadataDokuWikiStore::PERSISTENT_DOKUWIKI_KEY} is always stored.
482*04fd306cSNickeau     *
483*04fd306cSNickeau     *
484*04fd306cSNickeau     *
485*04fd306cSNickeau     */
486*04fd306cSNickeau    public abstract static function getPersistenceType(): string;
487*04fd306cSNickeau
488*04fd306cSNickeau
489*04fd306cSNickeau    /**
490*04fd306cSNickeau     * The user can't delete this metadata
491*04fd306cSNickeau     * in the persistent metadata
492*04fd306cSNickeau     */
493*04fd306cSNickeau    const NOT_MODIFIABLE_PERSISTENT_METADATA = [
494*04fd306cSNickeau        PagePath::PROPERTY_NAME,
495*04fd306cSNickeau        CreationDate::PROPERTY_NAME,
496*04fd306cSNickeau        ModificationDate::PROPERTY_NAME,
497*04fd306cSNickeau        PageId::PROPERTY_NAME,
498*04fd306cSNickeau        "user",
499*04fd306cSNickeau        "contributor",
500*04fd306cSNickeau        "creator",
501*04fd306cSNickeau        "date",
502*04fd306cSNickeau        action_plugin_combo_metadescription::DESCRIPTION_META_KEY, // Dokuwiki implements it as an array (you can't modify it directly)
503*04fd306cSNickeau        "last_change" // not sure why it's in the persistent data
504*04fd306cSNickeau    ];
505*04fd306cSNickeau
506*04fd306cSNickeau    /**
507*04fd306cSNickeau     * Metadata that we can lose
508*04fd306cSNickeau     * because they are generated
509*04fd306cSNickeau     *
510*04fd306cSNickeau     * They still needs to be saved in as persistent metadata
511*04fd306cSNickeau     * otherwise they are just not persisted
512*04fd306cSNickeau     * https://forum.dokuwiki.org/d/19764-how-to-test-a-current-metadata-setting
513*04fd306cSNickeau     */
514*04fd306cSNickeau    const RUNTIME_META = [
515*04fd306cSNickeau        "format",
516*04fd306cSNickeau        "internal", // toc, cache, ...
517*04fd306cSNickeau        "relation",
518*04fd306cSNickeau        PageH1::H1_PARSED,
519*04fd306cSNickeau        LowQualityCalculatedIndicator::PROPERTY_NAME
520*04fd306cSNickeau    ];
521*04fd306cSNickeau
522*04fd306cSNickeau
523*04fd306cSNickeau    /**
524*04fd306cSNickeau     * Delete the managed metadata
525*04fd306cSNickeau     * @param $metadataArray - a metadata array
526*04fd306cSNickeau     * @return array - the metadata array without the managed metadata
527*04fd306cSNickeau     */
528*04fd306cSNickeau    public static function deleteMutableMetadata($metadataArray): array
529*04fd306cSNickeau    {
530*04fd306cSNickeau        if (sizeof($metadataArray) === 0) {
531*04fd306cSNickeau            return $metadataArray;
532*04fd306cSNickeau        }
533*04fd306cSNickeau        $cleanedMetadata = [];
534*04fd306cSNickeau        foreach ($metadataArray as $key => $value) {
535*04fd306cSNickeau            try {
536*04fd306cSNickeau                if (!MetadataSystem::getForName($key)->isMutable()) {
537*04fd306cSNickeau                    $cleanedMetadata[$key] = $value;
538*04fd306cSNickeau                }
539*04fd306cSNickeau            } catch (ExceptionNotFound $e) {
540*04fd306cSNickeau                continue;
541*04fd306cSNickeau            }
542*04fd306cSNickeau        }
543*04fd306cSNickeau        return $cleanedMetadata;
544*04fd306cSNickeau    }
545*04fd306cSNickeau
546*04fd306cSNickeau    /**
547*04fd306cSNickeau     * This function will upsert the meta array
548*04fd306cSNickeau     * with a unique property
549*04fd306cSNickeau     * @param $metaArray
550*04fd306cSNickeau     * @param string $uniqueAttribute
551*04fd306cSNickeau     * @param array $attributes
552*04fd306cSNickeau     */
553*04fd306cSNickeau    public static function upsertMetaOnUniqueAttribute(&$metaArray, string $uniqueAttribute, array $attributes)
554*04fd306cSNickeau    {
555*04fd306cSNickeau
556*04fd306cSNickeau        foreach ($metaArray as $key => $meta) {
557*04fd306cSNickeau            if (!is_numeric($key)) {
558*04fd306cSNickeau                LogUtility::msg("The passed array is not a meta array because the index are not numeric. Unable to update it.");
559*04fd306cSNickeau                return;
560*04fd306cSNickeau            }
561*04fd306cSNickeau            if (isset($meta[$uniqueAttribute])) {
562*04fd306cSNickeau                $value = $meta[$uniqueAttribute];
563*04fd306cSNickeau                if ($value === $attributes[$uniqueAttribute]) {
564*04fd306cSNickeau                    $metaArray[$key] = $attributes;
565*04fd306cSNickeau                    return;
566*04fd306cSNickeau                }
567*04fd306cSNickeau            }
568*04fd306cSNickeau        }
569*04fd306cSNickeau        $metaArray[] = $attributes;
570*04fd306cSNickeau
571*04fd306cSNickeau    }
572*04fd306cSNickeau
573*04fd306cSNickeau    /**
574*04fd306cSNickeau     * Delete the runtime if present
575*04fd306cSNickeau     * (They were saved in persistent)
576*04fd306cSNickeau     */
577*04fd306cSNickeau    public static function deleteIfPresent(array &$persistentPageMeta, array $attributeToDeletes): bool
578*04fd306cSNickeau    {
579*04fd306cSNickeau        $unsetWasPerformed = false;
580*04fd306cSNickeau        foreach ($attributeToDeletes as $runtimeMeta) {
581*04fd306cSNickeau            if (isset($persistentPageMeta[$runtimeMeta])) {
582*04fd306cSNickeau                unset($persistentPageMeta[$runtimeMeta]);
583*04fd306cSNickeau                $unsetWasPerformed = true;
584*04fd306cSNickeau            }
585*04fd306cSNickeau        }
586*04fd306cSNickeau        return $unsetWasPerformed;
587*04fd306cSNickeau    }
588*04fd306cSNickeau
589*04fd306cSNickeau    /**
590*04fd306cSNickeau     * @return bool can the user change the value
591*04fd306cSNickeau     *
592*04fd306cSNickeau     *
593*04fd306cSNickeau     * The meta that are modifiable by the user (in the form, ...)
594*04fd306cSNickeau     *
595*04fd306cSNickeau     * Usage:
596*04fd306cSNickeau     * * In a form, the field will be disabled
597*04fd306cSNickeau     * * Replication: this meta could be replicated
598*04fd306cSNickeau     *   * in the {@link \syntax_plugin_combo_frontmatter}
599*04fd306cSNickeau     *   * or in the database
600*04fd306cSNickeau     *
601*04fd306cSNickeau     * If you deprecate a metadata, you should set this value to false
602*04fd306cSNickeau     * and set the replacement to true (where the replacement takes the value of the deprecated metadata)
603*04fd306cSNickeau     */
604*04fd306cSNickeau    public abstract static function isMutable(): bool;
605*04fd306cSNickeau
606*04fd306cSNickeau    /**
607*04fd306cSNickeau     * @return bool if true the metadata will be shown on the meta manager form
608*04fd306cSNickeau     * Note that a non-mutable meta may also be shown for information purpose
609*04fd306cSNickeau     */
610*04fd306cSNickeau    public static function isOnForm(): bool
611*04fd306cSNickeau    {
612*04fd306cSNickeau        return false;
613*04fd306cSNickeau    }
614*04fd306cSNickeau
615*04fd306cSNickeau
616*04fd306cSNickeau    /**
617*04fd306cSNickeau     * @return array|null The possible values that can take this metadata
618*04fd306cSNickeau     * If null, all, no constraints
619*04fd306cSNickeau     */
620*04fd306cSNickeau    public function getPossibleValues(): ?array
621*04fd306cSNickeau    {
622*04fd306cSNickeau        return null;
623*04fd306cSNickeau    }
624*04fd306cSNickeau
625*04fd306cSNickeau    /**
626*04fd306cSNickeau     * An utility function to {@link Metadata::sendToWriteStore()}
627*04fd306cSNickeau     * and {@link MetadataStore::persist()} at the same time in the {@link Metadata::getWriteStore() write store}
628*04fd306cSNickeau     *
629*04fd306cSNickeau     * @throws ExceptionBadArgument - if the value can not be persisted
630*04fd306cSNickeau     */
631*04fd306cSNickeau    public function persist(): Metadata
632*04fd306cSNickeau    {
633*04fd306cSNickeau
634*04fd306cSNickeau        $oldValue = $this->getWriteStore()->get($this);
635*04fd306cSNickeau        $this->sendToWriteStore();
636*04fd306cSNickeau        $this->getWriteStore()->persist();
637*04fd306cSNickeau        $actualValue = $this->toStoreValue();
638*04fd306cSNickeau        $attribute = $this->getName();
639*04fd306cSNickeau
640*04fd306cSNickeau        MetadataMutation::notifyMetadataMutation($attribute, $oldValue, $actualValue, $this->getResource()->getPathObject());
641*04fd306cSNickeau
642*04fd306cSNickeau        return $this;
643*04fd306cSNickeau    }
644*04fd306cSNickeau
645*04fd306cSNickeau    /**
646*04fd306cSNickeau     * Build the object from the store value
647*04fd306cSNickeau     *
648*04fd306cSNickeau     * The inverse function is {@link Metadata::toStoreValue()}
649*04fd306cSNickeau     *
650*04fd306cSNickeau     * The function used by {@link Metadata::buildFromReadStore()}
651*04fd306cSNickeau     * to build the value from the {@link MetadataStore::get()}
652*04fd306cSNickeau     * function.
653*04fd306cSNickeau     *
654*04fd306cSNickeau     * The difference between the {@link Metadata::setFromStoreValue()}
655*04fd306cSNickeau     * is that this function should not make any validity check
656*04fd306cSNickeau     * or throw any compile exception.
657*04fd306cSNickeau     *
658*04fd306cSNickeau     * It's a commodity function that should be use by the developer
659*04fd306cSNickeau     * when it sets a known value and therefore does not expect a quality error
660*04fd306cSNickeau     *
661*04fd306cSNickeau     * @param $value
662*04fd306cSNickeau     * @return mixed
663*04fd306cSNickeau     */
664*04fd306cSNickeau    public abstract function setFromStoreValueWithoutException($value): Metadata;
665*04fd306cSNickeau
666*04fd306cSNickeau    /**
667*04fd306cSNickeau     * Set a value from the {@link self::getReadStore()}
668*04fd306cSNickeau     *
669*04fd306cSNickeau     * If you have quality problem to throw, you can use this function
670*04fd306cSNickeau     * instead of {@link Metadata::setFromStoreValueWithoutException()}
671*04fd306cSNickeau     *
672*04fd306cSNickeau     * @param $value
673*04fd306cSNickeau     * @return Metadata
674*04fd306cSNickeau     */
675*04fd306cSNickeau    public function setFromStoreValue($value): Metadata
676*04fd306cSNickeau    {
677*04fd306cSNickeau        return $this->setFromStoreValueWithoutException($value);
678*04fd306cSNickeau    }
679*04fd306cSNickeau
680*04fd306cSNickeau
681*04fd306cSNickeau    /**
682*04fd306cSNickeau     * The class of an entity metadata (ie if the metadata has children / is a {@link Metadata::$parent}
683*04fd306cSNickeau     *
684*04fd306cSNickeau     * One id value = one row = one entity
685*04fd306cSNickeau     *
686*04fd306cSNickeau     * @return string|null
687*04fd306cSNickeau     */
688*04fd306cSNickeau    public function getUidClass(): ?string
689*04fd306cSNickeau    {
690*04fd306cSNickeau        if (count(static::getChildrenClass()) >= 1) {
691*04fd306cSNickeau            LogUtility::msg("An entity metadata should define a metadata that store the unique value");
692*04fd306cSNickeau        }
693*04fd306cSNickeau        return null;
694*04fd306cSNickeau    }
695*04fd306cSNickeau
696*04fd306cSNickeau    /**
697*04fd306cSNickeau     * The width on a scale of 12 for the form field
698*04fd306cSNickeau     * @return null
699*04fd306cSNickeau     */
700*04fd306cSNickeau    public static function getFormControlWidth()
701*04fd306cSNickeau    {
702*04fd306cSNickeau        return null;
703*04fd306cSNickeau    }
704*04fd306cSNickeau
705*04fd306cSNickeau    /**
706*04fd306cSNickeau     * @return string[] - the old name if any
707*04fd306cSNickeau     */
708*04fd306cSNickeau    public static function getOldPersistentNames(): array
709*04fd306cSNickeau    {
710*04fd306cSNickeau        return [];
711*04fd306cSNickeau    }
712*04fd306cSNickeau
713*04fd306cSNickeau    /**
714*04fd306cSNickeau     * @return mixed - the memory value
715*04fd306cSNickeau     * @throws ExceptionNotFound - if the value is null
716*04fd306cSNickeau     */
717*04fd306cSNickeau    public abstract function getValue();
718*04fd306cSNickeau
719*04fd306cSNickeau    /**
720*04fd306cSNickeau     * @return mixed
721*04fd306cSNickeau     * @throws ExceptionNotFound
722*04fd306cSNickeau     */
723*04fd306cSNickeau    public abstract function getDefaultValue();
724*04fd306cSNickeau
725*04fd306cSNickeau    /**
726*04fd306cSNickeau     * @return mixed - set the memory value from the store and return ut
727*04fd306cSNickeau     * @deprecated use the {@link self::toStoreValue()} instead
728*04fd306cSNickeau     */
729*04fd306cSNickeau    public function getValueFromStore()
730*04fd306cSNickeau    {
731*04fd306cSNickeau        return $this->toStoreValue();
732*04fd306cSNickeau    }
733*04fd306cSNickeau
734*04fd306cSNickeau
735*04fd306cSNickeau    /**
736*04fd306cSNickeau     * @throws ExceptionNotFound
737*04fd306cSNickeau     */
738*04fd306cSNickeau    public function getValueFromStoreOrDefault()
739*04fd306cSNickeau    {
740*04fd306cSNickeau        $this->setFromStoreValueWithoutException($this->getReadStore()->get($this));
741*04fd306cSNickeau        return $this->getValueOrDefault();
742*04fd306cSNickeau    }
743*04fd306cSNickeau
744*04fd306cSNickeau    /**
745*04fd306cSNickeau     * @throws ExceptionNotFound
746*04fd306cSNickeau     */
747*04fd306cSNickeau    public function getValueOrDefault()
748*04fd306cSNickeau    {
749*04fd306cSNickeau
750*04fd306cSNickeau        try {
751*04fd306cSNickeau            $value = $this->getValue();
752*04fd306cSNickeau            if ($value === "") {
753*04fd306cSNickeau                return $this->getDefaultValue();
754*04fd306cSNickeau            }
755*04fd306cSNickeau            return $value;
756*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
757*04fd306cSNickeau            return $this->getDefaultValue();
758*04fd306cSNickeau        }
759*04fd306cSNickeau
760*04fd306cSNickeau    }
761*04fd306cSNickeau
762*04fd306cSNickeau
763*04fd306cSNickeau    /**
764*04fd306cSNickeau     * @return MetadataStore - the store where the metadata are persist (by default, the {@link Metadata::getReadStore()}
765*04fd306cSNickeau     */
766*04fd306cSNickeau    public function getWriteStore(): MetadataStore
767*04fd306cSNickeau    {
768*04fd306cSNickeau        if ($this->writeStore === null) {
769*04fd306cSNickeau            return $this->getReadStore();
770*04fd306cSNickeau        }
771*04fd306cSNickeau        /**
772*04fd306cSNickeau         * WriteStore may be just the string class name
773*04fd306cSNickeau         */
774*04fd306cSNickeau        if (!($this->writeStore instanceof MetadataStore)) {
775*04fd306cSNickeau            $this->writeStore = MetadataStoreAbs::toMetadataStore($this->writeStore, $this->getResource());
776*04fd306cSNickeau        }
777*04fd306cSNickeau        return $this->writeStore;
778*04fd306cSNickeau    }
779*04fd306cSNickeau
780*04fd306cSNickeau    /**
781*04fd306cSNickeau     * @throws ExceptionBadArgument - if the class string of the children are not good
782*04fd306cSNickeau     */
783*04fd306cSNickeau    public function getUidObject(): Metadata
784*04fd306cSNickeau    {
785*04fd306cSNickeau        if ($this->uidObject === null) {
786*04fd306cSNickeau
787*04fd306cSNickeau            $this->uidObject = MetadataSystem::toMetadataObject($this->getUidClass())
788*04fd306cSNickeau                ->setResource($this->getResource());
789*04fd306cSNickeau
790*04fd306cSNickeau        }
791*04fd306cSNickeau        return $this->uidObject;
792*04fd306cSNickeau    }
793*04fd306cSNickeau
794*04fd306cSNickeau}
795