34 const DEFAULT_GROUP =
'';
40 const MAX_STORAGE_LENGTH = 65535;
45 const EXT_DATA_KEY =
'Cite:References';
50 const DATA_VERSION_NUMBER = 1;
55 const CACHE_DURATION_ONPARSE = 3600;
60 const CACHE_DURATION_ONFETCH = 18000;
100 private $mOutCnt = 0;
105 private $mGroupCnt = [];
113 private $mCallCnt = 0;
122 private $mBacklinkLabels;
129 private $mLinkLabels = [];
142 private $mHaveAfterParse =
false;
150 public $mInCite =
false;
158 public $mInReferences =
false;
165 private $mReferencesErrors = [];
172 private $mReferencesGroup =
'';
181 private $mRefCallStack = [];
186 private $mBumpRefData =
false;
192 private static $hooksInstalled =
false;
205 if ( $this->mInCite ) {
206 return htmlspecialchars(
"<ref>$str</ref>" );
210 $this->mInCite =
true;
214 $this->mInCite =
false;
216 $parserOutput =
$parser->getOutput();
217 $parserOutput->addModules(
'ext.cite.a11y' );
218 $parserOutput->addModuleStyles(
'ext.cite.styles' );
220 if ( is_callable( [ $frame,
'setVolatile' ] ) ) {
226 $this->mBumpRefData =
true;
240 private function guardedRef(
244 $default_group = self::DEFAULT_GROUP
248 # The key here is the "name" attribute.
249 list( $key, $group, $follow ) = $this->refArg(
$argv );
251 # Split these into groups.
252 if ( $group ===
null ) {
253 if ( $this->mInReferences ) {
254 $group = $this->mReferencesGroup;
256 $group = $default_group;
267 if ( $this->mInReferences ) {
268 $isSectionPreview =
$parser->getOptions()->getIsSectionPreview();
269 if ( $group != $this->mReferencesGroup ) {
270 # <ref> and <references> have conflicting group attributes.
271 $this->mReferencesErrors[] =
272 $this->
error(
'cite_error_references_group_mismatch', htmlspecialchars( $group ) );
273 } elseif ( $str !==
'' ) {
274 if ( !$isSectionPreview && !isset( $this->mRefs[$group] ) ) {
275 # Called with group attribute not defined in text.
276 $this->mReferencesErrors[] =
277 $this->
error(
'cite_error_references_missing_group', htmlspecialchars( $group ) );
278 } elseif ( $key ===
null || $key ===
'' ) {
279 # <ref> calls inside <references> must be named
280 $this->mReferencesErrors[] =
281 $this->
error(
'cite_error_references_no_key' );
282 } elseif ( !$isSectionPreview && !isset( $this->mRefs[$group][$key] ) ) {
283 # Called with name attribute not defined in text.
284 $this->mReferencesErrors[] =
285 $this->
error(
'cite_error_references_missing_key', $key );
288 isset( $this->mRefs[$group][$key][
'text'] ) &&
289 $str !== $this->mRefs[$group][$key][
'text']
293 $this->mRefs[$group][$key][
'text'] .=
' ' . $this->
error(
294 'cite_error_references_duplicate_key', $key,
'noparse'
297 # Assign the text to corresponding ref
298 $this->mRefs[$group][$key][
'text'] = $str;
302 # <ref> called in <references> has no content.
303 $this->mReferencesErrors[] =
304 $this->
error(
'cite_error_empty_references_define', $key );
310 # <ref ...></ref>. This construct is invalid if
311 # it's a contentful ref, but OK if it's a named duplicate and should
312 # be equivalent <ref ... />, for compatability with #tag.
313 if ( is_string( $key ) && $key !==
'' ) {
316 $this->mRefCallStack[] =
false;
318 return $this->
error(
'cite_error_ref_no_input' );
322 if ( $key ===
false ) {
323 # TODO: Comment this case; what does this condition mean?
324 $this->mRefCallStack[] =
false;
325 return $this->
error(
'cite_error_ref_too_many_keys' );
328 if ( $str ===
null && $key ===
null ) {
329 # Something like <ref />; this makes no sense.
330 $this->mRefCallStack[] =
false;
331 return $this->
error(
'cite_error_ref_no_key' );
334 if ( preg_match(
'/^[0-9]+$/', $key ) || preg_match(
'/^[0-9]+$/', $follow ) ) {
335 # Numeric names mess up the resulting id's, potentially produ-
336 # cing duplicate id's in the XHTML. The Right Thing To Do
337 # would be to mangle them, but it's not really high-priority
338 # (and would produce weird id's anyway).
340 $this->mRefCallStack[] =
false;
341 return $this->
error(
'cite_error_ref_numeric_key' );
346 preg_replace(
'#<([^ ]+?).*?>.*?</\\1 *>|<!--.*?-->#',
'', $str )
348 # (bug T8199) This most likely implies that someone left off the
349 # closing </ref> tag, which will cause the entire article to be
350 # eaten up until the next <ref>. So we bail out early instead.
351 # The fancy regex above first tries chopping out anything that
352 # looks like a comment or SGML tag, which is a crude way to avoid
353 # false alarms for <nowiki>, <pre>, etc.
355 # Possible improvement: print the warning, followed by the contents
356 # of the <ref> tag. This way no part of the article will be eaten
359 $this->mRefCallStack[] =
false;
360 return $this->
error(
'cite_error_included_ref' );
363 if ( is_string( $key ) || is_string( $str ) ) {
364 # We don't care about the content: if the key exists, the ref
365 # is presumptively valid. Either it stores a new ref, or re-
366 # fers to an existing one. If it refers to a nonexistent ref,
367 # we'll figure that out later. Likewise it's definitely valid
368 # if there's any content, regardless of key.
370 return $this->stack( $str, $key, $group, $follow,
$argv );
373 # Not clear how we could get here, but something is probably
374 # wrong with the types. Let's fail fast.
375 throw new Exception(
'Invalid $str and/or $key: ' .
serialize( [ $str, $key ] ) );
400 } elseif ( $cnt >= 1 ) {
401 if ( isset(
$argv[
'name'] ) && isset(
$argv[
'follow'] ) ) {
402 return [
false,
false,
false ];
404 if ( isset(
$argv[
'name'] ) ) {
406 $key = Sanitizer::escapeId(
$argv[
'name'],
'noninitial' );
407 unset(
$argv[
'name'] );
410 if ( isset(
$argv[
'follow'] ) ) {
412 $follow = Sanitizer::escapeId(
$argv[
'follow'],
'noninitial' );
413 unset(
$argv[
'follow'] );
416 if ( isset(
$argv[
'group'] ) ) {
418 $group =
$argv[
'group'];
419 unset(
$argv[
'group'] );
424 return [ $key, $group, $follow ];
427 return [
false,
false,
false ];
431 return [
null, $group,
false ];
447 private function stack( $str, $key =
null, $group, $follow,
array $call ) {
448 if ( !isset( $this->mRefs[$group] ) ) {
449 $this->mRefs[$group] = [];
451 if ( !isset( $this->mGroupCnt[$group] ) ) {
452 $this->mGroupCnt[$group] = 0;
454 if ( $follow !=
null ) {
455 if ( isset( $this->mRefs[$group][$follow] ) && is_array( $this->mRefs[$group][$follow] ) ) {
457 $this->mRefs[$group][$follow][
'text'] .=
' ' . $str;
460 $groupsCount =
count( $this->mRefs[$group] );
461 for ( $k = 0; $k < $groupsCount; $k++ ) {
462 if ( !isset( $this->mRefs[$group][$k][
'follow'] ) ) {
466 array_splice( $this->mRefs[$group], $k, 0, [ [
469 'key' => ++$this->mOutCnt,
472 array_splice( $this->mRefCallStack, $k, 0,
473 [ [
'new', $call, $str, $key, $group, $this->mOutCnt ] ] );
479 if ( $key ===
null ) {
482 $this->mRefs[$group][] = [
485 'key' => ++$this->mOutCnt
487 $this->mRefCallStack[] = [
'new', $call, $str, $key, $group, $this->mOutCnt ];
489 return $this->linkRef( $group, $this->mOutCnt );
491 if ( !is_string( $key ) ) {
492 throw new Exception(
'Invalid stack key: ' .
serialize( $key ) );
496 if ( !isset( $this->mRefs[$group][$key] ) || !is_array( $this->mRefs[$group][$key] ) ) {
498 $this->mRefs[$group][$key] = [
501 'key' => ++$this->mOutCnt,
502 'number' => ++$this->mGroupCnt[$group]
504 $this->mRefCallStack[] = [
'new', $call, $str, $key, $group, $this->mOutCnt ];
506 return $this->linkRef(
509 $this->mRefs[$group][$key][
'key'] .
"-" . $this->mRefs[$group][$key][
'count'],
510 $this->mRefs[$group][$key][
'number'],
511 "-" . $this->mRefs[$group][$key][
'key']
516 if ( $this->mRefs[$group][$key][
'text'] ===
null && $str !==
'' ) {
518 $this->mRefs[$group][$key][
'text'] = $str;
519 $this->mRefCallStack[] = [
'assign', $call, $str, $key, $group,
520 $this->mRefs[$group][$key][
'key'] ];
522 if ( $str !=
null && $str !==
'' && $str !== $this->mRefs[$group][$key][
'text'] ) {
525 $this->mRefs[$group][$key][
'text'] .=
' ' . $this->
error(
526 'cite_error_references_duplicate_key', $key,
'noparse'
529 $this->mRefCallStack[] = [
'increment', $call, $str, $key, $group,
530 $this->mRefs[$group][$key][
'key'] ];
532 return $this->linkRef(
535 $this->mRefs[$group][$key][
'key'] .
"-" . ++$this->mRefs[$group][$key][
'count'],
536 $this->mRefs[$group][$key][
'number'],
537 "-" . $this->mRefs[$group][$key][
'key']
562 private function rollbackRef(
$type, $key, $group, $index ) {
563 if ( !isset( $this->mRefs[$group] ) ) {
567 if ( $key ===
null ) {
568 foreach ( $this->mRefs[$group]
as $k => $v ) {
569 if ( $this->mRefs[$group][$k][
'key'] === $index ) {
577 if ( $key ===
null ) {
580 if ( !isset( $this->mRefs[$group][$key] ) ) {
583 if ( $this->mRefs[$group][$key][
'key'] != $index ) {
589 # Rollback the addition of new elements to the stack.
590 unset( $this->mRefs[$group][$key] );
591 if ( $this->mRefs[$group] === [] ) {
592 unset( $this->mRefs[$group] );
593 unset( $this->mGroupCnt[$group] );
597 # Rollback assignment of text to pre-existing elements.
598 $this->mRefs[$group][$key][
'text'] =
null;
599 # continue without break
601 # Rollback increase in named ref occurrences.
602 $this->mRefs[$group][$key][
'count']--;
618 if ( $this->mInCite || $this->mInReferences ) {
619 if ( is_null( $str ) ) {
620 return htmlspecialchars(
"<references/>" );
622 return htmlspecialchars(
"<references>$str</references>" );
625 $this->mInReferences =
true;
627 $this->mInReferences =
false;
628 if ( is_callable( [ $frame,
'setVolatile' ] ) ) {
642 private function guardedReferences(
646 $group = self::DEFAULT_GROUP
648 global $wgCiteResponsiveReferences;
652 if ( isset(
$argv[
'group'] ) ) {
653 $group =
$argv[
'group'];
654 unset(
$argv[
'group'] );
657 if ( strval( $str ) !==
'' ) {
658 $this->mReferencesGroup = $group;
660 # Detect whether we were sent already rendered <ref>s.
661 # Mostly a side effect of using #tag to call references.
662 # The following assumes that the parsed <ref>s sent within
663 # the <references> block were the most recent calls to
664 # <ref>. This assumption is true for all known use cases,
665 # but not strictly enforced by the parser. It is possible
666 # that some unusual combination of #tag, <references> and
667 # conditional parser functions could be created that would
668 # lead to malformed references here.
669 $count = substr_count( $str, Parser::MARKER_PREFIX .
"-ref-" );
672 # Undo effects of calling <ref> while unaware of containing <references>
673 for ( $i = 1; $i <= $count; $i++ ) {
674 if ( !$this->mRefCallStack ) {
678 $call = array_pop( $this->mRefCallStack );
679 $redoStack[] = $call;
680 if ( $call !==
false ) {
682 $ref_key, $ref_group, $ref_index ) = $call;
683 $this->rollbackRef(
$type, $ref_key, $ref_group, $ref_index );
687 # Rerun <ref> call now that mInReferences is set.
688 for ( $i =
count( $redoStack ) - 1; $i >= 0; $i-- ) {
689 $call = $redoStack[$i];
690 if ( $call !==
false ) {
692 $ref_key, $ref_group, $ref_index ) = $call;
693 $this->guardedRef( $ref_str, $ref_argv,
$parser );
697 # Parse $str to process any unparsed <ref> tags.
698 $parser->recursiveTagParse( $str );
701 $this->mRefCallStack = [];
704 if ( isset(
$argv[
'responsive'] ) ) {
705 $responsive =
$argv[
'responsive'] !==
'0';
706 unset(
$argv[
'responsive'] );
708 $responsive = $wgCiteResponsiveReferences;
713 return $this->
error(
'cite_error_references_invalid_parameters' );
716 $s = $this->referencesFormat( $group, $responsive );
718 # Append errors generated while processing <references>
719 if ( $this->mReferencesErrors ) {
720 $s .=
"\n" . implode(
"<br />\n", $this->mReferencesErrors );
721 $this->mReferencesErrors = [];
733 private function referencesFormat( $group, $responsive ) {
734 if ( !$this->mRefs || !isset( $this->mRefs[$group] ) ) {
739 foreach ( $this->mRefs[$group]
as $k => $v ) {
740 $ent[] = $this->referencesFormatEntry( $k, $v );
746 "\n" . implode(
"\n", $ent ) .
"\n"
752 if ( $wgCiteCacheReferences ) {
756 $this->mParser->Title()->getArticleID()
758 $data =
$wgMemc->get( $cacheKey );
761 if ( !$data || !$this->mParser->isValidHalfParsedText( $data ) ) {
763 $ret = rtrim( $this->mParser->recursiveTagParse( $parserInput ),
"\n" );
765 if ( $wgCiteCacheReferences ) {
766 $serData = $this->mParser->serializeHalfParsedText(
$ret );
767 $wgMemc->set( $cacheKey, $serData, 86400 );
771 $ret = $this->mParser->unserializeHalfParsedText( $data );
777 $wrapClasses = [
'mw-references-wrap' ];
778 if (
count( $this->mRefs[$group] ) > 10 ) {
779 $wrapClasses[] =
'mw-references-columns';
784 if ( !$this->mParser->getOptions()->getIsPreview() ) {
786 $this->saveReferencesData( $group );
790 unset( $this->mRefs[$group] );
791 unset( $this->mGroupCnt[$group] );
804 private function referencesFormatEntry( $key, $val ) {
806 if ( !is_array( $val ) ) {
808 'cite_references_link_one',
809 self::getReferencesKey( $key ),
810 $this->refKey( $key ),
811 $this->referenceText( $key, $val )
812 )->inContentLanguage()->plain();
814 $text = $this->referenceText( $key, $val[
'text'] );
815 if ( isset( $val[
'follow'] ) ) {
817 'cite_references_no_link',
818 self::getReferencesKey( $val[
'follow'] ),
820 )->inContentLanguage()->plain();
822 if ( !isset( $val[
'count'] ) ) {
824 return wfMessage(
'cite_references_link_many',
825 self::getReferencesKey( $key .
"-" . ( isset( $val[
'key'] ) ? $val[
'key'] :
'' ) ),
828 )->inContentLanguage()->plain();
830 if ( $val[
'count'] < 0 ) {
832 'cite_references_link_one',
833 self::getReferencesKey( $val[
'key'] ),
834 # $this->refKey( $val[
'key'], $val[
'count'] ),
835 $this->refKey( $val[
'key'] ),
837 )->inContentLanguage()->plain();
843 if ( $val[
'count'] === 0 ) {
845 'cite_references_link_one',
846 self::getReferencesKey( $key .
"-" . $val[
'key'] ),
847 # $this->refKey( $key, $val[
'count'] ),
848 $this->refKey( $key, $val[
'key'] .
"-" . $val[
'count'] ),
850 )->inContentLanguage()->plain();
855 for ( $i = 0; $i <= $val[
'count']; ++$i ) {
857 'cite_references_link_many_format',
858 $this->refKey( $key, $val[
'key'] .
"-$i" ),
859 $this->referencesFormatEntryNumericBacklinkLabel( $val[
'number'], $i, $val[
'count'] ),
860 $this->referencesFormatEntryAlternateBacklinkLabel( $i )
861 )->inContentLanguage()->plain();
864 $list = $this->listToText( $links );
866 return wfMessage(
'cite_references_link_many',
867 self::getReferencesKey( $key .
"-" . $val[
'key'] ),
870 )->inContentLanguage()->plain();
879 private function referenceText( $key, $text ) {
880 if ( !isset( $text ) || $text ===
'' ) {
881 if ( $this->mParser->getOptions()->getIsSectionPreview() ) {
882 return $this->warning(
'cite_warning_sectionpreview_no_text', $key,
'noparse' );
884 return $this->
error(
'cite_error_references_no_text', $key,
'noparse' );
886 return '<span class="reference-text">' . rtrim( $text,
"\n" ) .
"</span>\n";
901 private function referencesFormatEntryNumericBacklinkLabel(
$base, $offset, $max ) {
903 $scope = strlen( $max );
905 sprintf(
"%s.%0{$scope}s",
$base, $offset )
920 private function referencesFormatEntryAlternateBacklinkLabel( $offset ) {
921 if ( !isset( $this->mBacklinkLabels ) ) {
922 $this->genBacklinkLabels();
924 if ( isset( $this->mBacklinkLabels[$offset] ) ) {
925 return $this->mBacklinkLabels[$offset];
928 return $this->
error(
'cite_error_references_no_backlink_label',
null,
'noparse' );
944 private function getLinkLabel( $offset, $group, $label ) {
945 $message =
"cite_link_label_group-$group";
946 if ( !isset( $this->mLinkLabels[$group] ) ) {
947 $this->genLinkLabels( $group, $message );
949 if ( $this->mLinkLabels[$group] ===
false ) {
954 if ( isset( $this->mLinkLabels[$group][$offset - 1] ) ) {
955 return $this->mLinkLabels[$group][$offset - 1];
958 return $this->
error(
'cite_error_no_link_label_group', [ $group, $message ],
'noparse' );
973 private function refKey( $key, $num =
null ) {
974 $prefix =
wfMessage(
'cite_reference_link_prefix' )->inContentLanguage()->text();
975 $suffix =
wfMessage(
'cite_reference_link_suffix' )->inContentLanguage()->text();
976 if ( isset( $num ) ) {
977 $key =
wfMessage(
'cite_reference_link_key_with_num', $key, $num )
978 ->inContentLanguage()->plain();
981 return "$prefix$key$suffix";
994 public static function getReferencesKey( $key ) {
995 $prefix =
wfMessage(
'cite_references_link_prefix' )->inContentLanguage()->text();
996 $suffix =
wfMessage(
'cite_references_link_suffix' )->inContentLanguage()->text();
998 return "$prefix$key$suffix";
1016 private function linkRef( $group, $key, $count =
null, $label =
null, $subkey =
'' ) {
1018 $label = is_null( $label ) ? ++$this->mGroupCnt[$group] : $label;
1021 $this->mParser->recursiveTagParse(
1023 'cite_reference_link',
1024 $this->refKey( $key, $count ),
1025 self::getReferencesKey( $key . $subkey ),
1026 $this->getLinkLabel( $label, $group,
1027 ( ( $group === self::DEFAULT_GROUP ) ?
'' :
"$group " ) .
$wgContLang->formatNum( $label ) )
1028 )->inContentLanguage()->plain()
1044 private function listToText( $arr ) {
1045 $cnt =
count( $arr );
1047 $sep =
wfMessage(
'cite_references_link_many_sep' )->inContentLanguage()->plain();
1048 $and =
wfMessage(
'cite_references_link_many_and' )->inContentLanguage()->plain();
1052 return (
string)$arr[0];
1054 $t = array_slice( $arr, 0, $cnt - 1 );
1055 return implode( $sep,
$t ) . $and . $arr[$cnt - 1];
1064 private function genBacklinkLabels() {
1065 $text =
wfMessage(
'cite_references_link_many_format_backlink_labels' )
1066 ->inContentLanguage()->plain();
1067 $this->mBacklinkLabels = preg_split(
'#[\n\t ]#', $text );
1078 private function genLinkLabels( $group, $message ) {
1080 $msg =
wfMessage( $message )->inContentLanguage();
1081 if ( $msg->exists() ) {
1082 $text = $msg->plain();
1084 $this->mLinkLabels[$group] = ( !$text ) ?
false : preg_split(
'#[\n\t ]#', $text );
1095 public function clearState( Parser &
$parser ) {
1096 if (
$parser->extCite !== $this ) {
1100 # Don't clear state when we're in the middle of parsing
1102 if ( $this->mInCite || $this->mInReferences ) {
1106 $this->mGroupCnt = [];
1108 $this->mCallCnt = 0;
1110 $this->mReferencesErrors = [];
1111 $this->mRefCallStack = [];
1123 public function cloneState( Parser
$parser ) {
1124 if (
$parser->extCite !== $this ) {
1128 $parser->extCite = clone $this;
1130 $parser->setHook(
'references', [
$parser->extCite,
'references' ] );
1133 $parser->extCite->mInCite =
false;
1134 $parser->extCite->mInReferences =
false;
1154 public function checkRefsNoReferences( $afterParse, &
$parser, &$text ) {
1155 global $wgCiteResponsiveReferences;
1156 if ( is_null(
$parser->extCite ) ) {
1159 if (
$parser->extCite !== $this ) {
1160 return $parser->extCite->checkRefsNoReferences( $afterParse,
$parser, $text );
1163 if ( $afterParse ) {
1164 $this->mHaveAfterParse =
true;
1165 } elseif ( $this->mHaveAfterParse ) {
1169 if ( !
$parser->getOptions()->getIsPreview() ) {
1171 if ( $this->mRefs && isset( $this->mRefs[self::DEFAULT_GROUP] ) ) {
1172 $this->saveReferencesData();
1174 $isSectionPreview =
false;
1176 $isSectionPreview =
$parser->getOptions()->getIsSectionPreview();
1180 foreach ( $this->mRefs
as $group => $refs ) {
1184 if ( $group === self::DEFAULT_GROUP || $isSectionPreview ) {
1185 $s .= $this->referencesFormat( $group, $wgCiteResponsiveReferences );
1188 $this->
error(
'cite_error_group_refs_without_references', htmlspecialchars( $group ) );
1191 if ( $isSectionPreview &&
$s !==
'' ) {
1193 $text .=
"\n" .
'<div class="mw-ext-cite-cite_section_preview_references" >';
1194 $headerMsg =
wfMessage(
'cite_section_preview_references' );
1195 if ( !$headerMsg->isDisabled() ) {
1196 $text .=
'<h2 id="mw-ext-cite-cite_section_preview_references_header" >'
1197 . $headerMsg->escaped()
1200 $text .=
$s .
'</div>';
1214 private function saveReferencesData( $group = self::DEFAULT_GROUP ) {
1215 global $wgCiteStoreReferencesData;
1216 if ( !$wgCiteStoreReferencesData ) {
1219 $savedRefs = $this->mParser->getOutput()->getExtensionData( self::EXT_DATA_KEY );
1220 if ( $savedRefs ===
null ) {
1224 'version' => self::DATA_VERSION_NUMBER,
1227 if ( $this->mBumpRefData ) {
1231 $savedRefs[
'refs'][] = [];
1232 $this->mBumpRefData =
false;
1234 $n =
count( $savedRefs[
'refs'] ) - 1;
1236 $savedRefs[
'refs'][$n][$group] = $this->mRefs[$group];
1238 $this->mParser->getOutput()->setExtensionData( self::EXT_DATA_KEY, $savedRefs );
1250 public function checkAnyCalls( &
$output ) {
1253 return (
$wgParser->extCite->mCallCnt <= 0 );
1263 public static function setHooks( Parser
$parser ) {
1266 $parser->extCite =
new self();
1268 if ( !self::$hooksInstalled ) {
1273 $wgHooks[
'InlineEditorPartialAfterParse'][] = [
$parser->extCite,
'checkAnyCalls' ];
1274 self::$hooksInstalled =
true;
1277 $parser->setHook(
'references', [
$parser->extCite,
'references' ] );
1290 private function error( $key, $param =
null, $parse =
'parse' ) {
1291 # For ease of debugging and because errors are rare, we
1292 # use the user language and split the parser cache.
1293 $lang = $this->mParser->getOptions()->getUserLangObj();
1296 # We rely on the fact that PHP is okay with passing unused argu-
1297 # ments to functions. If $1 is not used in the message, wfMessage will
1298 # just ignore the extra parameter.
1303 ->inLanguage(
$lang )
1306 $this->mParser->addTrackingCategory(
'cite-tracking-category-cite-error' );
1311 'class' =>
'error mw-ext-cite-error',
1312 'lang' =>
$lang->getHtmlCode(),
1318 if ( $parse ===
'parse' ) {
1319 $ret = $this->mParser->recursiveTagParse(
$ret );
1333 private function warning( $key, $param =
null, $parse =
'parse' ) {
1334 # For ease of debugging and because errors are rare, we
1335 # use the user language and split the parser cache.
1336 $lang = $this->mParser->getOptions()->getUserLangObj();
1339 # We rely on the fact that PHP is okay with passing unused argu-
1340 # ments to functions. If $1 is not used in the message, wfMessage will
1341 # just ignore the extra parameter.
1346 ->inLanguage(
$lang )
1349 $key = preg_replace(
'/^cite_warning_/',
'', $key ) .
'';
1353 'class' =>
'warning mw-ext-cite-warning mw-ext-cite-warning-' .
1354 Sanitizer::escapeClass( $key ),
1355 'lang' =>
$lang->getHtmlCode(),
1361 if ( $parse ===
'parse' ) {
1362 $ret = $this->mParser->recursiveTagParse(
$ret );
1375 public static function getStoredReferences(
Title $title ) {
1376 global $wgCiteStoreReferencesData;
1377 if ( !$wgCiteStoreReferencesData ) {
1381 $key =
$cache->makeKey( self::EXT_DATA_KEY,
$title->getArticleID() );
1382 return $cache->getWithSetCallback(
1384 self::CACHE_DURATION_ONFETCH,
1387 $setOpts += Database::getCacheSetOptions(
$dbr );
1388 return self::recursiveFetchRefsFromDB(
$title,
$dbr );
1391 'checkKeys' => [ $key ],
1408 private static function recursiveFetchRefsFromDB(
Title $title, DatabaseBase
$dbr,
1409 $string =
'', $i = 1 ) {
1410 $id =
$title->getArticleID();
1416 'pp_propname' =>
'references-' . $i
1422 $decodedString = gzdecode( $string );
1423 if ( $decodedString !==
false ) {
1424 $json = json_decode( $decodedString,
true );
1425 if ( json_last_error() === JSON_ERROR_NONE ) {
1430 wfDebug(
"Corrupted json detected when retrieving stored references for title id $id" );
1433 return self::recursiveFetchRefsFromDB(
$title,
$dbr, $string, ++$i );
1439 wfDebug(
"Failed to retrieve stored references for title id $id" );