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