-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathTabberTransclude.php
213 lines (188 loc) · 6.28 KB
/
TabberTransclude.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
<?php
/**
* TabberNeue
* TabberTransclude Class
* Implement <tabbertransclude> tag
*
* @package TabberNeue
* @author alistair3149, Eric Fortin, Alexia E. Smith, Ciencia Al Poder
* @license GPL-3.0-or-later
* @link https://www.mediawiki.org/wiki/Extension:TabberNeue
*/
declare( strict_types=1 );
namespace MediaWiki\Extension\TabberNeue;
use Exception;
use MediaWiki\MediaWikiServices;
use Parser;
use PPFrame;
use Sanitizer;
use TemplateParser;
use Title;
class TabberTransclude {
/** @var bool */
private static $useLegacyId = false;
/**
* Parser callback for <tabbertransclude> tag
*
* @param string|null $input
* @param array $args
* @param Parser $parser Mediawiki Parser Object
* @param PPFrame $frame Mediawiki PPFrame Object
*
* @return string HTML
*/
public static function parserHook( ?string $input, array $args, Parser $parser, PPFrame $frame ) {
if ( $input === null ) {
return '';
}
$config = MediaWikiServices::getInstance()->getMainConfig();
$parserOutput = $parser->getOutput();
self::$useLegacyId = $config->get( 'TabberNeueUseLegacyTabIds' );
$count = count( $parserOutput->getExtensionData( 'tabber-count' ) ?? [] );
$html = self::render( $input, $count, $args, $parser, $frame );
$parser->getOutput()->addModuleStyles( [ 'ext.tabberNeue.init.styles' ] );
$parser->getOutput()->addModules( [ 'ext.tabberNeue' ] );
$parser->addTrackingCategory( 'tabberneue-tabbertransclude-category' );
return $html;
}
/**
* Renders the necessary HTML for a <tabbertransclude> tag.
*
* @param string $input The input URL between the beginning and ending tags.
* @param int $count Current Tabber count
* @param array $args
* @param Parser $parser Mediawiki Parser Object
* @param PPFrame $frame Mediawiki PPFrame Object
*
* @return string HTML
*/
public static function render( string $input, int $count, array $args, Parser $parser, PPFrame $frame ): string {
$selected = true;
$arr = explode( "\n", $input );
$data = [
'id' => isset( $args['id'] ) ? $args['id'] : "tabber-$count",
'class' => isset( $args['class'] ) ? $args['class'] : '',
'array-tabs' => []
];
foreach ( $arr as $tab ) {
$tabData = self::getTabData( $tab );
if ( $tabData === [] ) {
continue;
}
$tabpanelHtml = '';
try {
$tabpanelHtml = self::buildTabTransclude( $tabData, $parser, $frame, $selected );
} catch ( Exception $e ) {
// This can happen if a $currentTitle is null
continue;
}
$data['array-tabs'][] = [
'html-tabpanel' => $tabpanelHtml,
'label' => $tabData['label'],
'tabId' => "tabber-tab-{$tabData['id']}",
'tabpanelId' => self::$useLegacyId ? $tabData['id'] : "tabber-tabpanel-{$tabData['id']}"
];
}
$templateParser = new TemplateParser( __DIR__ . '/templates' );
return $templateParser->processTemplate( 'Tabber', $data );
}
/**
* Get individual tab data from wikitext.
*
* @param string $tab tab wikitext
*
* @return array
*/
private static function getTabData( string $tab ): array {
$data = [];
if ( empty( trim( $tab ) ) ) {
return $data;
}
// Transclude uses a different syntax: Page name|Tab label
// Use array_pad to make sure at least 2 array values are always returned
[ $content, $label ] = array_pad( explode( '|', $tab, 2 ), 2, '' );
$data['label'] = trim( $label );
// Label is empty, we cannot generate tabber
if ( $data['label'] === '' ) {
return $data;
}
$data['content'] = trim( $content );
$data['id'] = Sanitizer::escapeIdForAttribute( htmlspecialchars( $data['label'] ) );
return $data;
}
/**
* Build individual tab.
*
* @param array $tabData Tab data
* @param Parser $parser Mediawiki Parser Object
* @param PPFrame $frame Mediawiki PPFrame Object
* @param bool &$selected The tab is the selected one
*
* @return string HTML
* @throws Exception
*/
private static function buildTabTransclude( array $tabData, Parser $parser, PPFrame $frame, bool &$selected ): string {
$tabName = $tabData['label'];
$pageName = $tabData['content'];
$dataProps = [];
$title = Title::newFromText( trim( $pageName ) );
if ( !$title ) {
if ( empty( $tabName ) ) {
$tabName = $pageName;
}
$tabBody = sprintf( '<div class="error">Invalid title: %s</div>', $pageName );
} else {
$pageName = $title->getPrefixedText();
if ( empty( $tabName ) ) {
$tabName = $pageName;
}
$dataProps['page-title'] = $pageName;
if ( $selected ) {
$tabBody = $parser->recursiveTagParseFully(
sprintf( '{{:%s}}', $pageName ),
$frame
);
} else {
$service = MediaWikiServices::getInstance();
// Add a link placeholder, as a fallback if JavaScript doesn't execute
$linkRenderer = $service->getLinkRenderer();
$tabBody = sprintf(
'<div class="tabber__transclusion">%s</div>',
$linkRenderer->makeLink( $title, null, [ 'rel' => 'nofollow' ] )
);
$currentTitle = $parser->getPage();
$query = sprintf(
'?action=parse&format=json&formatversion=2&title=%s&text={{:%s}}&redirects=1&prop=text&disablelimitreport=1&disabletoc=1&wrapoutputclass=',
urlencode( $currentTitle->getPrefixedText() ),
urlencode( $pageName )
);
$utils = $service->getUrlUtils();
$utils->expand( wfScript( 'api' ) . $query, PROTO_CANONICAL );
$dataProps['load-url'] = $utils->expand( wfScript( 'api' ) . $query, PROTO_CANONICAL );
$oldTabBody = $tabBody;
// Allow extensions to update the lazy loaded tab
$service->getHookContainer()->run(
'TabberNeueRenderLazyLoadedTab',
[ &$tabBody, &$dataProps, $parser, $frame ]
);
if ( $oldTabBody !== $tabBody ) {
$parser->getOutput()->recordOption( 'tabberneuelazyupdated' );
}
}
// Register as a template
$revRecord = $parser->fetchCurrentRevisionRecordOfTitle( $title );
$parser->getOutput()->addTemplate(
$title,
$title->getArticleId(),
$revRecord ? $revRecord->getId() : null
);
}
$tab = '<article id="tabber-tabpanel-' . $tabData['id'] . '" class="tabber__panel" data-mw-tabber-title="' . htmlspecialchars( $tabName ) . '"';
$tab .= implode( array_map( static function ( $prop, $value ) {
return sprintf( ' data-mw-tabber-%s="%s"', $prop, htmlspecialchars( $value ) );
}, array_keys( $dataProps ), $dataProps ) );
$tab .= '>' . $tabBody . '</article>';
$selected = false;
return $tab;
}
}