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