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