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