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 poge 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 [];
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     * @throws ExceptionCompile
51     */
52    public static function process(MarkupPath $missingPage): array
53    {
54
55        $return = array();
56
57        $minimalScoreForARedirect = SiteConfig::getConfValue(self::CONF_MINIMAL_SCORE_FOR_REDIRECT, self::CONF_MINIMAL_SCORE_FOR_REDIRECT_DEFAULT);
58
59        list($bestPage, $bestScore) = self::getBestEndPageId($missingPage);
60        if ($bestPage != null) {
61            $redirectType = action_plugin_combo_router::REDIRECT_NOTFOUND_METHOD;
62            if ($minimalScoreForARedirect != 0 && $bestScore >= $minimalScoreForARedirect) {
63                Aliases::createForPage($bestPage)
64                    ->addAlias($missingPage, AliasType::REDIRECT)
65                    ->sendToWriteStore()
66                    ->setReadStore(MetadataDbStore::getOrCreateFromResource($bestPage))
67                    ->sendToWriteStore();
68                $redirectType = action_plugin_combo_router::REDIRECT_PERMANENT_METHOD;
69            }
70            $return = array(
71                $bestPage,
72                $redirectType
73            );
74        }
75        return $return;
76
77    }
78
79    /**
80     * @param MarkupPath[] $candidatePagesWithSameLastName
81     * @param MarkupPath $requestedPage
82     * @return array
83     */
84    public static function getBestEndPageIdFromPages(array $candidatePagesWithSameLastName, MarkupPath $requestedPage): array
85    {
86        // Default value
87        $bestScore = 0;
88        $bestPage = $candidatePagesWithSameLastName[0];
89
90        // The name of the dokuwiki id
91        $requestedPageNames = $requestedPage->getPathObject()->getNames();
92
93        // Loop
94        foreach ($candidatePagesWithSameLastName as $candidatePage) {
95
96            try {
97                if ($candidatePage->getWikiId() === $requestedPage->getWikiId()) {
98                    // when the index is not up to date
99                    continue;
100                }
101            } catch (ExceptionBadArgument $e) {
102                // should not happen but yeah
103            }
104
105            $candidatePageNames = $candidatePage->getPathObject()->getNames();
106            $score = 0;
107            foreach ($candidatePageNames as $candidatePageName) {
108                if (in_array($candidatePageName, $requestedPageNames)) {
109                    $score++;
110                }
111            }
112            if ($score > $bestScore) {
113                $bestScore = $score;
114                $bestPage = $candidatePage;
115            } else if ($score === $bestScore) {
116                /**
117                 * Best backlink count
118                 */
119                $candidateBacklinksCount = sizeof($candidatePage->getBacklinks());
120                $bestPageBacklinksCount = sizeof($bestPage->getBacklinks());
121                if ($candidateBacklinksCount > $bestPageBacklinksCount) {
122                    $bestPage = $candidatePage;
123                }
124            }
125
126        }
127
128        return array(
129            $bestPage,
130            $bestScore
131        );
132    }
133}
134