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