mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Cite
synced 2024-11-23 22:45:20 +00:00
Adding "follow" functionality to the Cite extension
* Interim state commit with experimental code. * Updates to citeParserTests.txt to check now valid follow functionality and newly passing tests. * Added to follow refs, <sup style="display: none;" about=... to suppress display of hidden sups needed for VE to use in editing follow refs. * Added code to implemented follow functionality and catch invalid usage. Bug: T51538 Change-Id: Ic3ac8237fd2c490cfaf2fe799759742f72f10686
This commit is contained in:
parent
46a9900f69
commit
467b82701b
|
@ -94,9 +94,28 @@ class Ref extends ExtensionTagHandler {
|
|||
'inPHPBlock' => true
|
||||
];
|
||||
|
||||
// ref follow nodes should already have their content spans stored in followContent
|
||||
if ( is_string( $dataMw->body->html ?? null ) ) {
|
||||
// First look for the extension's content in data-mw.body.html
|
||||
$src = $extApi->htmlToWikitext( $html2wtOpts, $dataMw->body->html );
|
||||
} elseif ( isset( $dataMw->attrs->follow ) ) {
|
||||
$src = '';
|
||||
if ( $node->hasAttribute( 'about' ) ) {
|
||||
$about = $node->getAttribute( 'about' );
|
||||
$bodyElt = [];
|
||||
$followNode = DOMCompat::getElementById( $node->ownerDocument, $dataMw->body->id )
|
||||
?? null;
|
||||
if ( $followNode != null ) {
|
||||
$bodyElt = DOMCompat::querySelectorAll( $followNode,
|
||||
"span[typeof~='mw:Cite/Follow'][about='" . $about . "']" );
|
||||
}
|
||||
// ensure that $bodyElt was found
|
||||
if ( count( $bodyElt ) > 0 ) {
|
||||
$src = $extApi->domToWikitext( $html2wtOpts, $bodyElt[0], true );
|
||||
$src = ltrim( $src, ' ' );
|
||||
}
|
||||
}
|
||||
|
||||
} elseif ( is_string( $dataMw->body->id ?? null ) ) {
|
||||
// If the body isn't contained in data-mw.body.html, look if
|
||||
// there's an element pointed to by body.id.
|
||||
|
@ -145,6 +164,14 @@ class Ref extends ExtensionTagHandler {
|
|||
return ''; // Drop it!
|
||||
}
|
||||
|
||||
// Search for spans with follow content
|
||||
if ( isset( $bodyElt->firstChild->nextSibling ) &&
|
||||
DOMUtils::hasTypeOf( $bodyElt->firstChild->nextSibling, 'mw:Cite/Follow' ) ) {
|
||||
$clonedNode = $bodyElt->cloneNode();
|
||||
$clonedNode->appendChild( $bodyElt->firstChild );
|
||||
$bodyElt = $clonedNode;
|
||||
}
|
||||
|
||||
$src = $extApi->domToWikitext( $html2wtOpts, $bodyElt, true );
|
||||
} else {
|
||||
$extApi->log( 'error', 'Ref body unavailable for: ' . DOMCompat::getOuterHTML( $node ) );
|
||||
|
|
|
@ -144,17 +144,73 @@ class References extends ExtensionTagHandler {
|
|||
// FIXME(SSS): Need to clarify semantics here.
|
||||
// If both the containing <references> elt as well as the nested <ref>
|
||||
// elt has a group attribute, what takes precedence?
|
||||
$group = $refDmw->attrs->group ?? $referencesGroup ?? '';
|
||||
$groupName = $refDmw->attrs->group ?? $referencesGroup ?? '';
|
||||
|
||||
// NOTE: This will have been trimmed in Utils::getExtArgInfo()'s call
|
||||
// to TokenUtils::kvToHash() and ExtensionHandler::normalizeExtOptions()
|
||||
$refName = $refDmw->attrs->name ?? '';
|
||||
$follow = $refDmw->attrs->follow ?? '';
|
||||
|
||||
$hasRefName = strlen( $refName ) > 0;
|
||||
$hasFollow = strlen( $follow ) > 0;
|
||||
|
||||
// Add ref-index linkback
|
||||
$linkBack = $doc->createElement( 'sup' );
|
||||
|
||||
// prechecking various combinations of un-named refs, named refs, follow and error cases to
|
||||
// properly determine how to proceed with adding a ref or not and generating appropriate code
|
||||
if ( $hasRefName ) {
|
||||
if ( $hasFollow ) {
|
||||
// having both a ref name and follow is an error, the ref should be represented
|
||||
// and the follow present, but not causing any effect, but add and
|
||||
// verify follow round trips anyway
|
||||
$debugging = 0;
|
||||
} else {
|
||||
// normal named ref, so add normally
|
||||
$debugging = 1;
|
||||
}
|
||||
} else {
|
||||
// Either an un-named ref or a follow
|
||||
if ( $hasFollow ) {
|
||||
// Is a follow ref, so check if a named ref has already been defined
|
||||
$group = $refsData->getRefGroup( $groupName, true );
|
||||
if ( isset( $group->indexByName[$follow] ) ) {
|
||||
// Yes the named ref is defined, so lets check if it has defined content
|
||||
$ref = $group->indexByName[$follow];
|
||||
if ( $ref->contentId ) {
|
||||
// The named ref did define content, so attach follow content normally
|
||||
$debugging = 2;
|
||||
} else {
|
||||
// The follow content follows a named ref that did not define content
|
||||
// and therefore the follow content cannot be appended and must be
|
||||
// handled differently
|
||||
$debugging = 3;
|
||||
}
|
||||
} else {
|
||||
// this follow precedes a named ref being defined or possibly never being defined
|
||||
// which is a type of error but still must round trip, so we must handle this case
|
||||
// with special code and not call the add function.
|
||||
$debugging = 4;
|
||||
|
||||
$html = $extApi->domToHtml( $c, true, true );
|
||||
$refDmw->body = (object)[ 'html' => $html ];
|
||||
DOMDataUtils::setDataMw( $linkBack, $refDmw );
|
||||
DOMUtils::addTypeOf( $linkBack, 'mw:Extension/ref mw:Error' );
|
||||
$node->parentNode->replaceChild( $linkBack, $node );
|
||||
return;
|
||||
// do not define a ref
|
||||
|
||||
}
|
||||
// Is an unnamed ref and not a follow, so add the ref normally
|
||||
} else {
|
||||
// normal un-named ref, so add normally
|
||||
$debugging = 5;
|
||||
}
|
||||
}
|
||||
|
||||
$ref = $refsData->add(
|
||||
$extApi, $group, $refName, $about, $nestedInReferences, $linkBack
|
||||
$extApi, $groupName, $refName, $follow, $contentId, $about, $nestedInReferences,
|
||||
$linkBack
|
||||
);
|
||||
|
||||
$errs = [];
|
||||
|
@ -163,6 +219,10 @@ class References extends ExtensionTagHandler {
|
|||
// FIXME: See T260082 for a more complete description of cause and deeper fix
|
||||
$missingContent = ( !empty( $cDp->empty ) || trim( $refDmw->body->extsrc ?? '' ) === '' );
|
||||
|
||||
if ( $refName !== '' && strlen( $follow ) > 0 ) {
|
||||
$errs[] = [ 'key' => 'cite_error_ref_too_many_keys' ];
|
||||
}
|
||||
|
||||
if ( $missingContent ) {
|
||||
// Check for missing name and content to generate error code
|
||||
if ( $refName === '' ) {
|
||||
|
@ -203,6 +263,10 @@ class References extends ExtensionTagHandler {
|
|||
}
|
||||
|
||||
$lastLinkback = $ref->linkbacks[count( $ref->linkbacks ) - 1] ?? null;
|
||||
|
||||
if ( strlen( $follow ) > 0 ) {
|
||||
$linkBack->setAttribute( 'style', 'display: none;' );
|
||||
}
|
||||
DOMUtils::addAttributes( $linkBack, [
|
||||
'about' => $about,
|
||||
'class' => 'mw-ref',
|
||||
|
@ -215,7 +279,6 @@ class References extends ExtensionTagHandler {
|
|||
if ( count( $errs ) > 0 ) {
|
||||
DOMUtils::addTypeOf( $linkBack, 'mw:Error' );
|
||||
}
|
||||
|
||||
$dataParsoid = new stdClass;
|
||||
if ( isset( $nodeDp->src ) ) {
|
||||
$dataParsoid->src = $nodeDp->src;
|
||||
|
@ -256,8 +319,11 @@ class References extends ExtensionTagHandler {
|
|||
'[' . ( $ref->group ? $ref->group . ' ' : '' ) . $ref->groupIndex . ']'
|
||||
)
|
||||
);
|
||||
$refLink->appendChild( $refLinkSpan );
|
||||
$linkBack->appendChild( $refLink );
|
||||
|
||||
if ( strlen( $follow ) == 0 || count( $errs ) > 0 ) {
|
||||
$refLink->appendChild( $refLinkSpan );
|
||||
$linkBack->appendChild( $refLink );
|
||||
}
|
||||
|
||||
$node->parentNode->replaceChild( $linkBack, $node );
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace Wikimedia\Parsoid\Ext\Cite;
|
|||
|
||||
use DOMElement;
|
||||
use stdClass;
|
||||
use Wikimedia\Parsoid\Ext\DOMUtils;
|
||||
use Wikimedia\Parsoid\Ext\ParsoidExtensionAPI;
|
||||
|
||||
class ReferencesData {
|
||||
|
@ -53,14 +54,16 @@ class ReferencesData {
|
|||
* @param ParsoidExtensionAPI $extApi
|
||||
* @param string $groupName
|
||||
* @param string $refName
|
||||
* @param string $follow
|
||||
* @param string $contentId
|
||||
* @param string $about
|
||||
* @param bool $skipLinkback
|
||||
* @param DOMElement $linkBack
|
||||
* @return stdClass
|
||||
*/
|
||||
public function add(
|
||||
ParsoidExtensionAPI $extApi, string $groupName, string $refName,
|
||||
string $about, bool $skipLinkback, DOMElement $linkBack
|
||||
ParsoidExtensionAPI $extApi, string $groupName, string $refName, string $follow,
|
||||
string $contentId, string $about, bool $skipLinkback, DOMElement $linkBack
|
||||
): stdClass {
|
||||
$group = $this->getRefGroup( $groupName, true );
|
||||
// Looks like Cite.php doesn't try to fix ids that already have
|
||||
|
@ -72,6 +75,41 @@ class ReferencesData {
|
|||
// in Parsoid: ExtensionHandler#normalizeExtOptions
|
||||
$refName = $extApi->sanitizeHTMLId( $refName );
|
||||
$hasRefName = strlen( $refName ) > 0;
|
||||
$hasFollow = strlen( $follow ) > 0;
|
||||
|
||||
// Is this a follow ref that has been preceeded by a named ref which defined
|
||||
// content or did not define content, or a self closed named ref without content
|
||||
if ( !$hasRefName && $hasFollow && isset( $group->indexByName[$follow] ) ) {
|
||||
$ref = $group->indexByName[$follow];
|
||||
$contentSup = $extApi->getContentDOM( $contentId );
|
||||
$ownerDoc = $contentSup->ownerDocument;
|
||||
$span = $ownerDoc->createElement( 'span' );
|
||||
DOMUtils::addTypeOf( $span, 'mw:Cite/Follow' );
|
||||
$span->setAttribute( 'about', $about );
|
||||
$spaceNode = $ownerDoc->createTextNode( ' ' );
|
||||
$span->appendChild( $spaceNode );
|
||||
DOMUtils::migrateChildren( $contentSup, $span );
|
||||
|
||||
// contentSup is now empty and can be used as a container for migrate children
|
||||
$contentSup->appendChild( $span );
|
||||
|
||||
// If the named ref has defined content
|
||||
if ( $ref->contentId ) {
|
||||
$refContent = $extApi->getContentDOM( $ref->contentId );
|
||||
ParsoidExtensionAPI::migrateChildrenBetweenDocs( $contentSup, $refContent, false );
|
||||
} else {
|
||||
// Otherwise we have a follow that comes after named ref without content
|
||||
// So create a sup in a fragment, set that into the environment and migrate
|
||||
// the follow content into the fragment
|
||||
$ref->contentId = $contentId;
|
||||
}
|
||||
return $ref;
|
||||
}
|
||||
|
||||
// Must check for error case where $hasRefName and $hasFollow are both present
|
||||
// which still needs to create the ref, but also flag it with an error due to
|
||||
// [ 'key' => 'cite_error_ref_too_many_keys' ]; as trapped in References.php
|
||||
// but which needs to preserve the extra key for round tripping
|
||||
|
||||
if ( $hasRefName && isset( $group->indexByName[$refName] ) ) {
|
||||
$ref = $group->indexByName[$refName];
|
||||
|
|
Loading…
Reference in a new issue