Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
129 / 129 |
|
100.00% |
7 / 7 |
CRAP | |
100.00% |
1 / 1 |
HtmlTalkPageResolutionView | |
100.00% |
129 / 129 |
|
100.00% |
7 / 7 |
13 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getHtml | |
100.00% |
55 / 55 |
|
100.00% |
1 / 1 |
6 | |||
wrapRow | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
buildOrderSelector | |
100.00% |
33 / 33 |
|
100.00% |
1 / 1 |
1 | |||
buildConflictingTalkRow | |
100.00% |
20 / 20 |
|
100.00% |
1 / 1 |
1 | |||
buildCopyRow | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
1 | |||
getMessageBox | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | namespace TwoColConflict\Html; |
4 | |
5 | use MediaWiki\Html\Html; |
6 | use MessageLocalizer; |
7 | use OOUI\ButtonWidget; |
8 | use OOUI\FieldLayout; |
9 | use OOUI\FieldsetLayout; |
10 | use OOUI\HtmlSnippet; |
11 | use OOUI\MessageWidget; |
12 | use OOUI\RadioInputWidget; |
13 | use TwoColConflict\SplitConflictUtils; |
14 | |
15 | /** |
16 | * TODO: Clean up, maybe CSS class names should match change type, and "split" replaced with |
17 | * "single" where appropriate. |
18 | * |
19 | * @license GPL-2.0-or-later |
20 | */ |
21 | class HtmlTalkPageResolutionView { |
22 | |
23 | private HtmlEditableTextComponent $editableTextComponent; |
24 | private MessageLocalizer $messageLocalizer; |
25 | |
26 | public function __construct( |
27 | HtmlEditableTextComponent $editableTextComponent, |
28 | MessageLocalizer $messageLocalizer |
29 | ) { |
30 | $this->editableTextComponent = $editableTextComponent; |
31 | $this->messageLocalizer = $messageLocalizer; |
32 | } |
33 | |
34 | /** |
35 | * @param array[] $unifiedDiff A list of changes as created by the AnnotatedHtmlDiffFormatter |
36 | * @param int $otherIndex |
37 | * @param int $yourIndex |
38 | * @param bool $isBetaFeature |
39 | * |
40 | * @return string HTML |
41 | */ |
42 | public function getHtml( |
43 | array $unifiedDiff, |
44 | int $otherIndex, |
45 | int $yourIndex, |
46 | bool $isBetaFeature |
47 | ): string { |
48 | $out = $this->getMessageBox( |
49 | 'twocolconflict-talk-header-overview', 'error', 'mw-twocolconflict-overview' ); |
50 | $hintMsg = $isBetaFeature ? |
51 | 'twocolconflict-split-header-hint-beta' : 'twocolconflict-split-header-hint'; |
52 | $out .= $this->getMessageBox( $hintMsg, 'notice' ); |
53 | |
54 | $rows = ''; |
55 | foreach ( $unifiedDiff as $i => $changeSet ) { |
56 | $text = $changeSet['copytext'] ?? $changeSet['newtext']; |
57 | switch ( $i ) { |
58 | case $otherIndex: |
59 | $rows .= $this->buildConflictingTalkRow( |
60 | $text, |
61 | $i, |
62 | 'delete', |
63 | 'other', |
64 | true, |
65 | 'twocolconflict-talk-conflicting' |
66 | ); |
67 | |
68 | $rows .= Html::rawElement( |
69 | 'div', |
70 | [ 'class' => 'mw-twocolconflict-single-swap-button-container' ], |
71 | new ButtonWidget( [ |
72 | 'infusable' => true, |
73 | 'framed' => true, |
74 | 'icon' => 'markup', |
75 | 'title' => $this->messageLocalizer->msg( |
76 | 'twocolconflict-talk-switch-tooltip' |
77 | )->text(), |
78 | 'classes' => [ 'mw-twocolconflict-single-swap-button' ], |
79 | 'tabIndex' => '1' |
80 | ] ) |
81 | ); |
82 | |
83 | break; |
84 | case $yourIndex: |
85 | $rows .= $this->buildConflictingTalkRow( |
86 | $text, |
87 | $i, |
88 | 'add', |
89 | 'your', |
90 | false, |
91 | 'twocolconflict-talk-your' |
92 | ); |
93 | break; |
94 | default: |
95 | $rows .= $this->buildCopyRow( $text, $i ); |
96 | } |
97 | } |
98 | // this will allow CSS formatting with :first-of-type |
99 | $out .= Html::rawElement( |
100 | 'div', |
101 | [ 'class' => 'mw-twocolconflict-single-column-rows' ], |
102 | $rows |
103 | ); |
104 | |
105 | $out .= $this->buildOrderSelector() . |
106 | Html::hidden( 'mw-twocolconflict-single-column-view', true ); |
107 | |
108 | return Html::rawElement( |
109 | 'div', |
110 | [ 'class' => 'mw-twocolconflict-split-view mw-twocolconflict-single-column-view' ], |
111 | $out |
112 | ); |
113 | } |
114 | |
115 | private function wrapRow( string $html, bool $isConflicting = false ): string { |
116 | $class = [ 'mw-twocolconflict-single-row' ]; |
117 | if ( $isConflicting ) { |
118 | $class[] = 'mw-twocolconflict-conflicting-talk-row'; |
119 | } |
120 | return Html::rawElement( 'div', [ 'class' => $class ], $html ); |
121 | } |
122 | |
123 | private function buildOrderSelector(): string { |
124 | $out = new FieldsetLayout( [ |
125 | 'label' => $this->messageLocalizer->msg( 'twocolconflict-talk-reorder-prompt' )->text(), |
126 | 'items' => [ |
127 | new FieldLayout( |
128 | new RadioInputWidget( [ |
129 | 'name' => 'mw-twocolconflict-reorder', |
130 | 'value' => 'reverse', |
131 | 'tabIndex' => '1', |
132 | ] ), |
133 | [ |
134 | 'align' => 'inline', |
135 | 'label' => $this->messageLocalizer->msg( 'twocolconflict-talk-reverse-order' )->text(), |
136 | ] |
137 | ), |
138 | new FieldLayout( |
139 | new RadioInputWidget( [ |
140 | 'name' => 'mw-twocolconflict-reorder', |
141 | 'value' => 'no-change', |
142 | 'selected' => true, |
143 | 'tabIndex' => '1', |
144 | ] ), |
145 | [ |
146 | 'align' => 'inline', |
147 | 'label' => $this->messageLocalizer->msg( 'twocolconflict-talk-same-order' )->text(), |
148 | ] |
149 | ), |
150 | ], |
151 | ] ); |
152 | |
153 | return Html::rawElement( |
154 | 'div', |
155 | [ 'class' => 'mw-twocolconflict-order-selector' ], |
156 | $out |
157 | ); |
158 | } |
159 | |
160 | private function buildConflictingTalkRow( |
161 | string $rawText, |
162 | int $rowNum, |
163 | string $classSuffix, |
164 | string $changeType, |
165 | bool $isDisabled, |
166 | string $conflictingTalkLabel |
167 | ): string { |
168 | $out = Html::rawElement( |
169 | 'div', |
170 | [ 'class' => 'mw-twocolconflict-conflicting-talk-label' ], |
171 | Html::rawElement( |
172 | 'span', |
173 | [], |
174 | Html::element( |
175 | 'span', |
176 | [ 'class' => 'mw-twocolconflict-split-' . $classSuffix ], |
177 | $this->messageLocalizer->msg( $conflictingTalkLabel )->text() |
178 | ) |
179 | ) |
180 | ); |
181 | |
182 | $out .= Html::rawElement( |
183 | 'div', |
184 | [ 'class' => 'mw-twocolconflict-split-' . $classSuffix . ' mw-twocolconflict-single-column' ], |
185 | $this->editableTextComponent->getHtml( |
186 | htmlspecialchars( $rawText ), $rawText, $rowNum, $changeType, $isDisabled ) |
187 | ); |
188 | return $this->wrapRow( $out, true ); |
189 | } |
190 | |
191 | private function buildCopyRow( |
192 | string $rawText, |
193 | int $rowNum |
194 | ): string { |
195 | $out = Html::rawElement( |
196 | 'div', |
197 | [ 'class' => 'mw-twocolconflict-split-copy mw-twocolconflict-single-column' ], |
198 | $this->editableTextComponent->getHtml( |
199 | htmlspecialchars( $rawText ), $rawText, $rowNum, 'copy', true ) |
200 | ); |
201 | return $this->wrapRow( $out ); |
202 | } |
203 | |
204 | private function getMessageBox( string $messageKey, string $type, string ...$classes ): string { |
205 | $html = $this->messageLocalizer->msg( $messageKey )->parse(); |
206 | // Force feedback links to be opened in a new tab, and not lose the edit |
207 | $html = SplitConflictUtils::addTargetBlankToLinks( $html ); |
208 | return ( new MessageWidget( [ |
209 | 'label' => new HtmlSnippet( $html ), |
210 | 'type' => $type, |
211 | ] ) ) |
212 | ->addClasses( [ 'mw-twocolconflict-messageWidget', ...$classes ] ) |
213 | ->toString(); |
214 | } |
215 | |
216 | } |