Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
52 / 52
100.00% covered (success)
100.00%
6 / 6
CRAP
100.00% covered (success)
100.00%
1 / 1
ResultsFormatter
100.00% covered (success)
100.00%
52 / 52
100.00% covered (success)
100.00%
6 / 6
15
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 formatResults
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
3
 formatRow
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 formatRowProperty
100.00% covered (success)
100.00%
29 / 29
100.00% covered (success)
100.00%
1 / 1
7
 getOverlapMessageKey
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 msg
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace MediaWiki\Extension\SimilarEditors;
4
5use MediaWiki\Html\Html;
6use MediaWiki\Language\Language;
7use MediaWiki\Linker\Linker;
8use MediaWiki\Message\Message;
9use MediaWiki\User\UserFactory;
10use MediaWiki\User\UserRigorOptions;
11
12class ResultsFormatter {
13    /**
14     * Properties, corresponding to table columns
15     */
16    private const PROPERTIES = [
17        'user',
18        'day-overlap',
19        'hour-overlap',
20        'edit-overlap',
21        'inverse-edit-overlap',
22        'edits',
23    ];
24
25    public function __construct(
26        private readonly UserFactory $userFactory,
27        private readonly Language $language,
28    ) {
29    }
30
31    /**
32     * @param string $target
33     * @param Neighbor[] $neighbors
34     * @return string
35     */
36    public function formatResults( string $target, array $neighbors ): string {
37        $headCells = '';
38        foreach ( self::PROPERTIES as $property ) {
39            // For grepping. The following messages can be used here:
40            // * similareditors-results-user
41            // * similareditors-results-day-overlap
42            // * similareditors-results-hour-overlap
43            // * similareditors-results-edit-overlap
44            // * similareditors-results-inverse-edit-overlap
45            // * similareditors-results-edits
46            $headCellText = $this->msg( 'similareditors-results-' . $property )->parse();
47            $headCells .= Html::rawElement( 'th', [], $headCellText );
48        }
49        $headRow = Html::rawElement( 'tr', [], $headCells );
50        $head = Html::rawElement( 'thead', [], $headRow );
51
52        $bodyRows = '';
53        foreach ( $neighbors as $neighbor ) {
54            $bodyRows .= $this->formatRow( $target, $neighbor );
55        }
56        $body = Html::rawElement( 'tbody', [], $bodyRows );
57
58        return Html::rawElement(
59            'table',
60            [ 'class' => 'mw-datatable sortable' ],
61            $head . $body
62        );
63    }
64
65    /**
66     * @param string $target
67     * @param Neighbor $neighbor
68     * @return string
69     */
70    private function formatRow( string $target, Neighbor $neighbor ): string {
71        $row = Html::openElement( 'tr', [] );
72
73        foreach ( self::PROPERTIES as $property ) {
74            $row .= Html::rawElement( 'td', [], $this->formatRowProperty( $target, $neighbor, $property ) ) . "\n";
75        }
76
77        $row .= Html::closeElement( 'tr' );
78
79        return $row;
80    }
81
82    /**
83     * @param string $target
84     * @param Neighbor $neighbor
85     * @param string $property
86     * @return string
87     */
88    private function formatRowProperty(
89        string $target,
90        Neighbor $neighbor,
91        string $property
92    ): string {
93        switch ( $property ) {
94            case 'user':
95                $user = $this->userFactory->newFromName(
96                    $neighbor->getUserText(),
97                    // May be an IP address
98                    UserRigorOptions::RIGOR_NONE
99                );
100                // TODO: revert as part of T309675
101                return Linker::userLink( 0, 'en>' . $user->getName() ) .
102                    ' ' . $this->msg( 'parentheses-start' ) .
103                    Linker::makeExternalLink(
104                        'https://interaction-timeline.toolforge.org/' .
105                            '?wiki=enwiki&user=' . urlencode( $target ) .
106                            '&user=' . urlencode( $user->getName() ),
107                        $this->msg( 'similareditors-results-user-timeline' )->parse(),
108                        false
109                    ) .
110                    $this->msg( 'parentheses-end' );
111            case 'day-overlap':
112                return $this->msg(
113                    $this->getOverlapMessageKey( $neighbor->getDayOverlap()->getLevel() )
114                )->parse();
115            case 'hour-overlap':
116                return $this->msg(
117                    $this->getOverlapMessageKey( $neighbor->getHourOverlap()->getLevel() )
118                )->parse();
119            case 'edit-overlap':
120                return (string)$neighbor->getEditOverlap();
121            case 'inverse-edit-overlap':
122                return (string)$neighbor->getEditOverlapInv();
123            case 'edits':
124                return (string)$neighbor->getNumEditsInData();
125        }
126    }
127
128    /**
129     * @param string $level
130     * @return string
131     */
132    private function getOverlapMessageKey( string $level ): string {
133        // For grepping. The following messages can be used here:
134        // * similareditors-overlap-level-no-overlap
135        // * similareditors-overlap-level-low
136        // * similareditors-overlap-level-medium
137        // * similareditors-overlap-level-high
138        return 'similareditors-overlap-level-' . strtolower( str_replace( ' ', '-', $level ) );
139    }
140
141    /**
142     * @param string $key
143     * @param array $params
144     * @return Message
145     */
146    private function msg( string $key, array $params = [] ): Message {
147        return new Message( $key, $params, $this->language );
148    }
149}