1<?php 2 3 4namespace ComboStrap; 5 6 7use ComboStrap\Meta\Api\MetadataWikiPath; 8use ComboStrap\Meta\Store\MetadataDokuWikiStore; 9use ComboStrap\Web\Url; 10use ComboStrap\Web\UrlEndpoint; 11 12 13class Canonical extends MetadataWikiPath 14{ 15 16 public const PROPERTY_NAME = "canonical"; 17 public const CANONICAL = "canonical"; 18 19 /** 20 * The auto-canonical feature does not create any canonical value on the file system 21 * but creates a canonical in the database (where the {@link \action_plugin_combo_router} 22 * takes its information and it enables to route via a calculated canonical 23 * (ie the {@link Canonical::getDefaultValue()} 24 */ 25 public const CONF_CANONICAL_LAST_NAMES_COUNT = 'MinimalNamesCountForAutomaticCanonical'; 26 27 public static function createForPage(MarkupPath $page): Canonical 28 { 29 return (new Canonical()) 30 ->setResource($page); 31 32 } 33 34 /** 35 * @return WikiPath 36 * @throws ExceptionNotFound 37 */ 38 public function getValueOrDefault(): WikiPath 39 { 40 try { 41 return $this->getValue(); 42 } catch (ExceptionNotFound $e) { 43 return $this->getDefaultValue(); 44 } 45 } 46 47 48 public static function getTab(): string 49 { 50 return MetaManagerForm::TAB_REDIRECTION_VALUE; 51 } 52 53 public static function getDescription(): string 54 { 55 return "The canonical path is a short unique path for the page (used in named permalink)"; 56 } 57 58 public static function getLabel(): string 59 { 60 return "Canonical Path"; 61 } 62 63 public static function getName(): string 64 { 65 return self::PROPERTY_NAME; 66 } 67 68 public static function getPersistenceType(): string 69 { 70 return MetadataDokuWikiStore::PERSISTENT_DOKUWIKI_KEY; 71 } 72 73 public static function isMutable(): bool 74 { 75 return true; 76 } 77 78 public static function createFromValue(string $canonical): Canonical 79 { 80 /** @noinspection PhpIncompatibleReturnTypeInspection */ 81 return (new Canonical()) 82 ->setValue($canonical); 83 } 84 85 86 /** 87 * @return WikiPath 88 * @throws ExceptionNotFound 89 */ 90 public function getDefaultValue(): WikiPath 91 { 92 93 $resourceCombo = $this->getResource(); 94 if (!($resourceCombo instanceof MarkupPath)) { 95 throw new ExceptionNotFound("No default value for other resources than page"); 96 } 97 98 /** 99 * The last part of the id as canonical 100 */ 101 // How many last parts are taken into account in the canonical processing (2 by default) 102 $canonicalLastNamesCount = SiteConfig::getConfValue(self::CONF_CANONICAL_LAST_NAMES_COUNT, 0); 103 if ($canonicalLastNamesCount <= 0) { 104 throw new ExceptionNotFound("Default canonical value is not enabled, no default canonical"); 105 } 106 107 /** 108 * Takes the last names part 109 */ 110 $namesOriginal = $this->getResource()->getPathObject()->getNamesWithoutExtension(); 111 /** 112 * Delete the identical names at the end 113 * To resolve this problem 114 * The page (viz:viz) and the page (data:viz:viz) have the same canonical. 115 * The page (viz:viz) will get the canonical viz 116 * The page (data:viz) will get the canonical data:viz 117 */ 118 $i = sizeof($namesOriginal) - 1; 119 $names = $namesOriginal; 120 while ($namesOriginal[$i] == ($namesOriginal[$i - 1] ?? null)) { 121 unset($names[$i]); 122 $i--; 123 if ($i <= 0) { 124 break; 125 } 126 } 127 /** 128 * Minimal length check 129 */ 130 $namesLength = sizeof($names); 131 if ($namesLength > $canonicalLastNamesCount) { 132 $names = array_slice($names, $namesLength - $canonicalLastNamesCount); 133 } 134 /** 135 * If this is a `start` page, delete the name 136 * ie javascript:start will become javascript 137 * (Not a home page) 138 * 139 * We don't use the {@link MarkupPath::isIndexPage()} 140 * because the path `ns:ns` is also an index if the 141 * page `ns:start` does not exists 142 */ 143 if ($resourceCombo->getPathObject()->getLastNameWithoutExtension() === Site::getIndexPageName()) { 144 $names = array_slice($names, 0, $namesLength - 1); 145 } 146 $calculatedCanonical = implode(":", $names); 147 WikiPath::addRootSeparatorIfNotPresent($calculatedCanonical); 148 try { 149 return WikiPath::createMarkupPathFromPath($calculatedCanonical); 150 } catch (ExceptionBadArgument $e) { 151 LogUtility::internalError("A canonical should not be the root, should not happen", self::CANONICAL); 152 throw new ExceptionNotFound(); 153 } 154 155 } 156 157 public static function getCanonical(): string 158 { 159 return self::CANONICAL; 160 } 161 162 163 public static function getDrive(): string 164 { 165 return WikiPath::MARKUP_DRIVE; 166 } 167 168 public static function isOnForm(): bool 169 { 170 return true; 171 } 172 173 174 /** 175 * The Url of the local web server 176 * @throws ExceptionNotFound 177 */ 178 public function getLocalUrl(): Url 179 { 180 return UrlEndpoint::createDokuUrl() 181 ->addQueryParameter(DokuwikiId::DOKUWIKI_ID_ATTRIBUTE, $this->getValue()->getWikiId()); 182 } 183 184 /** 185 * 186 * @return Url - the url of the combostrap web server for documentation 187 * @throws ExceptionNotFound 188 */ 189 public function getComboStrapUrlForDocumentation(): Url 190 { 191 $path = $this->getValue()->toAbsoluteId(); 192 $path = str_replace(":", "/", $path); 193 try { 194 return Url::createFromString(PluginUtility::$INFO_PLUGIN['url']) 195 ->setPath($path); 196 } catch (ExceptionBadArgument|ExceptionBadSyntax $e) { 197 $message = "The url in the plugin info file seems to be broken."; 198 LogUtility::internalError($message, self::CANONICAL, $e); 199 throw new ExceptionNotFound($message); 200 } 201 } 202 203} 204