Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
41 / 41
100.00% covered (success)
100.00%
6 / 6
CRAP
100.00% covered (success)
100.00%
1 / 1
HashtagCommentParserFactory
100.00% covered (success)
100.00%
41 / 41
100.00% covered (success)
100.00%
6 / 6
14
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 setContext
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 create
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 createWithTagTarget
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
 getDefaultTagTarget
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
4
 getInvalidList
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
6
1<?php
2namespace MediaWiki\Extension\Hashtags;
3
4use MediaWiki\ChangeTags\ChangeTagsStore;
5use MediaWiki\CommentFormatter\CommentParserFactory;
6use MediaWiki\Config\ServiceOptions;
7use MediaWiki\Context\IContextSource;
8use MediaWiki\Context\RequestContext;
9use MediaWiki\Linker\LinkRenderer;
10use MediaWiki\Linker\LinkTarget;
11use MediaWiki\SpecialPage\SpecialPageFactory;
12use MediaWiki\Title\TitleValue;
13
14class HashtagCommentParserFactory extends CommentParserFactory {
15
16    private CommentParserFactory $commentParserFactory;
17    private LinkRenderer $linkRenderer;
18    private ChangeTagsStore $changeTagsStore;
19    private bool $requireActivation;
20    private IContextSource $context;
21    private SpecialPageFactory $specialPageFactory;
22    private array $invalidList;
23    private TagCollector $tagCollector;
24
25    public const CONSTRUCTOR_OPTIONS = [
26        "HashtagsRequireActiveTag"
27    ];
28
29    public function __construct(
30        CommentParserFactory $commentParserFactory,
31        LinkRenderer $linkRenderer,
32        ChangeTagsStore $changeTagsStore,
33        SpecialPageFactory $specialPageFactory,
34        ServiceOptions $options,
35        TagCollector $tagCollector
36    ) {
37        $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
38        $this->commentParserFactory = $commentParserFactory;
39        $this->linkRenderer = $linkRenderer;
40        $this->changeTagsStore = $changeTagsStore;
41        $this->requireActivation = $options->get( 'HashtagsRequireActiveTag' );
42        $this->specialPageFactory = $specialPageFactory;
43        $this->tagCollector = $tagCollector;
44    }
45
46    public function setContext( IContextSource $context ) {
47        $this->context = $context;
48    }
49
50    /**
51     * @inheritDoc
52     * Make a HashtagCommentParser guessing the tag target based on current page
53     */
54    public function create() {
55        $target = $this->getDefaultTagTarget();
56        return $this->createWithTagTarget( $target );
57    }
58
59    /**
60     * Make a HashtagCommentParser for a specific tag link target
61     * @return CommentParser
62     */
63    public function createWithTagTarget( LinkTarget $target ) {
64        $originalObj = $this->commentParserFactory->create();
65        return new HashtagCommentParser(
66            $originalObj,
67            $this->linkRenderer,
68            $this->changeTagsStore,
69            $this->requireActivation,
70            $this->getInvalidList(),
71            $target,
72            $this->tagCollector
73        );
74    }
75
76    /**
77     * Get page to link tags to
78     */
79    private function getDefaultTagTarget(): LinkTarget {
80        if ( !isset( $this->context ) ) {
81            $this->context = RequestContext::getMain();
82        }
83        // I'm unsure how specific to go here. We could
84        // potentially try and make it go on the same page -
85        // e.g. Clicking on a tag on a history page could show
86        // just edits to that page with the tag. I think its
87        // better to go broad, just do all of RC for most
88        // things and Special:Log if we are already on Special:Log.
89        $curTitle = $this->context->getTitle();
90        // curTitle might be null during unit tests but otherwise should not be.
91        if ( $curTitle && $curTitle->isSpecial( 'Log' ) ) {
92            return new TitleValue( NS_SPECIAL, $this->specialPageFactory->getLocalNameFor( 'Log' ) );
93        }
94        return new TitleValue( NS_SPECIAL, $this->specialPageFactory->getLocalNameFor( 'Recentchanges' ) );
95    }
96
97    /**
98     * Get a list of tags not to use from the i18n message
99     *
100     * @return array [ tagname => true, ... ]
101     */
102    private function getInvalidList(): array {
103        if ( isset( $this->invalidList ) ) {
104            return $this->invalidList;
105        }
106        if ( !isset( $this->context ) ) {
107            $this->context = RequestContext::getMain();
108        }
109        $list = [];
110        $msg = $this->context->msg(
111            'hashtags-invalid-tags'
112        )->inContentLanguage()->plain();
113        $items = explode( "\n", $msg );
114        foreach ( $items as $i ) {
115            $item = trim( $i );
116            if ( substr( $item, 0, 1 ) === '#' && strpos( $item, ' ' ) === false ) {
117                $list[substr( $item, 1 )] = true;
118            }
119        }
120        $this->invalidList = $list;
121        return $this->invalidList;
122    }
123}