1<?php
2
3
4namespace ComboStrap;
5
6
7class Aliases extends MetadataTabular
8{
9
10    public const PROPERTY_NAME = "aliases";
11
12
13    /**
14     * @var Alias[]
15     */
16    private $aliases;
17
18
19    public static function createForPage(Page $page): Aliases
20    {
21
22        return (new Aliases())->setResource($page);
23
24    }
25
26    public static function create(): Aliases
27    {
28        return new Aliases();
29    }
30
31    /**
32     * @return Alias[]|null
33     */
34    public function getValueAsAlias(): ?array
35    {
36        $rows = $this->getValue();
37        if ($rows === null) {
38            return null;
39        }
40        $aliases = [];
41        foreach ($rows as $row) {
42            /**
43             * @var AliasPath $aliasMeta
44             */
45            $aliasMeta = $row[AliasPath::getPersistentName()];
46            $alias = Alias::create($this->getResource(), $aliasMeta->getValue());
47            /**
48             * @var AliasType $aliasType
49             */
50            $aliasType = $row[AliasType::getPersistentName()];
51            if ($aliasType !== null) {
52                $aliasTypeValue = $aliasType->getValue();
53                if ($aliasTypeValue !== null) {
54                    $alias->setType($aliasType->getValue());
55                }
56            }
57            $aliases[] = $alias;
58        }
59        return $aliases;
60    }
61
62    /**
63     * @param array|null $aliasesPersistentValues
64     * return Alias[]
65     */
66    private function toNativeAliasArray(?array $aliasesPersistentValues): array
67    {
68        if ($aliasesPersistentValues === null) {
69            return [];
70        }
71        $aliases = [];
72        foreach ($aliasesPersistentValues as $key => $value) {
73            if (is_array($value)) {
74                $path = $value[AliasPath::PERSISTENT_NAME];
75                if (empty($path)) {
76                    if (is_string($key)) {
77                        // Old way (deprecated)
78                        $path = $key;
79                    } else {
80                        LogUtility::msg("The path of the alias should not be empty to create a path", Alias::CANONICAL);
81                    }
82                }
83                $type = $value[AliasType::PERSISTENT_NAME];
84
85                /**
86                 * We don't create via the {@link Aliases::addAlias()}
87                 * to not persist for each each alias value
88                 **/
89                $aliases[] = Alias::create($this->getResource(), $path)
90                    ->setType($type);
91            } else {
92                $path = $value;
93                if (empty($path)) {
94                    LogUtility::msg("The value of the alias array should not be empty as it's the alias path", Alias::CANONICAL);
95                }
96                if (!is_string($path)) {
97                    $path = StringUtility::toString($path);
98                    LogUtility::msg("The alias element ($path) is not a string", Alias::CANONICAL);
99                }
100                $aliases[] = Alias::create($this->getResource(), $path);
101            }
102        }
103        return $aliases;
104    }
105
106
107    /**
108     * @param Alias[] $aliases
109     * @return null|array - the array to be saved in a text/json file
110     */
111    public static function toMetadataArray(?array $aliases): ?array
112    {
113        if ($aliases === null) {
114            return null;
115        }
116        $array = [];
117        foreach ($aliases as $alias) {
118            $array[$alias->getPath()] = [
119                AliasPath::PERSISTENT_NAME => $alias->getPath(),
120                AliasType::PERSISTENT_NAME => $alias->getType()
121            ];
122        }
123        return array_values($array);
124    }
125
126    public static function getName(): string
127    {
128        return self::PROPERTY_NAME;
129    }
130
131
132    public function getPersistenceType(): string
133    {
134        return MetadataDokuWikiStore::PERSISTENT_METADATA;
135    }
136
137    /**
138     * Code refactoring
139     * This method is not in the database page
140     * because it would create a cycle
141     *
142     * The old data was saved in the database
143     * but should have been saved on the file system
144     *
145     * Once
146     * @return Alias[]
147     * @deprecated 2021-10-31
148     */
149    private
150    function getAndDeleteDeprecatedAlias(): array
151    {
152        $sqlite = Sqlite::createOrGetSqlite();
153        if ($sqlite === null) return [];
154
155        $canonicalOrDefault = $this->getResource()->getCanonicalOrDefault();
156        $request = $sqlite
157            ->createRequest()
158            ->setQueryParametrized("select ALIAS from DEPRECATED_PAGES_ALIAS where CANONICAL = ?", [$canonicalOrDefault]);
159        $deprecatedAliases = [];
160        $deprecatedAliasInDb = [];
161        try {
162            $deprecatedAliasInDb = $request
163                ->execute()
164                ->getRows();
165        } catch (ExceptionCombo $e) {
166            LogUtility::msg("An exception has occurred with the deprecated alias selection query. {$e->getMessage()}", LogUtility::LVL_MSG_ERROR);
167            return [];
168        } finally {
169            $request->close();
170        }
171
172        array_map(
173            function ($row) use ($deprecatedAliases) {
174                $alias = $row['ALIAS'];
175                $deprecatedAliases[$alias] = Alias::create($this->getResource(), $alias)
176                    ->setType(AliasType::REDIRECT);
177            },
178            $deprecatedAliasInDb
179        );
180
181        /**
182         * Delete them
183         */
184
185        if (sizeof($deprecatedAliasInDb) > 0) {
186            $request = $sqlite
187                ->createRequest()
188                ->setQueryParametrized("delete from DEPRECATED_PAGE_ALIASES where CANONICAL = ?", [$canonicalOrDefault]);
189            try {
190                $request->execute();
191            } catch (ExceptionCombo $e) {
192                LogUtility::msg("An exception has occurred with the delete deprecated alias statement. {$e->getMessage()}", LogUtility::LVL_MSG_ERROR);
193            } finally {
194                $request->close();
195            }
196        }
197
198
199        /**
200         * Return
201         */
202        return $deprecatedAliases;
203
204    }
205
206    public function getDefaultValue(): array
207    {
208        return
209            [
210                [
211                    AliasPath::getPersistentName() => null,
212                    AliasType::getPersistentName() => AliasType::createForParent($this)->buildFromStoreValue(AliasType::DEFAULT)
213                ]
214            ];
215    }
216
217
218    public function getValue(): ?array
219    {
220        $this->buildCheck();
221
222        /**
223         * We don't do that on build because
224         * we are using a set a metadata method that creates
225         * a cycle via the {@link MetadataDokuWikiStore::PAGE_METADATA_MUTATION_EVENT}
226         */
227        if (
228            !$this->valueIsNotNull()
229            &&
230            $this->getReadStore() !== null
231            &&
232            $this->getReadStore() instanceof MetadataDokuWikiStore
233        ) {
234            $this->aliases = $this->getAndDeleteDeprecatedAlias();
235            /**
236             * To validate the migration we set a value
237             * (the array may be empty)
238             */
239            try {
240                $this->sendToWriteStore();
241            } catch (ExceptionCombo $e) {
242                LogUtility::msg("Error while persisting the new data");
243            }
244        }
245
246        return parent::getValue();
247    }
248
249    /**
250     * @throws ExceptionCombo
251     */
252    public
253    function addAlias(string $aliasPath, $aliasType = null): Aliases
254    {
255        $this->addAndGetAlias($aliasPath, $aliasType);
256        return $this;
257    }
258
259    /**
260     * @throws ExceptionCombo
261     */
262    public
263    function addAndGetAlias($aliasPath, $aliasType = null): Alias
264    {
265        $this->buildCheck();
266        $path = Metadata::toMetadataObject(AliasPath::class, $this)
267            ->setFromStoreValue($aliasPath);
268        $row[$path::getPersistentName()] = $path;
269
270        $alias = Alias::create($this->getResource(), $path->getValue());
271
272        if ($aliasType !== null) {
273            $aliasObject = Metadata::toMetadataObject(AliasType::class, $this)
274                ->setFromStoreValue($aliasType);
275            $row[$aliasObject::getPersistentName()] = $aliasObject;
276            $alias->setType($aliasType);
277        }
278        $this->rows[$path->getValue()] = $row;
279
280        return $alias;
281    }
282
283
284    public
285    function getCanonical(): string
286    {
287        return Alias::CANONICAL;
288    }
289
290    public
291    function getTab(): string
292    {
293        return MetaManagerForm::TAB_REDIRECTION_VALUE;
294    }
295
296    public
297    function getDescription(): string
298    {
299        return "Aliases that will redirect to this page.";
300    }
301
302    public
303    function getLabel(): string
304    {
305        return "Page Aliases";
306    }
307
308
309    public
310    function getMutable(): bool
311    {
312        return true;
313    }
314
315    public function getUidClass(): ?string
316    {
317        return AliasPath::class;
318    }
319
320    public function getChildrenClass(): ?array
321    {
322        return [AliasPath::class, AliasType::class];
323    }
324
325
326}
327