Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
35 / 35
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
FootnoteMarkFormatter
100.00% covered (success)
100.00%
35 / 35
100.00% covered (success)
100.00%
3 / 3
12
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 linkRef
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
6
 fetchCustomizedLinkLabel
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
5
1<?php
2
3namespace Cite;
4
5use MediaWiki\Parser\Sanitizer;
6use Parser;
7
8/**
9 * Footnote markers in the context of the Cite extension are the numbers in the article text, e.g.
10 * [1], that can be hovered or clicked to be able to read the attached footnote.
11 *
12 * @license GPL-2.0-or-later
13 */
14class FootnoteMarkFormatter {
15
16    /** @var array<string,string[]> In-memory cache for the cite_link_label_group-… link label lists */
17    private array $linkLabels = [];
18
19    private AnchorFormatter $anchorFormatter;
20    private ErrorReporter $errorReporter;
21    private ReferenceMessageLocalizer $messageLocalizer;
22
23    public function __construct(
24        ErrorReporter $errorReporter,
25        AnchorFormatter $anchorFormatter,
26        ReferenceMessageLocalizer $messageLocalizer
27    ) {
28        $this->anchorFormatter = $anchorFormatter;
29        $this->errorReporter = $errorReporter;
30        $this->messageLocalizer = $messageLocalizer;
31    }
32
33    /**
34     * Generate a link (<sup ...) for the <ref> element from a key
35     * and return XHTML ready for output
36     *
37     * @suppress SecurityCheck-DoubleEscaped
38     * @param Parser $parser
39     * @param string $group
40     * @param ReferenceStackItem $ref
41     *
42     * @return string
43     */
44    public function linkRef( Parser $parser, string $group, ReferenceStackItem $ref ): string {
45        $label = $this->fetchCustomizedLinkLabel( $parser, $group, $ref->number );
46        if ( $label === null ) {
47            $label = $this->messageLocalizer->localizeDigits( (string)$ref->number );
48            if ( $group !== Cite::DEFAULT_GROUP ) {
49                $label = "$group $label";
50            }
51        }
52        if ( isset( $ref->extendsIndex ) ) {
53            $label .= '.' . $this->messageLocalizer->localizeDigits( (string)$ref->extendsIndex );
54        }
55
56        $key = $ref->name ?? $ref->key;
57        // TODO: Use count without decrementing.
58        $count = $ref->name ? $ref->key . '-' . ( $ref->count - 1 ) : null;
59        $subkey = $ref->name ? '-' . $ref->key : null;
60
61        return $parser->recursiveTagParse(
62            $this->messageLocalizer->msg(
63                'cite_reference_link',
64                $this->anchorFormatter->backLinkTarget( $key, $count ),
65                $this->anchorFormatter->jumpLink( $key . $subkey ),
66                Sanitizer::safeEncodeAttribute( $label )
67            )->plain()
68        );
69    }
70
71    /**
72     * Generate a custom format link for a group given an offset, e.g.
73     * the second <ref group="foo"> is b if $this->mLinkLabels["foo"] =
74     * [ 'a', 'b', 'c', ...].
75     * Return an error if the offset > the # of array items
76     *
77     * @param Parser $parser
78     * @param string $group The group name
79     * @param int $number Expected to start at 1
80     *
81     * @return string|null Returns null if no custom labels for this group exist
82     */
83    private function fetchCustomizedLinkLabel( Parser $parser, string $group, int $number ): ?string {
84        if ( $group === Cite::DEFAULT_GROUP ) {
85            return null;
86        }
87
88        $message = "cite_link_label_group-$group";
89        if ( !array_key_exists( $group, $this->linkLabels ) ) {
90            $msg = $this->messageLocalizer->msg( $message );
91            $this->linkLabels[$group] = $msg->isDisabled() ? [] : preg_split( '/\s+/', $msg->plain() );
92        }
93
94        // Expected behavior for groups without custom labels
95        if ( !$this->linkLabels[$group] ) {
96            return null;
97        }
98
99        // Error message in case we run out of custom labels
100        return $this->linkLabels[$group][$number - 1] ?? $this->errorReporter->plain(
101            $parser,
102            'cite_error_no_link_label_group',
103            $group,
104            $message
105        );
106    }
107
108}