xref: /plugin/combo/ComboStrap/PageUrlPath.php (revision 241d63fa73ef2a57ee74f4b954a7b5c8eba40a9d)
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