1c3437056SNickeau<?php 2c3437056SNickeau 3c3437056SNickeau 4c3437056SNickeaunamespace ComboStrap; 5c3437056SNickeau 6c3437056SNickeau 704fd306cSNickeauuse ComboStrap\Meta\Api\Metadata; 804fd306cSNickeauuse ComboStrap\Meta\Api\MetadataText; 904fd306cSNickeauuse ComboStrap\Web\UrlRewrite; 10c3437056SNickeau 11c3437056SNickeau/** 12c3437056SNickeau * Class PageUrlPath 13c3437056SNickeau * @package ComboStrap 14c3437056SNickeau * 15c3437056SNickeau * 16c3437056SNickeau * The path (ie id attribute in the url) in a absolute format (ie with root) 17c3437056SNickeau * 1804fd306cSNickeau * This is used in the {@link UrlRewrite} module where the path is rewritten 1904fd306cSNickeau * 20c3437056SNickeau * url path: name for ns + slug (title) + page id 21c3437056SNickeau * or 22c3437056SNickeau * url path: canonical path + page id 23c3437056SNickeau * or 24c3437056SNickeau * url path: page path + page id 25c3437056SNickeau * 26c3437056SNickeau * 27c3437056SNickeau * - slug 28c3437056SNickeau * - hierarchical slug 29c3437056SNickeau * - permanent canonical path (page id) 30c3437056SNickeau * - canonical path 31c3437056SNickeau * - permanent page path (page id) 32c3437056SNickeau * - page path 33c3437056SNickeau * 3404fd306cSNickeau * This is not the URL of the page but of the generated HTML web page (Ie {@link MarkupPath}) with all pages (slots) 35c3437056SNickeau */ 3604fd306cSNickeauclass PageUrlPath extends MetadataText 37c3437056SNickeau{ 38c3437056SNickeau 39c3437056SNickeau /** 40c3437056SNickeau * 41c3437056SNickeau * The page id is separated in the URL with a "-" 42c3437056SNickeau * and not the standard "/" 43c3437056SNickeau * because in the devtool or any other tool, they takes 44c3437056SNickeau * the last part of the path as name. 45c3437056SNickeau * 46c3437056SNickeau * The name would be the short page id `22h2s2j4` 47c3437056SNickeau * and would have therefore no signification 48c3437056SNickeau * 49c3437056SNickeau * Instead the name is `metadata-manager-22h2s2j4` 50c3437056SNickeau * we can see then a page description, even order on it 51c3437056SNickeau */ 52c3437056SNickeau public const PAGE_ID_URL_SEPARATOR = "-"; 53c3437056SNickeau /** 54c3437056SNickeau * The canonical page for the page url 55c3437056SNickeau */ 56c3437056SNickeau public const CANONICAL = "page:url"; 57c3437056SNickeau const PROPERTY_NAME = "page-url-path"; 58c3437056SNickeau 5904fd306cSNickeau public static function createForPage(MarkupPath $page): PageUrlPath 60c3437056SNickeau { 61c3437056SNickeau return (new PageUrlPath()) 62c3437056SNickeau ->setResource($page); 63c3437056SNickeau } 64c3437056SNickeau 65c3437056SNickeau public static function getShortEncodedPageIdFromUrlId($lastPartName) 66c3437056SNickeau { 67c3437056SNickeau $lastPosition = strrpos($lastPartName, PageUrlPath::PAGE_ID_URL_SEPARATOR); 68c3437056SNickeau if ($lastPosition === false) { 69c3437056SNickeau return null; 70c3437056SNickeau } 71c3437056SNickeau return substr($lastPartName, $lastPosition + 1); 72c3437056SNickeau } 73c3437056SNickeau 7404fd306cSNickeau static public function getTab(): string 75c3437056SNickeau { 76c3437056SNickeau return MetaManagerForm::TAB_REDIRECTION_VALUE; 77c3437056SNickeau } 78c3437056SNickeau 7904fd306cSNickeau public function getValue(): string 80c3437056SNickeau { 81c3437056SNickeau 82c3437056SNickeau $page = $this->getResource(); 8304fd306cSNickeau if (!($page instanceof MarkupPath)) { 8404fd306cSNickeau throw new ExceptionNotFound("The Url Path is not implemented for the resource type (" . $page->getType() . ")"); 85c3437056SNickeau } 86c3437056SNickeau 87c3437056SNickeau /** 88c3437056SNickeau * Type of Url 89c3437056SNickeau */ 9004fd306cSNickeau $pageUrlType = PageUrlType::createFromPage($page); 9104fd306cSNickeau $urlType = $pageUrlType->getValue(); 9204fd306cSNickeau $urlTypeDefault = $pageUrlType->getDefaultValue(); 93c3437056SNickeau if ($urlType === $urlTypeDefault) { 9404fd306cSNickeau // not sure why ? may be to not store the value if it has the same default 9504fd306cSNickeau throw new ExceptionNotFound("Same value as default"); 96c3437056SNickeau } 97c3437056SNickeau return $this->getUrlPathFromType($urlType); 98c3437056SNickeau 99c3437056SNickeau } 100c3437056SNickeau 10104fd306cSNickeau /** 10204fd306cSNickeau * @return string 10304fd306cSNickeau * 10404fd306cSNickeau */ 10504fd306cSNickeau public function getValueOrDefault(): string 10604fd306cSNickeau { 10704fd306cSNickeau try { 10804fd306cSNickeau return $this->getValue(); 10904fd306cSNickeau } catch (ExceptionNotFound $e) { 11004fd306cSNickeau return $this->getDefaultValue(); 11104fd306cSNickeau } 112c3437056SNickeau 11304fd306cSNickeau } 11404fd306cSNickeau 11504fd306cSNickeau 11604fd306cSNickeau static public function getDescription(): string 117c3437056SNickeau { 118c3437056SNickeau return "The path used in the page url"; 119c3437056SNickeau } 120c3437056SNickeau 12104fd306cSNickeau static public function getLabel(): string 122c3437056SNickeau { 123c3437056SNickeau return "Url Path"; 124c3437056SNickeau } 125c3437056SNickeau 126c3437056SNickeau static public function getName(): string 127c3437056SNickeau { 128c3437056SNickeau return self::PROPERTY_NAME; 129c3437056SNickeau } 130c3437056SNickeau 13104fd306cSNickeau static public function getPersistenceType(): string 132c3437056SNickeau { 133c3437056SNickeau return Metadata::DERIVED_METADATA; 134c3437056SNickeau } 135c3437056SNickeau 13604fd306cSNickeau static public function isMutable(): bool 137c3437056SNickeau { 138c3437056SNickeau return false; 139c3437056SNickeau } 140c3437056SNickeau 14104fd306cSNickeau /** 14204fd306cSNickeau * @return string 14304fd306cSNickeau */ 14404fd306cSNickeau public function getDefaultValue(): string 145c3437056SNickeau { 146c3437056SNickeau 14704fd306cSNickeau $urlTypeDefault = PageUrlType::createFromPage($this->getResource())->getDefaultValue(); 148c3437056SNickeau return $this->getUrlPathFromType($urlTypeDefault); 149c3437056SNickeau 150c3437056SNickeau } 151c3437056SNickeau 15204fd306cSNickeau static public function getCanonical(): string 153c3437056SNickeau { 154c3437056SNickeau return self::CANONICAL; 155c3437056SNickeau } 156c3437056SNickeau 157c3437056SNickeau 158c3437056SNickeau private 159c3437056SNickeau function toPermanentUrlPath(string $id): string 160c3437056SNickeau { 161c3437056SNickeau return $id . self::PAGE_ID_URL_SEPARATOR . $this->getPageIdAbbrUrlEncoded(); 162c3437056SNickeau } 163c3437056SNickeau 164c3437056SNickeau /** 165c3437056SNickeau * Add a one letter checksum 166c3437056SNickeau * to verify that this is a page id abbr 167c3437056SNickeau * ( and not to hit the index for nothing ) 168c3437056SNickeau * @return string 169c3437056SNickeau */ 170c3437056SNickeau public 171c3437056SNickeau function getPageIdAbbrUrlEncoded(): ?string 172c3437056SNickeau { 173c3437056SNickeau $page = $this->getPage(); 174c3437056SNickeau if ($page->getPageIdAbbr() == null) return null; 175c3437056SNickeau $abbr = $page->getPageIdAbbr(); 176c3437056SNickeau return self::encodePageId($abbr); 177c3437056SNickeau } 178c3437056SNickeau 179c3437056SNickeau /** 180c3437056SNickeau * Add a checksum character to the page id 181c3437056SNickeau * to check if it's a page id that we get in the url 182c3437056SNickeau * @param string $pageId 183c3437056SNickeau * @return string 184c3437056SNickeau */ 185c3437056SNickeau public static function encodePageId(string $pageId): string 186c3437056SNickeau { 187c3437056SNickeau return self::getPageIdChecksumCharacter($pageId) . $pageId; 188c3437056SNickeau } 189c3437056SNickeau 190c3437056SNickeau /** 191c3437056SNickeau * @param string $encodedPageId 192c3437056SNickeau * @return string|null return the decoded page id or null if it's not an encoded page id 193c3437056SNickeau */ 194c3437056SNickeau public static function decodePageId(string $encodedPageId): ?string 195c3437056SNickeau { 196c3437056SNickeau if (empty($encodedPageId)) return null; 197c3437056SNickeau $checkSum = $encodedPageId[0]; 198c3437056SNickeau $extractedEncodedPageId = substr($encodedPageId, 1); 199c3437056SNickeau $calculatedCheckSum = self::getPageIdChecksumCharacter($extractedEncodedPageId); 200c3437056SNickeau if ($calculatedCheckSum == null) return null; 201c3437056SNickeau if ($calculatedCheckSum != $checkSum) return null; 202c3437056SNickeau return $extractedEncodedPageId; 203c3437056SNickeau } 204c3437056SNickeau 205c3437056SNickeau /** 206c3437056SNickeau * @param string $pageId 207c3437056SNickeau * @return string|null - the checksum letter or null if this is not a page id 208c3437056SNickeau */ 209c3437056SNickeau public static function getPageIdChecksumCharacter(string $pageId): ?string 210c3437056SNickeau { 211c3437056SNickeau $total = 0; 212c3437056SNickeau for ($i = 0; $i < strlen($pageId); $i++) { 213c3437056SNickeau $letter = $pageId[$i]; 214c3437056SNickeau $pos = strpos(PageId::PAGE_ID_ALPHABET, $letter); 215c3437056SNickeau if ($pos === false) { 216c3437056SNickeau return null; 217c3437056SNickeau } 218c3437056SNickeau $total += $pos; 219c3437056SNickeau } 220c3437056SNickeau $checkSum = $total % strlen(PageId::PAGE_ID_ALPHABET); 221c3437056SNickeau return PageId::PAGE_ID_ALPHABET[$checkSum]; 222c3437056SNickeau } 223c3437056SNickeau 224c3437056SNickeau /** 225c3437056SNickeau * Utility to change the type of the resource 22604fd306cSNickeau * @return MarkupPath|null 227c3437056SNickeau */ 22804fd306cSNickeau private function getPage(): ?MarkupPath 229c3437056SNickeau { 230c3437056SNickeau $resource = $this->getResource(); 23104fd306cSNickeau if ($resource instanceof MarkupPath) { 232c3437056SNickeau return $resource; 233c3437056SNickeau } 234c3437056SNickeau return null; 235c3437056SNickeau } 236c3437056SNickeau 23704fd306cSNickeau /** 23804fd306cSNickeau * In case of internal error, the path is returned 23904fd306cSNickeau */ 24004fd306cSNickeau public function getUrlPathFromType(string $urlType): string 241c3437056SNickeau { 24204fd306cSNickeau 243c3437056SNickeau $page = $this->getResource(); 24404fd306cSNickeau $pagePath = $page->getPathObject()->toAbsoluteId(); 24504fd306cSNickeau if ((!$page instanceof MarkupPath)) { 24604fd306cSNickeau $message = "The url path is only for page resources"; 24704fd306cSNickeau LogUtility::internalError($message, $this->getCanonical()); 24804fd306cSNickeau return $pagePath; 249c3437056SNickeau } 250c3437056SNickeau 25104fd306cSNickeau 252c3437056SNickeau switch ($urlType) { 253c3437056SNickeau case PageUrlType::CONF_VALUE_PAGE_PATH: 254c3437056SNickeau // the default 255c3437056SNickeau return $pagePath; 256c3437056SNickeau case PageUrlType::CONF_VALUE_PERMANENT_PAGE_PATH: 257c3437056SNickeau return $this->toPermanentUrlPath($pagePath); 258c3437056SNickeau case PageUrlType::CONF_VALUE_CANONICAL_PATH: 25904fd306cSNickeau try { 26004fd306cSNickeau return Canonical::createForPage($page)->getValueOrDefault()->toAbsoluteId(); 26104fd306cSNickeau } catch (ExceptionNotFound $e) { 26204fd306cSNickeau // no canonical, path as default 26304fd306cSNickeau return $pagePath; 26404fd306cSNickeau } 265c3437056SNickeau case PageUrlType::CONF_VALUE_PERMANENT_CANONICAL_PATH: 266c3437056SNickeau return $this->toPermanentUrlPath($page->getCanonicalOrDefault()); 267c3437056SNickeau case PageUrlType::CONF_VALUE_SLUG: 268c3437056SNickeau return $this->toPermanentUrlPath($page->getSlugOrDefault()); 269c3437056SNickeau case PageUrlType::CONF_VALUE_HIERARCHICAL_SLUG: 270c3437056SNickeau $urlPath = $page->getSlugOrDefault(); 27104fd306cSNickeau $parentPage = $page; 27204fd306cSNickeau while (true) { 27304fd306cSNickeau try { 27404fd306cSNickeau $parentPage = $parentPage->getParent(); 27504fd306cSNickeau } catch (ExceptionNotFound $e) { 27604fd306cSNickeau break; 27704fd306cSNickeau } 278c3437056SNickeau if (!$parentPage->isRootHomePage()) { 279*241d63faSgerardnico try { 280c3437056SNickeau $urlPath = Slug::toSlugPath($parentPage->getNameOrDefault()) . $urlPath; 281*241d63faSgerardnico } catch (ExceptionNull $e) { 282*241d63faSgerardnico throw new \RuntimeException("The default name of the page (" . $parentPage . ") should not be empty."); 283*241d63faSgerardnico } 284c3437056SNickeau } 285c3437056SNickeau } 286c3437056SNickeau return $this->toPermanentUrlPath($urlPath); 287c3437056SNickeau case PageUrlType::CONF_VALUE_HOMED_SLUG: 288c3437056SNickeau $urlPath = $page->getSlugOrDefault(); 28904fd306cSNickeau try { 29004fd306cSNickeau $parentPage = $page->getParent(); 291c3437056SNickeau if (!$parentPage->isRootHomePage()) { 292*241d63faSgerardnico try { 293c3437056SNickeau $urlPath = Slug::toSlugPath($parentPage->getNameOrDefault()) . $urlPath; 294*241d63faSgerardnico } catch (ExceptionNull $e) { 295*241d63faSgerardnico throw new \RuntimeException("The default name of the page (" . $parentPage . ") should not be empty."); 296*241d63faSgerardnico } 297c3437056SNickeau } 29804fd306cSNickeau } catch (ExceptionNotFound $e) { 29904fd306cSNickeau // no parent page 300c3437056SNickeau } 301c3437056SNickeau return $this->toPermanentUrlPath($urlPath); 302c3437056SNickeau default: 30304fd306cSNickeau $message = "The url type ($urlType) is unknown and was unexpected"; 30404fd306cSNickeau LogUtility::internalError($message, self::PROPERTY_NAME); 30504fd306cSNickeau return $pagePath; 30604fd306cSNickeau 307c3437056SNickeau } 308c3437056SNickeau } 309c3437056SNickeau 31004fd306cSNickeau static public function isOnForm(): bool 31104fd306cSNickeau { 31204fd306cSNickeau return true; 31304fd306cSNickeau } 31404fd306cSNickeau 31504fd306cSNickeau public function getValueOrDefaultAsWikiId(): string 31604fd306cSNickeau { 31704fd306cSNickeau return WikiPath::removeRootSepIfPresent($this->getValueOrDefault()); 31804fd306cSNickeau } 31904fd306cSNickeau 320c3437056SNickeau} 321