Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
c446e95
Initial DOMProcessor
OAuthority Aug 8, 2025
97603af
Bump version to 1.0 so we can track which functions were added when
OAuthority Aug 8, 2025
79a4dc5
Begin InfoboxTag and Controller
OAuthority Aug 8, 2025
368842e
More updates
OAuthority Aug 9, 2025
45ad372
Fix parameter name
OAuthority Aug 9, 2025
06e2fa1
change function name
OAuthority Aug 10, 2025
69f2e95
First pass at Parsoid compat render service
OAuthority Aug 10, 2025
59e2e63
fix if statement
OAuthority Aug 10, 2025
e360d7e
remove duplicate declaration
OAuthority Aug 10, 2025
7bfdafc
Further work on generating infobox template
OAuthority Aug 10, 2025
07156bf
Don't add the classes to the wrapper in this hook
OAuthority Aug 10, 2025
841d6f3
Add the modules back
OAuthority Aug 10, 2025
f4e0c7e
Use DOMCompat instead of constructing a DOMDocument
OAuthority Aug 10, 2025
9e880d5
Simplify this abit using DOMCompat
OAuthority Aug 10, 2025
c036061
Actually parse the wt2Html
OAuthority Aug 10, 2025
3698cc3
Create new parser for Parsoid
OAuthority Aug 10, 2025
aa812cd
Don't parse the wikitext here, it will be parsed later by ParsoidMed…
OAuthority Aug 11, 2025
a40626d
introduce a MediaNode specifically for Parsoid
OAuthority Aug 11, 2025
9bcc4e6
Pass the parser here
OAuthority Aug 11, 2025
4641e51
Refactor this to only check the instance once
OAuthority Aug 11, 2025
c4b3fbb
Do some hacky regex to get our galleries!
OAuthority Aug 11, 2025
f19719e
Add missing consts - this class should be refactored into an abstract…
OAuthority Aug 11, 2025
32b9fb9
New nodes
OAuthority Aug 11, 2025
44ea661
if just one image, render it
OAuthority Aug 11, 2025
8c73325
Merge pull request #1 from OAuthority/parsoid-complex
OAuthority Aug 11, 2025
0bb334c
[chore]: split a lot of the stuff into a abstract class to reduce dup…
OAuthority Aug 14, 2025
bc90daa
Move the render media function into abstract
OAuthority Aug 14, 2025
8a24cbd
Change func. name
OAuthority Aug 14, 2025
de7e5d6
Delete unused TagController
OAuthority Aug 19, 2025
f74f02a
Merge pull request #2 from OAuthority/parsoid-complex
OAuthority Aug 19, 2025
badca54
fix fatal error when an image isn't passed
OAuthority Sep 4, 2025
e108e0b
Fix null return error when a media node is unused but exists
OAuthority Sep 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions extension.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,19 @@
],
"url": "https://github.com/Universal-Omega/PortableInfobox",
"descriptionmsg": "portable-infobox-desc",
"version": "0.8",
"version": "1.0",
"type": "parserhook",
"license-name": "GPL-3.0-or-later",
"requires": {
"MediaWiki": ">= 1.43.0"
},
"config": {
"AllInfoboxesExcludedSubpages": {
"value": [ "doc", "draft", "test" ]
"value": [
"doc",
"draft",
"test"
]
},
"PortableInfoboxCacheRenderers": {
"value": false
Expand Down Expand Up @@ -125,5 +129,6 @@
"APIListModules": {
"allinfoboxes": "PortableInfobox\\Controllers\\ApiQueryAllInfoboxes"
},
"ParsoidModules": [ "PortableInfobox\\Parsoid\\InfoboxTag" ],
"manifest_version": 2
}
}
57 changes: 57 additions & 0 deletions includes/Parsoid/InfoboxTag.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

namespace PortableInfobox\Parsoid;

use Wikimedia\Parsoid\DOM\Element;
use Wikimedia\Parsoid\Ext\DOMUtils;
use Wikimedia\Parsoid\Ext\ExtensionModule;
use Wikimedia\Parsoid\Ext\ExtensionTagHandler;
use Wikimedia\Parsoid\Ext\ParsoidExtensionAPI;

