1<?php
2
3namespace ComboStrap;
4
5use action_plugin_combo_router;
6use ComboStrap\Meta\Field\Aliases;
7use ComboStrap\Meta\Field\AliasType;
8use ComboStrap\Meta\Store\MetadataDbStore;
9
10
11/**
12 * Class UrlManagerBestEndPage
13 *
14 * A class that implements the BestEndPage Algorithm for the {@link action_plugin_combo_router urlManager}
15 */
16class RouterBestEndPage
17{
18
19    /**
20     * If the number of names part that match is greater or equal to
21     * this configuration, an Id Redirect is performed
22     * A value of 0 disable and send only HTTP redirect
23     */
24    const CONF_MINIMAL_SCORE_FOR_REDIRECT = 'BestEndPageMinimalScoreForAliasCreation';
25    const CONF_MINIMAL_SCORE_FOR_REDIRECT_DEFAULT = '2';
26
27
28    /**
29     * @param MarkupPath $requestedPage
30     * @return array - the best page id and its score
31     * The score is the number of name that matches
32     */
33    public static function getBestEndPageId(MarkupPath $requestedPage): array
34    {
35
36        $pagesWithSameName = Index::getOrCreate()
37            ->getPagesWithSameLastName($requestedPage);
38        if (sizeof($pagesWithSameName) == 0) {
39            return [null, null];
40        }
41        return self::getBestEndPageIdFromPages($pagesWithSameName, $requestedPage);
42
43
44    }
45
46
47    /**
48     * @param MarkupPath $missingPage
49     * @return array with the best page and the type of redirect
50     */
51    public static function process(MarkupPath $missingPage): array
52    {
53
54        $return = [null, null];
55
56        $minimalScoreForARedirect = SiteConfig::getConfValue(self::CONF_MINIMAL_SCORE_FOR_REDIRECT, self::CONF_MINIMAL_SCORE_FOR_REDIRECT_DEFAULT);
57
58        list($bestPage, $bestScore) = self::getBestEndPageId($missingPage);
59        if ($bestPage != null) {
60            $redirectType = RouterRedirection::REDIRECT_NOTFOUND_METHOD;
61            if ($minimalScoreForARedirect != 0 && $bestScore >= $minimalScoreForARedirect) {
62                try {
63                    Aliases::createForPage($bestPage)
64                        ->addAlias($missingPage, AliasType::REDIRECT)
65                        ->sendToWriteStore()
66                        ->setReadStore(MetadataDbStore::getOrCreateFromResource($bestPage))
67                        ->sendToWriteStore();
68                } catch (ExceptionBadArgument|ExceptionCompile $e) {
69                    LogUtility::error("Error while creating an alias",LogUtility::SUPPORT_CANONICAL,$e);
70                    return $return;
71                }
72                $redirectType = RouterRedirection::REDIRECT_PERMANENT_METHOD;
73            }
74            $return = array(
75                $bestPage,
76                $redirectType
77            );
78        }
79        return $return;
80
81    }
82
83    /**
84     * @param MarkupPath[] $candidatePagesWithSameLastName
85     * @param MarkupPath $requestedPage
86     * @return array
87     */
88    public static function getBestEndPageIdFromPages(array $candidatePagesWithSameLastName, MarkupPath $requestedPage): array
89    {
90        // Default value
91        $bestScore = 0;
92        $bestPage = $candidatePagesWithSameLastName[0];
93
94        // The name of the dokuwiki id
95        $requestedPageNames = $requestedPage->getPathObject()->getNames();
96
97        // Loop
98        foreach ($candidatePagesWithSameLastName as $candidatePage) {
99
100            try {
101                if ($candidatePage->getWikiId() === $requestedPage->getWikiId()) {
102                    // when the index is not up to date
103                    continue;
104                }
105            } catch (ExceptionBadArgument $e) {
106                // should not happen but yeah
107            }
108
109            $candidatePageNames = $candidatePage->getPathObject()->getNames();
110            $score = 0;
111            foreach ($candidatePageNames as $candidatePageName) {
112                if (in_array($candidatePageName, $requestedPageNames)) {
113                    $score++;
114                }
115            }
116            if ($score > $bestScore) {
117                $bestScore = $score;
118                $bestPage = $candidatePage;
119            } else if ($score === $bestScore) {
120                /**
121                 * Best backlink count
122                 */
123                $candidateBacklinksCount = sizeof($candidatePage->getBacklinks());
124                $bestPageBacklinksCount = sizeof($bestPage->getBacklinks());
125                if ($candidateBacklinksCount > $bestPageBacklinksCount) {
126                    $bestPage = $candidatePage;
127                }
128            }
129
130        }
131
132        return array(
133            $bestPage,
134            $bestScore
135        );
136    }
137}
138