1c3437056SNickeau<?php 2c3437056SNickeau 3c3437056SNickeau 4c3437056SNickeaunamespace ComboStrap; 5c3437056SNickeau 6c3437056SNickeau 7*04fd306cSNickeauuse ComboStrap\Meta\Api\Metadata; 8*04fd306cSNickeauuse ComboStrap\Meta\Api\MetadataText; 9*04fd306cSNickeauuse ComboStrap\Meta\Api\MetadataWikiPath; 10*04fd306cSNickeauuse ComboStrap\Web\UrlRewrite; 11c3437056SNickeau 12c3437056SNickeau/** 13c3437056SNickeau * Class PageUrlPath 14c3437056SNickeau * @package ComboStrap 15c3437056SNickeau * 16c3437056SNickeau * 17c3437056SNickeau * The path (ie id attribute in the url) in a absolute format (ie with root) 18c3437056SNickeau * 19*04fd306cSNickeau * This is used in the {@link UrlRewrite} module where the path is rewritten 20*04fd306cSNickeau * 21c3437056SNickeau * url path: name for ns + slug (title) + page id 22c3437056SNickeau * or 23c3437056SNickeau * url path: canonical path + page id 24c3437056SNickeau * or 25c3437056SNickeau * url path: page path + page id 26c3437056SNickeau * 27c3437056SNickeau * 28c3437056SNickeau * - slug 29c3437056SNickeau * - hierarchical slug 30c3437056SNickeau * - permanent canonical path (page id) 31c3437056SNickeau * - canonical path 32c3437056SNickeau * - permanent page path (page id) 33c3437056SNickeau * - page path 34c3437056SNickeau * 35*04fd306cSNickeau * This is not the URL of the page but of the generated HTML web page (Ie {@link MarkupPath}) with all pages (slots) 36c3437056SNickeau */ 37*04fd306cSNickeauclass PageUrlPath extends MetadataText 38c3437056SNickeau{ 39c3437056SNickeau 40c3437056SNickeau /** 41c3437056SNickeau * 42c3437056SNickeau * The page id is separated in the URL with a "-" 43c3437056SNickeau * and not the standard "/" 44c3437056SNickeau * because in the devtool or any other tool, they takes 45c3437056SNickeau * the last part of the path as name. 46c3437056SNickeau * 47c3437056SNickeau * The name would be the short page id `22h2s2j4` 48c3437056SNickeau * and would have therefore no signification 49c3437056SNickeau * 50c3437056SNickeau * Instead the name is `metadata-manager-22h2s2j4` 51c3437056SNickeau * we can see then a page description, even order on it 52c3437056SNickeau */ 53c3437056SNickeau public const PAGE_ID_URL_SEPARATOR = "-"; 54c3437056SNickeau /** 55c3437056SNickeau * The canonical page for the page url 56c3437056SNickeau */ 57c3437056SNickeau public const CANONICAL = "page:url"; 58c3437056SNickeau const PROPERTY_NAME = "page-url-path"; 59c3437056SNickeau 60*04fd306cSNickeau public static function createForPage(MarkupPath $page): PageUrlPath 61c3437056SNickeau { 62c3437056SNickeau return (new PageUrlPath()) 63c3437056SNickeau ->setResource($page); 64c3437056SNickeau } 65c3437056SNickeau 66c3437056SNickeau public static function getShortEncodedPageIdFromUrlId($lastPartName) 67c3437056SNickeau { 68c3437056SNickeau $lastPosition = strrpos($lastPartName, PageUrlPath::PAGE_ID_URL_SEPARATOR); 69c3437056SNickeau if ($lastPosition === false) { 70c3437056SNickeau return null; 71c3437056SNickeau } 72c3437056SNickeau return substr($lastPartName, $lastPosition + 1); 73c3437056SNickeau } 74c3437056SNickeau 75*04fd306cSNickeau static public function getTab(): string 76c3437056SNickeau { 77c3437056SNickeau return MetaManagerForm::TAB_REDIRECTION_VALUE; 78c3437056SNickeau } 79c3437056SNickeau 80*04fd306cSNickeau public function getValue(): string 81c3437056SNickeau { 82c3437056SNickeau 83c3437056SNickeau $page = $this->getResource(); 84*04fd306cSNickeau if (!($page instanceof MarkupPath)) { 85*04fd306cSNickeau throw new ExceptionNotFound("The Url Path is not implemented for the resource type (" . $page->getType() . ")"); 86c3437056SNickeau } 87c3437056SNickeau 88c3437056SNickeau /** 89c3437056SNickeau * Type of Url 90c3437056SNickeau */ 91*04fd306cSNickeau $pageUrlType = PageUrlType::createFromPage($page); 92*04fd306cSNickeau $urlType = $pageUrlType->getValue(); 93*04fd306cSNickeau $urlTypeDefault = $pageUrlType->getDefaultValue(); 94c3437056SNickeau if ($urlType === $urlTypeDefault) { 95*04fd306cSNickeau // not sure why ? may be to not store the value if it has the same default 96*04fd306cSNickeau throw new ExceptionNotFound("Same value as default"); 97c3437056SNickeau } 98c3437056SNickeau return $this->getUrlPathFromType($urlType); 99c3437056SNickeau 100c3437056SNickeau } 101c3437056SNickeau 102*04fd306cSNickeau /** 103*04fd306cSNickeau * @return string 104*04fd306cSNickeau * 105*04fd306cSNickeau */ 106*04fd306cSNickeau public function getValueOrDefault(): string 107*04fd306cSNickeau { 108*04fd306cSNickeau try { 109*04fd306cSNickeau return $this->getValue(); 110*04fd306cSNickeau } catch (ExceptionNotFound $e) { 111*04fd306cSNickeau return $this->getDefaultValue(); 112*04fd306cSNickeau } 113c3437056SNickeau 114*04fd306cSNickeau } 115*04fd306cSNickeau 116*04fd306cSNickeau 117*04fd306cSNickeau static public function getDescription(): string 118c3437056SNickeau { 119c3437056SNickeau return "The path used in the page url"; 120c3437056SNickeau } 121c3437056SNickeau 122*04fd306cSNickeau static public function getLabel(): string 123c3437056SNickeau { 124c3437056SNickeau return "Url Path"; 125c3437056SNickeau } 126c3437056SNickeau 127c3437056SNickeau static public function getName(): string 128c3437056SNickeau { 129c3437056SNickeau return self::PROPERTY_NAME; 130c3437056SNickeau } 131c3437056SNickeau 132*04fd306cSNickeau static public function getPersistenceType(): string 133c3437056SNickeau { 134c3437056SNickeau return Metadata::DERIVED_METADATA; 135c3437056SNickeau } 136c3437056SNickeau 137*04fd306cSNickeau static public function isMutable(): bool 138c3437056SNickeau { 139c3437056SNickeau return false; 140c3437056SNickeau } 141c3437056SNickeau 142*04fd306cSNickeau /** 143*04fd306cSNickeau * @return string 144*04fd306cSNickeau */ 145*04fd306cSNickeau public function getDefaultValue(): string 146c3437056SNickeau { 147c3437056SNickeau 148*04fd306cSNickeau $urlTypeDefault = PageUrlType::createFromPage($this->getResource())->getDefaultValue(); 149c3437056SNickeau return $this->getUrlPathFromType($urlTypeDefault); 150c3437056SNickeau 151c3437056SNickeau } 152c3437056SNickeau 153*04fd306cSNickeau static public function getCanonical(): string 154c3437056SNickeau { 155c3437056SNickeau return self::CANONICAL; 156c3437056SNickeau } 157c3437056SNickeau 158c3437056SNickeau 159c3437056SNickeau private 160c3437056SNickeau function toPermanentUrlPath(string $id): string 161c3437056SNickeau { 162c3437056SNickeau return $id . self::PAGE_ID_URL_SEPARATOR . $this->getPageIdAbbrUrlEncoded(); 163c3437056SNickeau } 164c3437056SNickeau 165c3437056SNickeau /** 166c3437056SNickeau * Add a one letter checksum 167c3437056SNickeau * to verify that this is a page id abbr 168c3437056SNickeau * ( and not to hit the index for nothing ) 169c3437056SNickeau * @return string 170c3437056SNickeau */ 171c3437056SNickeau public 172c3437056SNickeau function getPageIdAbbrUrlEncoded(): ?string 173c3437056SNickeau { 174c3437056SNickeau $page = $this->getPage(); 175c3437056SNickeau if ($page->getPageIdAbbr() == null) return null; 176c3437056SNickeau $abbr = $page->getPageIdAbbr(); 177c3437056SNickeau return self::encodePageId($abbr); 178c3437056SNickeau } 179c3437056SNickeau 180c3437056SNickeau /** 181c3437056SNickeau * Add a checksum character to the page id 182c3437056SNickeau * to check if it's a page id that we get in the url 183c3437056SNickeau * @param string $pageId 184c3437056SNickeau * @return string 185c3437056SNickeau */ 186c3437056SNickeau public static function encodePageId(string $pageId): string 187c3437056SNickeau { 188c3437056SNickeau return self::getPageIdChecksumCharacter($pageId) . $pageId; 189c3437056SNickeau } 190c3437056SNickeau 191c3437056SNickeau /** 192c3437056SNickeau * @param string $encodedPageId 193c3437056SNickeau * @return string|null return the decoded page id or null if it's not an encoded page id 194c3437056SNickeau */ 195c3437056SNickeau public static function decodePageId(string $encodedPageId): ?string 196c3437056SNickeau { 197c3437056SNickeau if (empty($encodedPageId)) return null; 198c3437056SNickeau $checkSum = $encodedPageId[0]; 199c3437056SNickeau $extractedEncodedPageId = substr($encodedPageId, 1); 200c3437056SNickeau $calculatedCheckSum = self::getPageIdChecksumCharacter($extractedEncodedPageId); 201c3437056SNickeau if ($calculatedCheckSum == null) return null; 202c3437056SNickeau if ($calculatedCheckSum != $checkSum) return null; 203c3437056SNickeau return $extractedEncodedPageId; 204c3437056SNickeau } 205c3437056SNickeau 206c3437056SNickeau /** 207c3437056SNickeau * @param string $pageId 208c3437056SNickeau * @return string|null - the checksum letter or null if this is not a page id 209c3437056SNickeau */ 210c3437056SNickeau public static function getPageIdChecksumCharacter(string $pageId): ?string 211c3437056SNickeau { 212c3437056SNickeau $total = 0; 213c3437056SNickeau for ($i = 0; $i < strlen($pageId); $i++) { 214c3437056SNickeau $letter = $pageId[$i]; 215c3437056SNickeau $pos = strpos(PageId::PAGE_ID_ALPHABET, $letter); 216c3437056SNickeau if ($pos === false) { 217c3437056SNickeau return null; 218c3437056SNickeau } 219c3437056SNickeau $total += $pos; 220c3437056SNickeau } 221c3437056SNickeau $checkSum = $total % strlen(PageId::PAGE_ID_ALPHABET); 222c3437056SNickeau return PageId::PAGE_ID_ALPHABET[$checkSum]; 223c3437056SNickeau } 224c3437056SNickeau 225c3437056SNickeau /** 226c3437056SNickeau * Utility to change the type of the resource 227*04fd306cSNickeau * @return MarkupPath|null 228c3437056SNickeau */ 229*04fd306cSNickeau private function getPage(): ?MarkupPath 230c3437056SNickeau { 231c3437056SNickeau $resource = $this->getResource(); 232*04fd306cSNickeau if ($resource instanceof MarkupPath) { 233c3437056SNickeau return $resource; 234c3437056SNickeau } 235c3437056SNickeau return null; 236c3437056SNickeau } 237c3437056SNickeau 238*04fd306cSNickeau /** 239*04fd306cSNickeau * In case of internal error, the path is returned 240*04fd306cSNickeau */ 241*04fd306cSNickeau public function getUrlPathFromType(string $urlType): string 242c3437056SNickeau { 243*04fd306cSNickeau 244c3437056SNickeau $page = $this->getResource(); 245*04fd306cSNickeau $pagePath = $page->getPathObject()->toAbsoluteId(); 246*04fd306cSNickeau if ((!$page instanceof MarkupPath)) { 247*04fd306cSNickeau $message = "The url path is only for page resources"; 248*04fd306cSNickeau LogUtility::internalError($message, $this->getCanonical()); 249*04fd306cSNickeau return $pagePath; 250c3437056SNickeau } 251c3437056SNickeau 252*04fd306cSNickeau 253c3437056SNickeau switch ($urlType) { 254c3437056SNickeau case PageUrlType::CONF_VALUE_PAGE_PATH: 255c3437056SNickeau // the default 256c3437056SNickeau return $pagePath; 257c3437056SNickeau case PageUrlType::CONF_VALUE_PERMANENT_PAGE_PATH: 258c3437056SNickeau return $this->toPermanentUrlPath($pagePath); 259c3437056SNickeau case PageUrlType::CONF_VALUE_CANONICAL_PATH: 260*04fd306cSNickeau try { 261*04fd306cSNickeau return Canonical::createForPage($page)->getValueOrDefault()->toAbsoluteId(); 262*04fd306cSNickeau } catch (ExceptionNotFound $e) { 263*04fd306cSNickeau // no canonical, path as default 264*04fd306cSNickeau return $pagePath; 265*04fd306cSNickeau } 266c3437056SNickeau case PageUrlType::CONF_VALUE_PERMANENT_CANONICAL_PATH: 267c3437056SNickeau return $this->toPermanentUrlPath($page->getCanonicalOrDefault()); 268c3437056SNickeau case PageUrlType::CONF_VALUE_SLUG: 269c3437056SNickeau return $this->toPermanentUrlPath($page->getSlugOrDefault()); 270c3437056SNickeau case PageUrlType::CONF_VALUE_HIERARCHICAL_SLUG: 271c3437056SNickeau $urlPath = $page->getSlugOrDefault(); 272*04fd306cSNickeau $parentPage = $page; 273*04fd306cSNickeau while (true) { 274*04fd306cSNickeau try { 275*04fd306cSNickeau $parentPage = $parentPage->getParent(); 276*04fd306cSNickeau } catch (ExceptionNotFound $e) { 277*04fd306cSNickeau break; 278*04fd306cSNickeau } 279c3437056SNickeau if (!$parentPage->isRootHomePage()) { 280c3437056SNickeau $urlPath = Slug::toSlugPath($parentPage->getNameOrDefault()) . $urlPath; 281c3437056SNickeau } 282c3437056SNickeau } 283c3437056SNickeau return $this->toPermanentUrlPath($urlPath); 284c3437056SNickeau case PageUrlType::CONF_VALUE_HOMED_SLUG: 285c3437056SNickeau $urlPath = $page->getSlugOrDefault(); 286*04fd306cSNickeau try { 287*04fd306cSNickeau $parentPage = $page->getParent(); 288c3437056SNickeau if (!$parentPage->isRootHomePage()) { 289c3437056SNickeau $urlPath = Slug::toSlugPath($parentPage->getNameOrDefault()) . $urlPath; 290c3437056SNickeau } 291*04fd306cSNickeau } catch (ExceptionNotFound $e) { 292*04fd306cSNickeau // no parent page 293c3437056SNickeau } 294c3437056SNickeau return $this->toPermanentUrlPath($urlPath); 295c3437056SNickeau default: 296*04fd306cSNickeau $message = "The url type ($urlType) is unknown and was unexpected"; 297*04fd306cSNickeau LogUtility::internalError($message, self::PROPERTY_NAME); 298*04fd306cSNickeau return $pagePath; 299*04fd306cSNickeau 300c3437056SNickeau } 301c3437056SNickeau } 302c3437056SNickeau 303*04fd306cSNickeau static public function isOnForm(): bool 304*04fd306cSNickeau { 305*04fd306cSNickeau return true; 306*04fd306cSNickeau } 307*04fd306cSNickeau 308*04fd306cSNickeau public function getValueOrDefaultAsWikiId(): string 309*04fd306cSNickeau { 310*04fd306cSNickeau return WikiPath::removeRootSepIfPresent($this->getValueOrDefault()); 311*04fd306cSNickeau } 312*04fd306cSNickeau 313c3437056SNickeau} 314