/extensions/DelayedDefinition/DelayedDefinition_body.php

https://github.com/ChuguluGames/mediawiki-svn · PHP · 174 lines · 94 code · 34 blank · 46 comment · 6 complexity · 246f28897af3e55f29d5130e3360f1bd MD5 · raw file

  1. <?php
  2. class ExtDelayedDefinition {
  3. var $mCache, $mTransTable;
  4. var $mMarkerHead;
  5. var $mMarkerTail;
  6. var $mParser;
  7. // Resets state, called when parser clears state.
  8. function clearState() {
  9. $this->mCache = array();
  10. $this->mTransTable = array();
  11. $salt = dechex( mt_rand() );
  12. $this->mMarkerHead = "\x7fDELAY-DEF-" . $salt . "-";
  13. $this->mMarkerTail = "-DEF-DELAY\x7f";
  14. $this->mParser = null;
  15. return true;
  16. }
  17. // Initialize hooks and tags
  18. function __construct( &$parser ) {
  19. global $wgHooks;
  20. $parser->setHook( 'define', array( &$this, 'define' ) );
  21. $parser->setHook( 'display', array( &$this, 'display' ) );
  22. $wgHooks['ParserClearState'][] = array( &$this, 'clearState' );
  23. $wgHooks['ParserAfterTidy'][] = array( &$this, 'replaceMarkers' );
  24. $this->clearState();
  25. }
  26. /**
  27. * Parses the <define> block into HTML
  28. *
  29. * THIS IS A GIANT, CRAPPY HACK!
  30. * FIXME: This function makes a recursive call to Parser::parse.
  31. *
  32. * Currently (Mediawiki 1.16), there are no Parser hooks that allow this to work
  33. * without the ugly hack, and Parser::recursiveTagParse only runs the
  34. * Parser::internalParse branch of the parser engine. In addition, tag extensions
  35. * are limited to returning HTML, so partial parsing is not acceptable here.
  36. *
  37. * A reasonable fix here would seem to require multiple additions to the current Parser.
  38. *
  39. * Please note that since the hack being used here is outside the scope of intended
  40. * use for Parser::parse, it is unclear if it will work correctly in all cases, and
  41. * future versions of Mediawiki may break this behavior.
  42. **/
  43. function parse( $text ) {
  44. global $wgParser;
  45. $opt =& $wgParser->getOptions();
  46. $orig = $opt->getIsSectionPreview();
  47. // Avoids problems associated with parsing a partial page.
  48. $opt->setIsSectionPreview( true );
  49. // Call parse while duplicating the current parameters and forcing
  50. // clearState to be false.
  51. $out = $wgParser->parse( $text, $wgParser->getTitle(), $opt,
  52. false, false, $wgParser->getRevisionId() )->getText();
  53. $opt->setIsSectionPreview( $orig );
  54. // Remove extraneous encoding created by the parser.
  55. $out = preg_replace( "/<!--.*-->/Us", "", $out );
  56. return trim( $out );
  57. }
  58. // Tag callback for <define>.
  59. function define( $input, $argv, &$parser ) {
  60. if ( !array_key_exists( 'name', $argv ) ) {
  61. // name argument is missing.
  62. return '<strong class="error">' . wfMsgForContent( "delaydef-error-no-name" ) . '</strong>';
  63. }
  64. $key = $this->findKey( $argv['name'] );
  65. if ( array_key_exists( $key, $this->mCache ) ) {
  66. // Attempt to redefine the same name twice.
  67. return '<strong class="error">' .
  68. wfMsgForContent( "delaydef-error-redef", $argv['name'] ) .
  69. '</strong>';
  70. }
  71. $this->mCache[$key] = $this->parse( $input );
  72. // Use a blank placeholder, otherwise MW may use consecutive
  73. // newlines to generate unintended paragraph breaks.
  74. return $this->mMarkerHead . "blank" . $this->mMarkerTail;
  75. }
  76. // Tag callback for <display>.
  77. function display( $input, $argv, &$parser ) {
  78. if ( !array_key_exists( 'name', $argv ) ) {
  79. // name argument is missing
  80. return '<strong class="error">' . wfMsgForContent( "delaydef-error-no-name" ) . '</strong>';
  81. } elseif ( $input !== null ) {
  82. /* if <display name="foo"> BAR </display> is used
  83. * then treat this as both the definition of "foo"
  84. * and as a display request.
  85. */
  86. $out = $this->define( $input, $argv, $parser );
  87. if ( $out !== '' ) {
  88. // Passes errors to user.
  89. return $out;
  90. }
  91. }
  92. return $this->getMarker( $argv['name'] );
  93. }
  94. // Generate unique markers for DelayedDefinion, replaced by replaceMarkers
  95. // after page is fully parsed.
  96. function getMarker( $name ) {
  97. $key = $this->findKey( $name );
  98. return $this->mMarkerHead . $key . $this->mMarkerTail;
  99. }
  100. // Translate names into unique numeric keys.
  101. function findKey( $name ) {
  102. if ( !array_key_exists( $name, $this->mTransTable ) ) {
  103. $this->mTransTable[$name] = count( $this->mTransTable );
  104. }
  105. return $this->mTransTable[$name];
  106. }
  107. // Replace the unique markers with actual content.
  108. // Called by the hook ParserAfterTidy.
  109. function replaceMarkers( &$parser, &$text ) {
  110. // Replace display markers with content.
  111. foreach ( $this->mCache as $key => $content ) {
  112. $marker = $this->mMarkerHead . $key . $this->mMarkerTail;
  113. $text = str_replace( $marker, $content, $text );
  114. }
  115. // Strip the blank marker.
  116. $marker = $this->mMarkerHead . "blank" . $this->mMarkerTail;
  117. $text = str_replace( $marker, "", $text );
  118. // On section preview, kill extra markers. Assumed to be defined on the
  119. // rest of the page.
  120. if ( $parser->getOptions()->getIsSectionPreview() ) {
  121. $regex = "/" . $this->mMarkerHead . "\d+" . $this->mMarkerTail . "/";
  122. $text = preg_replace( $regex, "", $text );
  123. return true;
  124. }
  125. // Find display markers with no matching define and replace them with
  126. // an error message.
  127. $regex = "/" . $this->mMarkerHead . "(\d+)" . $this->mMarkerTail . "/";
  128. $matches = array();
  129. preg_match_all( $regex, $text, $matches );
  130. $rev = array_flip( $this->mTransTable );
  131. foreach ( $matches[0] as $k => $v ) {
  132. $key = $matches[1][$k];
  133. $missing = '<strong class="error">' .
  134. wfMsgForContent( 'delaydef-error-missing-def', $rev[intval( $key )] ) .
  135. '</strong>';
  136. $text = str_replace( $v, $missing, $text );
  137. }
  138. return true;
  139. }
  140. }