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