Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
38 / 38
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
PruningLinkRecommendationProvider
100.00% covered (success)
100.00%
38 / 38
100.00% covered (success)
100.00%
3 / 3
7
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 get
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 pruneLinkRecommendation
100.00% covered (success)
100.00%
29 / 29
100.00% covered (success)
100.00%
1 / 1
4
1<?php
2
3namespace GrowthExperiments\NewcomerTasks\AddLink;
4
5use GrowthExperiments\NewcomerTasks\TaskType\LinkRecommendationTaskType;
6use GrowthExperiments\NewcomerTasks\TaskType\TaskType;
7use MediaWiki\Cache\LinkBatchFactory;
8use MediaWiki\Linker\LinkTarget;
9use MediaWiki\Title\MalformedTitleException;
10use MediaWiki\Title\TitleFactory;
11use StatusValue;
12
13/**
14 * A link recommendation provider that removes red links and links which have been rejected too
15 * often. The class works as a decorator wrapping another provider.
16 */
17class PruningLinkRecommendationProvider implements LinkRecommendationProvider {
18
19    /** @var TitleFactory */
20    private $titleFactory;
21
22    /** @var LinkBatchFactory */
23    private $linkBatchFactory;
24
25    /** @var LinkRecommendationStore */
26    private $linkRecommendationStore;
27
28    /** @var LinkRecommendationProvider */
29    private $innerProvider;
30
31    /** @var bool */
32    private $pruneRedLinks;
33
34    /**
35     * @param TitleFactory $titleFactory
36     * @param LinkBatchFactory $linkBatchFactory
37     * @param LinkRecommendationStore $linkRecommendationStore
38     * @param LinkRecommendationProvider $innerProvider
39     * @param bool $pruneRedLinks Prune red links? Should be true in production settings as we
40     *   don't want to recommend red links, but in a developer setup it might be convenient to
41     *   pretend that all recommended articles exist.
42     */
43    public function __construct(
44        TitleFactory $titleFactory,
45        LinkBatchFactory $linkBatchFactory,
46        LinkRecommendationStore $linkRecommendationStore,
47        LinkRecommendationProvider $innerProvider,
48        bool $pruneRedLinks
49    ) {
50        $this->titleFactory = $titleFactory;
51        $this->linkBatchFactory = $linkBatchFactory;
52        $this->linkRecommendationStore = $linkRecommendationStore;
53        $this->innerProvider = $innerProvider;
54        $this->pruneRedLinks = $pruneRedLinks;
55    }
56
57    /**
58     * @inheritDoc
59     * @throws MalformedTitleException
60     */
61    public function get( LinkTarget $title, TaskType $taskType ) {
62        $recommendation = $this->innerProvider->get( $title, $taskType );
63        if ( $recommendation instanceof StatusValue ) {
64            return $recommendation;
65        }
66
67        return $this->pruneLinkRecommendation( $recommendation );
68    }
69
70    /**
71     * Remove exclusion-listed links and optionally red links from a LinkRecommendation.
72     * Returns a warning status when all links have been removed.
73     * @param LinkRecommendation $linkRecommendation
74     * @return LinkRecommendation|StatusValue
75     * @throws MalformedTitleException
76     */
77    private function pruneLinkRecommendation( LinkRecommendation $linkRecommendation ) {
78        $excludedLinkIds = $this->linkRecommendationStore->getExcludedLinkIds(
79            $linkRecommendation->getPageId(),
80            LinkRecommendationTaskType::REJECTION_EXCLUSION_LIMIT
81        );
82        $this->linkBatchFactory->newLinkBatch(
83            array_map(
84                function ( LinkRecommendationLink $link ) {
85                    return $this->titleFactory->newFromText( $link->getLinkTarget() );
86                },
87                $linkRecommendation->getLinks()
88            )
89        )->execute();
90        $goodLinks = array_filter( $linkRecommendation->getLinks(),
91            function ( LinkRecommendationLink $link ) use ( $excludedLinkIds ) {
92                $pageId = $this->titleFactory->newFromTextThrow( $link->getLinkTarget() )->getArticleID();
93                if ( $this->pruneRedLinks && !$pageId ) {
94                    return false;
95                }
96                return !in_array( $pageId, $excludedLinkIds );
97            } );
98
99        if ( !$goodLinks ) {
100            // Message used for debugging, keep it in English to reduce translator burden.
101            return StatusValue::newGood()->warning( 'rawmessage',
102                'All of the links in the recommendation have been pruned' );
103        }
104        // In most cases we could just return the original object; opt for consistency instead.
105        return new LinkRecommendation(
106            $linkRecommendation->getTitle(),
107            $linkRecommendation->getPageId(),
108            $linkRecommendation->getRevisionId(),
109            $goodLinks,
110            $linkRecommendation->getMetadata()
111        );
112    }
113
114}