Skip to content

Commit

Permalink
feat: replace mediawiki-core slot render mods with hook
Browse files Browse the repository at this point in the history
Refs: #14
  • Loading branch information
simontaurus committed Nov 18, 2024
1 parent 3bc2f27 commit c75f2db
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 2 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ See also [T324933](https://phabricator.wikimedia.org/T324933)
"wgMwJsonAiCompletionApiUrl": {
"value": null,
"description": "REST-API endpoint acception {\"promt\": \"...\", \"jsonschema\": \"\"} and returning a valide schema instance."
},
"wgMwJsonOrderSlotRenderResults": {
"value": false,
"description": "Brings the render results of the slots into order 'header', 'main', 'footer', <additional slots>."
},
"wgMwJsonWrapSlotRenderResults": {
"value": false,
"description": "Wraps the render results of the slots in a div element"
}
}
```
Expand Down
11 changes: 10 additions & 1 deletion extension.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"manifest_version":2,
"name":"MwJson",
"version":"0.43.0",
"version":"0.44.0",
"author":[
"[https://github.com/simontaurus Simon Stier]"
],
Expand Down Expand Up @@ -247,6 +247,7 @@
},
"Hooks":{
"BeforePageDisplay":"MwJson::onBeforePageDisplay",
"OutputPageParserOutput": "MwJson::onOutputPageParserOutput",
"ResourceLoaderGetConfigVars": "MwJson::onResourceLoaderGetConfigVars"
},
"SpecialPages": {
Expand All @@ -260,6 +261,14 @@
"MwJsonAiCompletionApiUrl": {
"value": null,
"description": "REST-API endpoint acception {\"promt\": \"...\", \"jsonschema\": \"\"} and returning a valide schema instance."
},
"MwJsonOrderSlotRenderResults": {
"value": false,
"description": "Brings the render results of the slots into order 'header', 'main', 'footer', <additional slots>."
},
"MwJsonWrapSlotRenderResults": {
"value": false,
"description": "Wraps the render results of the slots in a div element"
}
}
}
135 changes: 134 additions & 1 deletion includes/MwJson.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
<?php

use MediaWiki\MediaWikiServices;

class MwJson {

public static function onBeforePageDisplay( $out ) {
public static function onBeforePageDisplay( $out, $skin ) {

$out->addModules( 'ext.MwJson' );

Expand All @@ -16,4 +18,135 @@ public static function onResourceLoaderGetConfigVars( array &$vars, $skin, Confi
$vars['wgMwJsonAiCompletionApiUrl'] = $config->get( 'MwJsonAiCompletionApiUrl' );
}

public static function onOutputPageParserOutput($out, $parserOutput) {
return MwJson::transformSlotRenderResults($out, $parserOutput);
}

protected static function transformSlotRenderResults($out, $parserOutput)
{
//return;
$config = MediaWikiServices::getInstance()->getMainConfig();
if ( !( $config->get( 'MwJsonOrderSlotRenderResults' ) )
&& !( $config->get( 'MwJsonWrapSlotRenderResults' ) ) ) return;

//$wgHooks['BeforePageDisplay'][] = function ( $out, $skin ) {
// get the HTML from the parser output when using hook OutputPageParserOutput
$html = $parserOutput->getText();
// get the HTML from the output when using hook BeforePageDisplay
//$html = $out->getHtml();

// Ensure the HTML is properly encoded in UTF-8
$html = mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8');

// Note: this manipulation interactes with Skin:Citizen if wgCitizenEnableCollapsibleSections is true
// The behavior of those section is correct but the elements are located in the wrong wrapper
// which is not a major as long as those wrappers are not visible
$wrap = $config->get( 'MwJsonWrapSlotRenderResults' );

// Use DOMDocument to parse the HTML
$dom = new DOMDocument('1.0', 'UTF-8');
@$dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);

// Use XPath to find the target div
$xpath = new DOMXPath($dom);
$parserOutputDivs = $xpath->query('//div[contains(@class, "mw-parser-output")]');

foreach ($parserOutputDivs as $div) {
$slotHeaders = $xpath->query('.//*[contains(@class, "mw-slot-header")]', $div);

if ($slotHeaders->length > 0) {
$slots = ['main' => []];
$currentSlot = 'main';

// Iterate over all child nodes of the div
foreach ($div->childNodes as $child) {
if ($child->nodeType === XML_ELEMENT_NODE && $child->hasAttributes() && $child->getAttribute('class') === 'mw-slot-header') {
// Create a new slot with the text content of the mw-slot-header
$currentSlot = trim($child->textContent);
$slots[$currentSlot] = [];
} else {
// Add the child to the current slot
$slots[$currentSlot][] = $child;
}
}

// Create a new document fragment to hold the wrapped slots
$newFragment = $dom->createDocumentFragment();

// Define the order of slots
$orderedSlots = [];
if ($config->get( 'MwJsonOrderSlotRenderResults')) $orderedSlots = ['header', 'main', 'footer'];

// Append slots in the defined order
foreach ($orderedSlots as $slotName) {
if (isset($slots[$slotName])) {
if ($wrap) {
$wrapperDiv = $dom->createElement('div');
$wrapperDiv->setAttribute('id', 'mw-slot-wrapper-' . htmlspecialchars($slotName));
$wrapperDiv->setAttribute('class', 'mw-slot-wrapper');
// debug
//$wrapperDiv->setAttribute('style', 'border: 1px solid #aaa;');
//$wrapperDiv->appendChild($dom->createElement('p', '>' . htmlspecialchars($slotName)));

foreach ($slots[$slotName] as $element) {
$wrapperDiv->appendChild($element);
}

$newFragment->appendChild($wrapperDiv);
} else {
foreach ($slots[$slotName] as $element) {
$newFragment->appendChild($element);
}
}
}
}

// Append any remaining slots that are not in the predefined order
foreach ($slots as $slotName => $elements) {
if (!in_array($slotName, $orderedSlots)) {

$details = $dom->createElement('details');
$details->setAttribute('class', 'mw-slot-details');
$summary = $dom->createElement('summary', htmlspecialchars($slotName));
$summary->setAttribute('class', 'mw-slot-details-summary');
$details->appendChild($summary);

if ($wrap) {
$wrapperDiv = $dom->createElement('div');
$wrapperDiv->setAttribute('id', 'mw-slot-wrapper-' . htmlspecialchars($slotName));
$wrapperDiv->setAttribute('class', 'mw-slot-wrapper');
// debug
//$wrapperDiv->setAttribute('style', 'border: 1px solid #aaa;');
//$wrapperDiv->appendChild($dom->createElement('p', '>' . htmlspecialchars($slotName)));

foreach ($elements as $element) {
$details->appendChild($element);
}

$wrapperDiv->appendChild($details);
$newFragment->appendChild($wrapperDiv);
} else {
foreach ($elements as $element) {
$details->appendChild($element);
}
$newFragment->appendChild($details);
}
}
}

// Replace the original content with the new wrapped content
while ($div->firstChild) {
$div->removeChild($div->firstChild);
}
$div->appendChild($newFragment);
}
}

// Save the modified HTML back to the parser output when using OutputPageParserOutput
$parserOutput->setText($dom->saveHTML());
// when using BeforePageDisplay
//$out->clearHtml();
//$out->addHtml( $dom->saveHTML() );
return true;
}
}

0 comments on commit c75f2db

Please sign in to comment.