1<?php 2 3 4namespace ComboStrap\Meta\Field; 5 6 7use ComboStrap\ExceptionCompile; 8use ComboStrap\ExceptionNotFound; 9use ComboStrap\ExceptionSqliteNotAvailable; 10use ComboStrap\LogUtility; 11use ComboStrap\MarkupPath; 12use ComboStrap\Meta\Api\Metadata; 13use ComboStrap\Meta\Api\MetadataSystem; 14use ComboStrap\Meta\Store\MetadataDokuWikiStore; 15use ComboStrap\Meta\Api\MetadataTabular; 16use ComboStrap\MetaManagerForm; 17use ComboStrap\Sqlite; 18use ComboStrap\StringUtility; 19use ComboStrap\WikiPath; 20 21class Aliases extends MetadataTabular 22{ 23 24 public const PROPERTY_NAME = "aliases"; 25 26 27 public static function createForPage(MarkupPath $page): Aliases 28 { 29 30 return (new Aliases())->setResource($page); 31 32 } 33 34 public static function create(): Aliases 35 { 36 return new Aliases(); 37 } 38 39 /** 40 * @return Alias[]|null 41 * @throws ExceptionNotFound 42 */ 43 public function getValueAsAlias(): array 44 { 45 $rows = $this->getValue(); 46 $aliases = []; 47 foreach ($rows as $row) { 48 /** 49 * @var AliasPath $aliasMeta 50 */ 51 $aliasMeta = $row[AliasPath::getPersistentName()]; 52 $alias = Alias::create($this->getResource(), $aliasMeta->getValue()); 53 /** 54 * @var AliasType $aliasType 55 */ 56 $aliasType = $row[AliasType::getPersistentName()] ?? null; 57 if ($aliasType !== null) { 58 try { 59 $alias->setType($aliasType->getValue()); 60 } catch (ExceptionNotFound $e) { 61 // ok 62 } 63 64 } 65 $aliases[] = $alias; 66 } 67 return $aliases; 68 } 69 70 71 /** 72 * @param array|null $aliasesPersistentValues 73 * return Alias[] 74 */ 75 private function toNativeAliasArray(?array $aliasesPersistentValues): array 76 { 77 if ($aliasesPersistentValues === null) { 78 return []; 79 } 80 $aliases = []; 81 foreach ($aliasesPersistentValues as $key => $value) { 82 if (is_array($value)) { 83 $path = $value[AliasPath::PERSISTENT_NAME]; 84 if (empty($path)) { 85 if (is_string($key)) { 86 // Old way (deprecated) 87 $path = $key; 88 } else { 89 LogUtility::msg("The path of the alias should not be empty to create a path", Alias::CANONICAL); 90 } 91 } 92 $type = $value[AliasType::PERSISTENT_NAME]; 93 94 /** 95 * We don't create via the {@link Aliases::addAlias()} 96 * to not persist for each each alias value 97 **/ 98 $aliases[] = Alias::create($this->getResource(), $path) 99 ->setType($type); 100 } else { 101 $path = $value; 102 if (empty($path)) { 103 LogUtility::msg("The value of the alias array should not be empty as it's the alias path", Alias::CANONICAL); 104 } 105 if (!is_string($path)) { 106 $path = StringUtility::toString($path); 107 LogUtility::error("The alias element ($path) is not a string", Alias::CANONICAL); 108 } 109 $path = WikiPath::createMarkupPathFromId($path); 110 $aliases[] = Alias::create($this->getResource(), $path); 111 } 112 } 113 return $aliases; 114 } 115 116 117 /** 118 * @param Alias[] $aliases 119 * @return null|array - the array to be saved in a text/json file 120 */ 121 public static function toMetadataArray(?array $aliases): ?array 122 { 123 if ($aliases === null) { 124 return null; 125 } 126 $array = []; 127 foreach ($aliases as $alias) { 128 $array[$alias->getPath()->toAbsoluteId()] = [ 129 AliasPath::PERSISTENT_NAME => $alias->getPath()->toAbsoluteId(), 130 AliasType::PERSISTENT_NAME => $alias->getType() 131 ]; 132 } 133 return array_values($array); 134 } 135 136 public static function getName(): string 137 { 138 return self::PROPERTY_NAME; 139 } 140 141 142 static public function getPersistenceType(): string 143 { 144 return MetadataDokuWikiStore::PERSISTENT_DOKUWIKI_KEY; 145 } 146 147 /** 148 * Code refactoring 149 * This method is not in the database page 150 * because it would create a cycle 151 * 152 * The old data was saved in the database 153 * but should have been saved on the file system 154 * 155 * Once 156 * @return Alias[] 157 * @throws ExceptionSqliteNotAvailable 158 * @deprecated 2021-10-31 159 */ 160 private 161 function getAndDeleteDeprecatedAlias(): array 162 { 163 $sqlite = Sqlite::createOrGetSqlite(); 164 165 try { 166 $canonicalOrDefault = $this->getResource()->getCanonicalOrDefault(); 167 } catch (ExceptionNotFound $e) { 168 return []; 169 } 170 /** @noinspection SqlResolve */ 171 $request = $sqlite 172 ->createRequest() 173 ->setQueryParametrized("select ALIAS from DEPRECATED_PAGES_ALIAS where CANONICAL = ?", [$canonicalOrDefault]); 174 $deprecatedAliases = []; 175 $deprecatedAliasInDb = []; 176 try { 177 $deprecatedAliasInDb = $request 178 ->execute() 179 ->getRows(); 180 } catch (ExceptionCompile $e) { 181 LogUtility::msg("An exception has occurred with the deprecated alias selection query. {$e->getMessage()}"); 182 return []; 183 } finally { 184 $request->close(); 185 } 186 187 array_map( 188 function ($row) use ($deprecatedAliases) { 189 $alias = $row['ALIAS']; 190 $deprecatedAliases[$alias] = Alias::create($this->getResource(), $alias) 191 ->setType(AliasType::REDIRECT); 192 }, 193 $deprecatedAliasInDb 194 ); 195 196 /** 197 * Delete them 198 */ 199 200 if (sizeof($deprecatedAliasInDb) > 0) { 201 /** @noinspection SqlResolve */ 202 $request = $sqlite 203 ->createRequest() 204 ->setQueryParametrized("delete from DEPRECATED_PAGE_ALIASES where CANONICAL = ?", [$canonicalOrDefault]); 205 try { 206 $request->execute(); 207 } catch (ExceptionCompile $e) { 208 LogUtility::msg("An exception has occurred with the delete deprecated alias statement. {$e->getMessage()}", LogUtility::LVL_MSG_ERROR); 209 } finally { 210 $request->close(); 211 } 212 } 213 214 215 /** 216 * Return 217 */ 218 return $deprecatedAliases; 219 220 } 221 222 223 /** 224 * @return Metadata[][] - an list of rows of metadata columns 225 */ 226 public function getDefaultValue(): array 227 { 228 return 229 [ 230 [ 231 AliasPath::getPersistentName() => AliasPath::createForParent($this), 232 AliasType::getPersistentName() => AliasType::createForParent($this)->setFromStoreValueWithoutException(AliasType::DEFAULT) 233 ] 234 ]; 235 } 236 237 238 public function getValue(): array 239 { 240 $this->buildCheck(); 241 242 /** 243 * We don't do that on build because 244 * we are using a set a metadata method that creates 245 * a cycle via the {@link MetadataMutation::PAGE_METADATA_MUTATION_EVENT} 246 */ 247 if ( 248 !$this->valueIsNotNull() 249 && 250 $this->getReadStore() !== null 251 && 252 $this->getReadStore() instanceof MetadataDokuWikiStore 253 ) { 254 $this->getAndDeleteDeprecatedAlias(); 255 /** 256 * To validate the migration we set a value 257 * (the array may be empty) 258 */ 259 try { 260 $this->sendToWriteStore(); 261 } catch (ExceptionCompile $e) { 262 LogUtility::msg("Error while persisting the new data"); 263 } 264 } 265 266 return parent::getValue(); 267 } 268 269 /** 270 * @throws ExceptionCompile 271 */ 272 public 273 function addAlias(string $aliasPath, $aliasType = null): Aliases 274 { 275 $this->addAndGetAlias($aliasPath, $aliasType); 276 return $this; 277 } 278 279 /** 280 * @throws ExceptionCompile 281 */ 282 public 283 function addAndGetAlias($aliasPath, $aliasType = null): Alias 284 { 285 $this->buildCheck(); 286 /** 287 * @var AliasPath $path 288 */ 289 $path = MetadataSystem::toMetadataObject(AliasPath::class, $this) 290 ->setFromStoreValue($aliasPath); 291 $row[$path::getPersistentName()] = $path; 292 293 $alias = Alias::create($this->getResource(), $path->getValue()); 294 295 if ($aliasType !== null) { 296 $aliasObject = MetadataSystem::toMetadataObject(AliasType::class, $this) 297 ->setFromStoreValue($aliasType); 298 $row[$aliasObject::getPersistentName()] = $aliasObject; 299 $alias->setType($aliasType); 300 } 301 $this->rows[$path->getValue()->toAbsoluteId()] = $row; 302 303 return $alias; 304 } 305 306 307 static public 308 function getCanonical(): string 309 { 310 return Alias::CANONICAL; 311 } 312 313 static public 314 function getTab(): string 315 { 316 return MetaManagerForm::TAB_REDIRECTION_VALUE; 317 } 318 319 static public 320 function getDescription(): string 321 { 322 return "Aliases that will redirect to this page."; 323 } 324 325 static public function getLabel(): string 326 { 327 return "Page Aliases"; 328 } 329 330 331 static public function isMutable(): bool 332 { 333 return true; 334 } 335 336 public function getUidClass(): ?string 337 { 338 return AliasPath::class; 339 } 340 341 /** 342 * @return Metadata[] 343 */ 344 static public function getChildrenClass(): array 345 { 346 return [AliasPath::class, AliasType::class]; 347 } 348 349 350 static public function isOnForm(): bool 351 { 352 return true; 353 } 354} 355