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