38 const DEFAULT_GROUP =
'';
44 const MAX_STORAGE_LENGTH = 65535;
49 const EXT_DATA_KEY =
'Cite:References';
54 const DATA_VERSION_NUMBER = 1;
59 const CACHE_DURATION_ONPARSE = 3600;
64 const CACHE_DURATION_ONFETCH = 18000;
104 private $mOutCnt = 0;
109 private $mGroupCnt = [];
117 private $mCallCnt = 0;
126 private $mBacklinkLabels;
133 private $mLinkLabels = [];
146 private $mHaveAfterParse =
false;
154 public $mInCite =
false;
162 public $mInReferences =
false;
169 private $mReferencesErrors = [];
176 private $mReferencesGroup =
'';
185 private $mRefCallStack = [];
190 private $mBumpRefData =
false;
196 private static $hooksInstalled =
false;
209 if ( $this->mInCite ) {
210 return htmlspecialchars(
"<ref>$str</ref>" );
214 $this->mInCite =
true;
218 $this->mInCite =
false;
220 $parserOutput =
$parser->getOutput();
221 $parserOutput->addModules(
'ext.cite.a11y' );
222 $parserOutput->addModuleStyles(
'ext.cite.styles' );
228 $this->mBumpRefData =
true;
242 private function guardedRef(
246 $default_group = self::DEFAULT_GROUP
250 # The key here is the "name" attribute.
251 list( $key, $group, $follow, $dir ) = $this->refArg( $argv );
253 if ( $dir ===
'' && $str !==
'' ) {
254 $str .= $this->plainError(
'cite_error_ref_invalid_dir', $argv[
'dir'] );
256 # Split these into groups.
257 if ( $group ===
null ) {
258 if ( $this->mInReferences ) {
259 $group = $this->mReferencesGroup;
261 $group = $default_group;
272 if ( $this->mInReferences ) {
273 $isSectionPreview =
$parser->getOptions()->getIsSectionPreview();
274 if ( $group != $this->mReferencesGroup ) {
275 # <ref> and <references> have conflicting group attributes.
276 $this->mReferencesErrors[] =
278 'cite_error_references_group_mismatch',
279 Sanitizer::safeEncodeAttribute( $group )
281 } elseif ( $str !==
'' ) {
282 if ( !$isSectionPreview && !isset( $this->mRefs[$group] ) ) {
283 # Called with group attribute not defined in text.
284 $this->mReferencesErrors[] =
286 'cite_error_references_missing_group',
287 Sanitizer::safeEncodeAttribute( $group )
289 } elseif ( $key ===
null || $key ===
'' ) {
290 # <ref> calls inside <references> must be named
291 $this->mReferencesErrors[] =
292 $this->
error(
'cite_error_references_no_key' );
293 } elseif ( !$isSectionPreview && !isset( $this->mRefs[$group][$key] ) ) {
294 # Called with name attribute not defined in text.
295 $this->mReferencesErrors[] =
296 $this->
error(
'cite_error_references_missing_key', Sanitizer::safeEncodeAttribute( $key ) );
299 isset( $this->mRefs[$group][$key][
'text'] ) &&
300 $str !== $this->mRefs[$group][$key][
'text']
304 $this->mRefs[$group][$key][
'text'] .=
' ' . $this->plainError(
305 'cite_error_references_duplicate_key', $key
308 # Assign the text to corresponding ref
309 $this->mRefs[$group][$key][
'text'] = $str;
313 # <ref> called in <references> has no content.
314 $this->mReferencesErrors[] =
315 $this->
error(
'cite_error_empty_references_define', Sanitizer::safeEncodeAttribute( $key ) );
321 # <ref ...></ref>. This construct is invalid if
322 # it's a contentful ref, but OK if it's a named duplicate and should
323 # be equivalent <ref ... />, for compatability with #tag.
324 if ( is_string( $key ) && $key !==
'' ) {
327 $this->mRefCallStack[] =
false;
329 return $this->
error(
'cite_error_ref_no_input' );
333 if ( $key ===
false ) {
334 # TODO: Comment this case; what does this condition mean?
335 $this->mRefCallStack[] =
false;
336 return $this->
error(
'cite_error_ref_too_many_keys' );
339 if ( $str ===
null && $key ===
null ) {
340 # Something like <ref />; this makes no sense.
341 $this->mRefCallStack[] =
false;
342 return $this->
error(
'cite_error_ref_no_key' );
345 if ( is_string( $key ) && preg_match(
'/^[0-9]+$/', $key ) ||
346 is_string( $follow ) && preg_match(
'/^[0-9]+$/', $follow )
348 # Numeric names mess up the resulting id's, potentially produ-
349 # cing duplicate id's in the XHTML. The Right Thing To Do
350 # would be to mangle them, but it's not really high-priority
351 # (and would produce weird id's anyway).
353 $this->mRefCallStack[] =
false;
354 return $this->
error(
'cite_error_ref_numeric_key' );
359 preg_replace(
'#<([^ ]+?).*?>.*?</\\1 *>|<!--.*?-->#',
'', $str )
361 # (bug T8199) This most likely implies that someone left off the
362 # closing </ref> tag, which will cause the entire article to be
363 # eaten up until the next <ref>. So we bail out early instead.
364 # The fancy regex above first tries chopping out anything that
365 # looks like a comment or SGML tag, which is a crude way to avoid
366 # false alarms for <nowiki>, <pre>, etc.
368 # Possible improvement: print the warning, followed by the contents
369 # of the <ref> tag. This way no part of the article will be eaten
372 $this->mRefCallStack[] =
false;
373 return $this->
error(
'cite_error_included_ref' );
376 if ( is_string( $key ) || is_string( $str ) ) {
377 # We don't care about the content: if the key exists, the ref
378 # is presumptively valid. Either it stores a new ref, or re-
379 # fers to an existing one. If it refers to a nonexistent ref,
380 # we'll figure that out later. Likewise it's definitely valid
381 # if there's any content, regardless of key.
383 return $this->stack( $str, $key, $group, $follow, $argv, $dir,
$parser );
386 # Not clear how we could get here, but something is probably
387 # wrong with the types. Let's fail fast.
388 throw new Exception(
'Invalid $str and/or $key: ' .
serialize( [ $str, $key ] ) );
404 private function refArg(
array $argv ) {
405 $cnt =
count( $argv );
410 if ( isset( $argv[
'dir'] ) ) {
413 $isValidDir = in_array( strtolower( $argv[
'dir'] ), [
'ltr',
'rtl' ] );
418 unset( $argv[
'dir'] );
425 } elseif ( $cnt >= 1 ) {
426 if ( isset( $argv[
'name'] ) && isset( $argv[
'follow'] ) ) {
427 return [
false,
false,
false,
false ];
429 if ( isset( $argv[
'name'] ) ) {
431 $key = trim( $argv[
'name'] );
432 unset( $argv[
'name'] );
435 if ( isset( $argv[
'follow'] ) ) {
437 $follow = trim( $argv[
'follow'] );
438 unset( $argv[
'follow'] );
441 if ( isset( $argv[
'group'] ) ) {
443 $group = $argv[
'group'];
444 unset( $argv[
'group'] );
449 return [ $key, $group, $follow, $dir ];
452 return [
false,
false,
false,
false ];
456 return [
null, $group,
false, $dir ];
474 private function stack( $str, $key, $group, $follow,
array $call, $dir, Parser
$parser ) {
475 if ( !isset( $this->mRefs[$group] ) ) {
476 $this->mRefs[$group] = [];
478 if ( !isset( $this->mGroupCnt[$group] ) ) {
479 $this->mGroupCnt[$group] = 0;
481 if ( $follow !=
null ) {
482 if ( isset( $this->mRefs[$group][$follow] ) && is_array( $this->mRefs[$group][$follow] ) ) {
484 $this->mRefs[$group][$follow][
'text'] .=
' ' . $str;
487 $groupsCount =
count( $this->mRefs[$group] );
488 for ( $k = 0; $k < $groupsCount; $k++ ) {
489 if ( !isset( $this->mRefs[$group][$k][
'follow'] ) ) {
493 array_splice( $this->mRefs[$group], $k, 0, [ [
496 'key' => ++$this->mOutCnt,
500 array_splice( $this->mRefCallStack, $k, 0,
501 [ [
'new', $call, $str, $key, $group, $this->mOutCnt ] ] );
507 if ( $key ===
null ) {
511 $this->mRefs[$group][] = [
514 'key' => ++$this->mOutCnt,
517 $this->mRefCallStack[] = [
'new', $call, $str, $key, $group, $this->mOutCnt ];
519 return $this->linkRef( $group, $this->mOutCnt );
521 if ( !is_string( $key ) ) {
522 throw new Exception(
'Invalid stack key: ' .
serialize( $key ) );
526 if ( !isset( $this->mRefs[$group][$key] ) || !is_array( $this->mRefs[$group][$key] ) ) {
528 $this->mRefs[$group][$key] = [
531 'key' => ++$this->mOutCnt,
532 'number' => ++$this->mGroupCnt[$group],
535 $this->mRefCallStack[] = [
'new', $call, $str, $key, $group, $this->mOutCnt ];
537 return $this->linkRef(
540 $this->mRefs[$group][$key][
'key'] .
"-" . $this->mRefs[$group][$key][
'count'],
541 $this->mRefs[$group][$key][
'number'],
542 "-" . $this->mRefs[$group][$key][
'key']
547 if ( $this->mRefs[$group][$key][
'text'] ===
null && $str !==
'' ) {
549 $this->mRefs[$group][$key][
'text'] = $str;
551 $this->mRefs[$group][$key][
'dir'] = $dir;
552 $this->mRefCallStack[] = [
'assign', $call, $str, $key, $group,
553 $this->mRefs[$group][$key][
'key'] ];
555 if ( $str !=
null && $str !==
''
557 &&
$parser->mStripState->unstripBoth( $str )
558 !==
$parser->mStripState->unstripBoth( $this->mRefs[$group][$key][
'text'] )
562 $this->mRefs[$group][$key][
'text'] .=
' ' . $this->plainError(
563 'cite_error_references_duplicate_key', $key
566 $this->mRefCallStack[] = [
'increment', $call, $str, $key, $group,
567 $this->mRefs[$group][$key][
'key'] ];
569 return $this->linkRef(
572 $this->mRefs[$group][$key][
'key'] .
"-" . ++$this->mRefs[$group][$key][
'count'],
573 $this->mRefs[$group][$key][
'number'],
574 "-" . $this->mRefs[$group][$key][
'key']
599 private function rollbackRef(
$type, $key, $group, $index ) {
600 if ( !isset( $this->mRefs[$group] ) ) {
604 if ( $key ===
null ) {
605 foreach ( $this->mRefs[$group]
as $k => $v ) {
606 if ( $this->mRefs[$group][$k][
'key'] === $index ) {
614 if ( $key ===
null ) {
617 if ( !isset( $this->mRefs[$group][$key] ) ) {
620 if ( $this->mRefs[$group][$key][
'key'] != $index ) {
626 # Rollback the addition of new elements to the stack.
627 unset( $this->mRefs[$group][$key] );
628 if ( $this->mRefs[$group] === [] ) {
629 unset( $this->mRefs[$group] );
630 unset( $this->mGroupCnt[$group] );
634 # Rollback assignment of text to pre-existing elements.
635 $this->mRefs[$group][$key][
'text'] =
null;
636 # continue without break
638 # Rollback increase in named ref occurrences.
639 $this->mRefs[$group][$key][
'count']--;
655 if ( $this->mInCite || $this->mInReferences ) {
656 if ( is_null( $str ) ) {
657 return htmlspecialchars(
"<references/>" );
659 return htmlspecialchars(
"<references>$str</references>" );
662 $this->mInReferences =
true;
663 $ret = $this->guardedReferences( $str, $argv,
$parser );
664 $this->mInReferences =
false;
678 private function guardedReferences(
682 $group = self::DEFAULT_GROUP
684 global $wgCiteResponsiveReferences;
688 if ( isset( $argv[
'group'] ) ) {
689 $group = $argv[
'group'];
690 unset( $argv[
'group'] );
693 if ( strval( $str ) !==
'' ) {
694 $this->mReferencesGroup = $group;
696 # Detect whether we were sent already rendered <ref>s.
697 # Mostly a side effect of using #tag to call references.
698 # The following assumes that the parsed <ref>s sent within
699 # the <references> block were the most recent calls to
700 # <ref>. This assumption is true for all known use cases,
701 # but not strictly enforced by the parser. It is possible
702 # that some unusual combination of #tag, <references> and
703 # conditional parser functions could be created that would
704 # lead to malformed references here.
705 $count = substr_count( $str, Parser::MARKER_PREFIX .
"-ref-" );
708 # Undo effects of calling <ref> while unaware of containing <references>
709 for ( $i = 1; $i <= $count; $i++ ) {
710 if ( !$this->mRefCallStack ) {
714 $call = array_pop( $this->mRefCallStack );
715 $redoStack[] = $call;
716 if ( $call !==
false ) {
718 $ref_key, $ref_group, $ref_index ) = $call;
719 $this->rollbackRef(
$type, $ref_key, $ref_group, $ref_index );
723 # Rerun <ref> call now that mInReferences is set.
724 for ( $i =
count( $redoStack ) - 1; $i >= 0; $i-- ) {
725 $call = $redoStack[$i];
726 if ( $call !==
false ) {
728 $ref_key, $ref_group, $ref_index ) = $call;
729 $this->guardedRef( $ref_str, $ref_argv,
$parser );
733 # Parse $str to process any unparsed <ref> tags.
734 $parser->recursiveTagParse( $str );
737 $this->mRefCallStack = [];
740 if ( isset( $argv[
'responsive'] ) ) {
741 $responsive = $argv[
'responsive'] !==
'0';
742 unset( $argv[
'responsive'] );
744 $responsive = $wgCiteResponsiveReferences;
749 return $this->
error(
'cite_error_references_invalid_parameters' );
752 $s = $this->referencesFormat( $group, $responsive );
754 # Append errors generated while processing <references>
755 if ( $this->mReferencesErrors ) {
756 $s .=
"\n" . implode(
"<br />\n", $this->mReferencesErrors );
757 $this->mReferencesErrors = [];
772 private function referencesFormat( $group, $responsive ) {
773 if ( !$this->mRefs || !isset( $this->mRefs[$group] ) ) {
778 foreach ( $this->mRefs[$group]
as $k => $v ) {
779 $ent[] = $this->referencesFormatEntry( $k, $v );
785 "\n" . implode(
"\n", $ent ) .
"\n"
789 $ret = rtrim( $this->mParser->recursiveTagParse( $parserInput ),
"\n" );
794 $wrapClasses = [
'mw-references-wrap' ];
795 if (
count( $this->mRefs[$group] ) > 10 ) {
796 $wrapClasses[] =
'mw-references-columns';
801 if ( !$this->mParser->getOptions()->getIsPreview() ) {
803 $this->saveReferencesData( $group );
807 unset( $this->mRefs[$group] );
808 unset( $this->mGroupCnt[$group] );
821 private function referencesFormatEntry( $key, $val ) {
823 if ( !is_array( $val ) ) {
825 'cite_references_link_one',
827 self::getReferencesKey( $key )
830 $this->refKey( $key )
832 $this->referenceText( $key, $val ),
834 )->inContentLanguage()->plain();
836 $text = $this->referenceText( $key, $val[
'text'] );
837 if ( isset( $val[
'follow'] ) ) {
839 'cite_references_no_link',
841 self::getReferencesKey( $val[
'follow'] )
844 )->inContentLanguage()->plain();
846 if ( !isset( $val[
'count'] ) ) {
848 return wfMessage(
'cite_references_link_many',
850 self::getReferencesKey( $key .
"-" . ( isset( $val[
'key'] ) ? $val[
'key'] :
'' ) )
854 )->inContentLanguage()->plain();
856 if ( $val[
'count'] < 0 ) {
858 'cite_references_link_one',
860 self::getReferencesKey( $val[
'key'] )
863 # $this->refKey( $val[
'key'], $val[
'count'] )
864 $this->refKey( $val[
'key'] )
868 )->inContentLanguage()->plain();
874 if ( $val[
'count'] === 0 ) {
876 'cite_references_link_one',
878 self::getReferencesKey( $key .
"-" . $val[
'key'] )
881 # $this->refKey( $key, $val[
'count'] ),
882 $this->refKey( $key, $val[
'key'] .
"-" . $val[
'count'] )
886 )->inContentLanguage()->plain();
891 for ( $i = 0; $i <= $val[
'count']; ++$i ) {
893 'cite_references_link_many_format',
895 $this->refKey( $key, $val[
'key'] .
"-$i" )
897 $this->referencesFormatEntryNumericBacklinkLabel( $val[
'number'], $i, $val[
'count'] ),
898 $this->referencesFormatEntryAlternateBacklinkLabel( $i )
899 )->inContentLanguage()->plain();
902 $list = $this->listToText( $links );
904 return wfMessage(
'cite_references_link_many',
906 self::getReferencesKey( $key .
"-" . $val[
'key'] )
911 )->inContentLanguage()->plain();
920 private function referenceText( $key, $text ) {
921 if ( !isset( $text ) || $text ===
'' ) {
922 if ( $this->mParser->getOptions()->getIsSectionPreview() ) {
923 return $this->warning(
'cite_warning_sectionpreview_no_text', $key,
'noparse' );
925 return $this->plainError(
'cite_error_references_no_text', $key );
927 return '<span class="reference-text">' . rtrim( $text,
"\n" ) .
"</span>\n";
940 private function referencesFormatEntryNumericBacklinkLabel(
$base, $offset, $max ) {
942 $scope = strlen( $max );
944 sprintf(
"%s.%0{$scope}s",
$base, $offset )
959 private function referencesFormatEntryAlternateBacklinkLabel( $offset ) {
960 if ( !isset( $this->mBacklinkLabels ) ) {
961 $this->genBacklinkLabels();
963 if ( isset( $this->mBacklinkLabels[$offset] ) ) {
964 return $this->mBacklinkLabels[$offset];
967 return $this->plainError(
'cite_error_references_no_backlink_label',
null );
983 private function getLinkLabel( $offset, $group, $label ) {
984 $message =
"cite_link_label_group-$group";
985 if ( !isset( $this->mLinkLabels[$group] ) ) {
986 $this->genLinkLabels( $group, $message );
988 if ( $this->mLinkLabels[$group] ===
false ) {
993 if ( isset( $this->mLinkLabels[$group][$offset - 1] ) ) {
994 return $this->mLinkLabels[$group][$offset - 1];
997 return $this->plainError(
'cite_error_no_link_label_group', [ $group, $message ] );
1010 private function refKey( $key, $num =
null ) {
1011 $prefix =
wfMessage(
'cite_reference_link_prefix' )->inContentLanguage()->text();
1012 $suffix =
wfMessage(
'cite_reference_link_suffix' )->inContentLanguage()->text();
1013 if ( isset( $num ) ) {
1014 $key =
wfMessage(
'cite_reference_link_key_with_num', $key, $num )
1015 ->inContentLanguage()->plain();
1018 return "$prefix$key$suffix";
1029 public static function getReferencesKey( $key ) {
1030 $prefix =
wfMessage(
'cite_references_link_prefix' )->inContentLanguage()->text();
1031 $suffix =
wfMessage(
'cite_references_link_suffix' )->inContentLanguage()->text();
1033 return "$prefix$key$suffix";
1052 private function linkRef( $group, $key, $count =
null, $label =
null, $subkey =
'' ) {
1054 $label = is_null( $label ) ? ++$this->mGroupCnt[$group] : $label;
1056 return $this->mParser->recursiveTagParse(
1058 'cite_reference_link',
1059 $this->normalizeKey(
1060 $this->refKey( $key, $count )
1062 $this->normalizeKey(
1063 self::getReferencesKey( $key . $subkey )
1065 Sanitizer::safeEncodeAttribute(
1066 $this->getLinkLabel( $label, $group,
1067 ( ( $group === self::DEFAULT_GROUP ) ?
'' :
"$group " ) .
$wgContLang->formatNum( $label ) )
1069 )->inContentLanguage()->plain()
1079 private function normalizeKey( $key ) {
1080 $key = Sanitizer::escapeIdForAttribute( $key );
1081 $key = Sanitizer::safeEncodeAttribute( $key );
1096 private function listToText( $arr ) {
1097 $cnt =
count( $arr );
1099 $sep =
wfMessage(
'cite_references_link_many_sep' )->inContentLanguage()->plain();
1100 $and =
wfMessage(
'cite_references_link_many_and' )->inContentLanguage()->plain();
1104 return (
string)$arr[0];
1106 $t = array_slice( $arr, 0, $cnt - 1 );
1107 return implode( $sep,
$t ) . $and . $arr[$cnt - 1];
1116 private function genBacklinkLabels() {
1117 $text =
wfMessage(
'cite_references_link_many_format_backlink_labels' )
1118 ->inContentLanguage()->plain();
1119 $this->mBacklinkLabels = preg_split(
'#[\n\t ]#', $text );
1130 private function genLinkLabels( $group, $message ) {
1132 $msg =
wfMessage( $message )->inContentLanguage();
1133 if ( $msg->exists() ) {
1134 $text = $msg->plain();
1136 $this->mLinkLabels[$group] = ( !$text ) ?
false : preg_split(
'#[\n\t ]#', $text );
1145 public function clearState( Parser
$parser ) {
1146 if (
$parser->extCite !== $this ) {
1151 # Don't clear state when we're in the middle of parsing
1153 if ( $this->mInCite || $this->mInReferences ) {
1157 $this->mGroupCnt = [];
1159 $this->mCallCnt = 0;
1161 $this->mReferencesErrors = [];
1162 $this->mRefCallStack = [];
1170 public function cloneState( Parser
$parser ) {
1171 if (
$parser->extCite !== $this ) {
1176 $parser->extCite = clone $this;
1178 $parser->setHook(
'references', [
$parser->extCite,
'references' ] );
1181 $parser->extCite->mInCite =
false;
1182 $parser->extCite->mInReferences =
false;
1198 public function checkRefsNoReferences( $afterParse,
$parser, &$text ) {
1199 global $wgCiteResponsiveReferences;
1200 if ( is_null(
$parser->extCite ) ) {
1203 if (
$parser->extCite !== $this ) {
1204 $parser->extCite->checkRefsNoReferences( $afterParse,
$parser, $text );
1208 if ( $afterParse ) {
1209 $this->mHaveAfterParse =
true;
1210 } elseif ( $this->mHaveAfterParse ) {
1214 if ( !
$parser->getOptions()->getIsPreview() ) {
1216 if ( $this->mRefs && isset( $this->mRefs[self::DEFAULT_GROUP] ) ) {
1217 $this->saveReferencesData();
1219 $isSectionPreview =
false;
1221 $isSectionPreview =
$parser->getOptions()->getIsSectionPreview();
1225 foreach ( $this->mRefs
as $group => $refs ) {
1229 if ( $group === self::DEFAULT_GROUP || $isSectionPreview ) {
1230 $this->mInReferences =
true;
1231 $s .= $this->referencesFormat( $group, $wgCiteResponsiveReferences );
1232 $this->mInReferences =
false;
1236 'cite_error_group_refs_without_references',
1237 Sanitizer::safeEncodeAttribute( $group )
1241 if ( $isSectionPreview &&
$s !==
'' ) {
1243 $text .=
"\n" .
'<div class="mw-ext-cite-cite_section_preview_references" >';
1244 $headerMsg =
wfMessage(
'cite_section_preview_references' );
1245 if ( !$headerMsg->isDisabled() ) {
1246 $text .=
'<h2 id="mw-ext-cite-cite_section_preview_references_header" >'
1247 . $headerMsg->escaped()
1250 $text .=
$s .
'</div>';
1263 private function saveReferencesData( $group = self::DEFAULT_GROUP ) {
1264 global $wgCiteStoreReferencesData;
1265 if ( !$wgCiteStoreReferencesData ) {
1268 $savedRefs = $this->mParser->getOutput()->getExtensionData( self::EXT_DATA_KEY );
1269 if ( $savedRefs ===
null ) {
1273 'version' => self::DATA_VERSION_NUMBER,
1276 if ( $this->mBumpRefData ) {
1280 $savedRefs[
'refs'][] = [];
1281 $this->mBumpRefData =
false;
1283 $n =
count( $savedRefs[
'refs'] ) - 1;
1285 $savedRefs[
'refs'][$n][$group] = $this->mRefs[$group];
1287 $this->mParser->getOutput()->setExtensionData( self::EXT_DATA_KEY, $savedRefs );
1295 public static function setHooks( Parser
$parser ) {
1298 $parser->extCite =
new self();
1300 if ( !self::$hooksInstalled ) {
1305 self::$hooksInstalled =
true;
1308 $parser->setHook(
'references', [
$parser->extCite,
'references' ] );
1318 private function error( $key, $param =
null ) {
1319 $error = $this->plainError( $key, $param );
1320 return $this->mParser->recursiveTagParse( $error );
1331 private function plainError( $key, $param =
null ) {
1332 # For ease of debugging and because errors are rare, we
1333 # use the user language and split the parser cache.
1334 $lang = $this->mParser->getOptions()->getUserLangObj();
1335 $dir =
$lang->getDir();
1337 # We rely on the fact that PHP is okay with passing unused argu-
1338 # ments to functions. If $1 is not used in the message, wfMessage will
1339 # just ignore the extra parameter.
1344 ->inLanguage(
$lang )
1347 $this->mParser->addTrackingCategory(
'cite-tracking-category-cite-error' );
1352 'class' =>
'error mw-ext-cite-error',
1353 'lang' =>
$lang->getHtmlCode(),
1370 private function warning( $key, $param =
null, $parse =
'parse' ) {
1371 # For ease of debugging and because errors are rare, we
1372 # use the user language and split the parser cache.
1373 $lang = $this->mParser->getOptions()->getUserLangObj();
1374 $dir =
$lang->getDir();
1376 # We rely on the fact that PHP is okay with passing unused argu-
1377 # ments to functions. If $1 is not used in the message, wfMessage will
1378 # just ignore the extra parameter.
1383 ->inLanguage(
$lang )
1386 $key = preg_replace(
'/^cite_warning_/',
'', $key ) .
'';
1390 'class' =>
'warning mw-ext-cite-warning mw-ext-cite-warning-' .
1391 Sanitizer::escapeClass( $key ),
1392 'lang' =>
$lang->getHtmlCode(),
1398 if ( $parse ===
'parse' ) {
1399 $ret = $this->mParser->recursiveTagParse(
$ret );
1412 public static function getStoredReferences(
Title $title ) {
1413 global $wgCiteStoreReferencesData;
1414 if ( !$wgCiteStoreReferencesData ) {
1417 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1418 $key =
$cache->makeKey( self::EXT_DATA_KEY,
$title->getArticleID() );
1419 return $cache->getWithSetCallback(
1421 self::CACHE_DURATION_ONFETCH,
1424 $setOpts += Database::getCacheSetOptions(
$dbr );
1425 return self::recursiveFetchRefsFromDB(
$title,
$dbr );
1428 'checkKeys' => [ $key ],
1446 $string =
'', $i = 1 ) {
1447 $id =
$title->getArticleID();
1453 'pp_propname' =>
'references-' . $i
1459 $decodedString = gzdecode( $string );
1460 if ( $decodedString !==
false ) {
1461 $json = json_decode( $decodedString,
true );
1462 if ( json_last_error() === JSON_ERROR_NONE ) {
1467 wfDebug(
"Corrupted json detected when retrieving stored references for title id $id" );
1470 return self::recursiveFetchRefsFromDB(
$title,
$dbr, $string, ++$i );
1476 wfDebug(
"Failed to retrieve stored references for title id $id" );