class InfoboxTag extends ExtensionTagHandler implements ExtensionModule {

/**
* @inheritDoc
*/
public function getConfig(): array
{
return [
'name' => 'PortableInfobox',
'tags' => [
[
'name' => 'infobox',
'handler' => self::class
]
],
'domProcessors' => [
'PortableInfobox\\Parsoid\\PortableInfoboxDOMProcessor'
]
];
}

/**
* @inheritDoc
*/
public function sourceToDom( ParsoidExtensionAPI $api, string $src, array $args )
{
$domFragments = $api->extTagToDOM( $args, $src, [
'wrapperTag' => 'aside',
'parseOpts' => [
'extTag' => 'infobox',
'context' => 'inline'
]
]);


// this is a bit messed up as these methods are deprecated, but the documentation
// for the replacement methods doesn't exist or make sense
// this is commented out at present, as these scripts and styles will be added
// by the legacy parser (might need it here to stick these in the parser cache also?)
$api->getMetadata()->addModules( [ 'ext.PortableInfobox.scripts' ] );
$api->getMetadata()->addModuleStyles( [ 'ext.PortableInfobox.styles' ] );

// return this back. At this point, we have constructed the outer tag (<aside class=...</aside>)
// and this function is done with its work. The rest of the work will happen in the DOMProcessor
return $domFragments;
}
}
117 changes: 117 additions & 0 deletions includes/Parsoid/ParsoidMediaWikiParser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<?php

namespace PortableInfobox\Parsoid;

use MediaWiki\Title\Title;
use PortableInfobox\Services\Parser\ExternalParser;
use ReflectionClass;
use Wikimedia\Parsoid\Ext\ParsoidExtensionAPI;
use Wikimedia\Parsoid\Fragments\WikitextPFragment;
use Wikimedia\Parsoid\Utils\DOMCompat;

class ParsoidMediaWikiParser implements ExternalParser {

public ParsoidExtensionAPI $api;

public function __construct(
ParsoidExtensionAPI $api
) {
$this->api = $api;
}

public function parseRecursive( $wikitext )
{
if ( $wikitext === null ) {
return null;
}

$paramParsed = $this->api->wikitextToDOM( $wikitext, [
// this differs from earlier as we need the frame to be able to grab the
// params the user passed - parsoid handles this internally it appears
'processInNewFrame' => false,
'parseOpts' => [ 'context' => 'inline' ]
], true );

// we don't want Parsoid to wrap in a span or add a typeof here,
// just interested in the content
return DOMCompat::getOuterHTML( $paramParsed );
}

public function replaceVariables( $wikitext ) {
// no-op - I think handled by ->wikiTextToDOM?
}

public function addImage($title, array $sizeParams): ?string
{
// no-op at present. Used to extend the image tag with specific information for the PageImages extension.
// that extension relies on Parser hooks and therefore is not safe to assume that will work indefinitely.
// could potentially just do it for now whilst the hooks still exist, and maybe remove at a later date if
// PageImages is not made Parsoid-compat.
return '';
}

public function getParsoidExtensionApi(): ParsoidExtensionAPI {
return $this->api;
}

/**
* Extract the gallery and return the filename -> captions. PortableInfobox currently does this
* a lot cleaner as it piggybacks on onAfterParserFetchFileAndTitle hook to set the images into the data bag.
* This hook is NOT available on Parsoid, and we have no other way to get the resultant class which PortableInfobox
* currently relies on. So we need to fake it as best we can and hope WMF comes up with something later down
* the line.
* @param mixed $wikitext
* @return array an array of the images
*/
public function extractGallery( string $wikitext ): array
{
if ( $wikitext === null ) {
return [];
}

// the legacy implementation reuturns an array where each element is an array of the caption
// and the title object for that specific image. We don't have access to this by default,
// since there is no concept of half parsing in Parsoid - we either ask Parsoid for the Parsed wt->html
// or we work with the WT and grab what we need.
$result = [];

// this is quicker than passing the wikitext to Parsoid and extracting
// the images etc from it.
if ( preg_match( '/<gallery[^>]*>(.*?)<\/gallery>/s', $wikitext, $matches ) ) {
$galleryContent = trim( $matches[1] );

if ( empty( $galleryContent ) ) {
return [];
}

$lines = explode( "\n", $galleryContent );

foreach ( $lines as $line ) {
$line = trim( $line );

if ( empty( $line ) ) {
continue;
}

$parts = explode( '|', $line, 2 );
$filename = trim( $parts[0] );
$caption = isset( $parts[1] ) ? trim( $parts[1] ) : '';

if ( empty( $filename ) ) {
continue;
}

$title = Title::newFromText( $filename, NS_FILE );

if ( $title !== null ) {
$result[] = [
'label' => $caption,
'title' => $title
];
}
}
}

return $result;
}
}
Loading
Loading