MediaWiki  1.23.15
LanguageConverter.php
Go to the documentation of this file.
1 <?php
29 class LanguageConverter {
30 
36  static public $languagesWithVariants = array(
37  'gan',
38  'iu',
39  'kk',
40  'ku',
41  'shi',
42  'sr',
43  'tg',
44  'uz',
45  'zh',
46  );
47 
48  public $mMainLanguageCode;
49  public $mVariants, $mVariantFallbacks, $mVariantNames;
50  public $mTablesLoaded = false;
51  public $mTables;
52  // 'bidirectional' 'unidirectional' 'disable' for each variant
53  public $mManualLevel;
54 
58  public $mCacheKey;
59 
60  public $mLangObj;
61  public $mFlags;
62  public $mDescCodeSep = ':', $mDescVarSep = ';';
63  public $mUcfirst = false;
64  public $mConvRuleTitle = false;
65  public $mURLVariant;
66  public $mUserVariant;
67  public $mHeaderVariant;
68  public $mMaxDepth = 10;
69  public $mVarSeparatorPattern;
70 
71  const CACHE_VERSION_KEY = 'VERSION 7';
72 
83  public function __construct( $langobj, $maincode, $variants = array(),
84  $variantfallbacks = array(), $flags = array(),
85  $manualLevel = array() ) {
86  global $wgDisabledVariants;
87  $this->mLangObj = $langobj;
88  $this->mMainLanguageCode = $maincode;
89  $this->mVariants = array_diff( $variants, $wgDisabledVariants );
90  $this->mVariantFallbacks = $variantfallbacks;
91  $this->mVariantNames = Language::fetchLanguageNames();
92  $this->mCacheKey = wfMemcKey( 'conversiontables', $maincode );
93  $defaultflags = array(
94  // 'S' show converted text
95  // '+' add rules for alltext
96  // 'E' the gave flags is error
97  // these flags above are reserved for program
98  'A' => 'A', // add rule for convert code (all text convert)
99  'T' => 'T', // title convert
100  'R' => 'R', // raw content
101  'D' => 'D', // convert description (subclass implement)
102  '-' => '-', // remove convert (not implement)
103  'H' => 'H', // add rule for convert code (but no display in placed code)
104  'N' => 'N' // current variant name
105  );
106  $this->mFlags = array_merge( $defaultflags, $flags );
107  foreach ( $this->mVariants as $v ) {
108  if ( array_key_exists( $v, $manualLevel ) ) {
109  $this->mManualLevel[$v] = $manualLevel[$v];
110  } else {
111  $this->mManualLevel[$v] = 'bidirectional';
112  }
113  $this->mFlags[$v] = $v;
114  }
115  }
116 
123  public function getVariants() {
124  return $this->mVariants;
125  }
126 
138  public function getVariantFallbacks( $variant ) {
139  if ( isset( $this->mVariantFallbacks[$variant] ) ) {
140  return $this->mVariantFallbacks[$variant];
141  }
142  return $this->mMainLanguageCode;
143  }
144 
149  public function getConvRuleTitle() {
150  return $this->mConvRuleTitle;
151  }
152 
157  public function getPreferredVariant() {
158  global $wgDefaultLanguageVariant, $wgUser;
159 
160  $req = $this->getURLVariant();
161 
162  if ( $wgUser->isLoggedIn() && !$req ) {
163  $req = $this->getUserVariant();
164  } elseif ( !$req ) {
165  $req = $this->getHeaderVariant();
166  }
167 
168  if ( $wgDefaultLanguageVariant && !$req ) {
169  $req = $this->validateVariant( $wgDefaultLanguageVariant );
170  }
171 
172  // This function, unlike the other get*Variant functions, is
173  // not memoized (i.e. there return value is not cached) since
174  // new information might appear during processing after this
175  // is first called.
176  if ( $this->validateVariant( $req ) ) {
177  return $req;
178  }
179  return $this->mMainLanguageCode;
180  }
181 
187  public function getDefaultVariant() {
188  global $wgDefaultLanguageVariant;
189 
190  $req = $this->getURLVariant();
191 
192  if ( !$req ) {
193  $req = $this->getHeaderVariant();
194  }
195 
196  if ( $wgDefaultLanguageVariant && !$req ) {
197  $req = $this->validateVariant( $wgDefaultLanguageVariant );
198  }
199 
200  if ( $req ) {
201  return $req;
202  }
203  return $this->mMainLanguageCode;
204  }
205 
211  public function validateVariant( $variant = null ) {
212  if ( $variant !== null && in_array( $variant, $this->mVariants ) ) {
213  return $variant;
214  }
215  return null;
216  }
217 
223  public function getURLVariant() {
224  global $wgRequest;
225 
226  if ( $this->mURLVariant ) {
227  return $this->mURLVariant;
228  }
229 
230  // see if the preference is set in the request
231  $ret = $wgRequest->getText( 'variant' );
232 
233  if ( !$ret ) {
234  $ret = $wgRequest->getVal( 'uselang' );
235  }
236 
237  $this->mURLVariant = $this->validateVariant( $ret );
238  return $this->mURLVariant;
239  }
240 
246  protected function getUserVariant() {
248 
249  // memoizing this function wreaks havoc on parserTest.php
250  /*
251  if ( $this->mUserVariant ) {
252  return $this->mUserVariant;
253  }
254  */
255 
256  // Get language variant preference from logged in users
257  // Don't call this on stub objects because that causes infinite
258  // recursion during initialisation
259  if ( $wgUser->isLoggedIn() ) {
260  if ( $this->mMainLanguageCode == $wgContLang->getCode() ) {
261  $ret = $wgUser->getOption( 'variant' );
262  } else {
263  $ret = $wgUser->getOption( 'variant-' . $this->mMainLanguageCode );
264  }
265  } else {
266  // figure out user lang without constructing wgLang to avoid
267  // infinite recursion
268  $ret = $wgUser->getOption( 'language' );
269  }
270 
271  $this->mUserVariant = $this->validateVariant( $ret );
272  return $this->mUserVariant;
273  }
274 
280  protected function getHeaderVariant() {
281  global $wgRequest;
282 
283  if ( $this->mHeaderVariant ) {
284  return $this->mHeaderVariant;
285  }
286 
287  // see if some supported language variant is set in the
288  // HTTP header.
289  $languages = array_keys( $wgRequest->getAcceptLang() );
290  if ( empty( $languages ) ) {
291  return null;
292  }
293 
294  $fallbackLanguages = array();
295  foreach ( $languages as $language ) {
296  $this->mHeaderVariant = $this->validateVariant( $language );
297  if ( $this->mHeaderVariant ) {
298  break;
299  }
300 
301  // To see if there are fallbacks of current language.
302  // We record these fallback variants, and process
303  // them later.
304  $fallbacks = $this->getVariantFallbacks( $language );
305  if ( is_string( $fallbacks ) && $fallbacks !== $this->mMainLanguageCode ) {
306  $fallbackLanguages[] = $fallbacks;
307  } elseif ( is_array( $fallbacks ) ) {
308  $fallbackLanguages =
309  array_merge( $fallbackLanguages, $fallbacks );
310  }
311  }
312 
313  if ( !$this->mHeaderVariant ) {
314  // process fallback languages now
315  $fallback_languages = array_unique( $fallbackLanguages );
316  foreach ( $fallback_languages as $language ) {
317  $this->mHeaderVariant = $this->validateVariant( $language );
318  if ( $this->mHeaderVariant ) {
319  break;
320  }
321  }
322  }
323 
324  return $this->mHeaderVariant;
325  }
326 
337  public function autoConvert( $text, $toVariant = false ) {
338  wfProfileIn( __METHOD__ );
339 
340  $this->loadTables();
341 
342  if ( !$toVariant ) {
343  $toVariant = $this->getPreferredVariant();
344  if ( !$toVariant ) {
345  wfProfileOut( __METHOD__ );
346  return $text;
347  }
348  }
349 
350  if ( $this->guessVariant( $text, $toVariant ) ) {
351  wfProfileOut( __METHOD__ );
352  return $text;
353  }
354 
355  /* we convert everything except:
356  1. HTML markups (anything between < and >)
357  2. HTML entities
358  3. placeholders created by the parser
359  */
361  if ( isset( $wgParser ) && $wgParser->UniqPrefix() != '' ) {
362  $marker = '|' . $wgParser->UniqPrefix() . '[\-a-zA-Z0-9]+';
363  } else {
364  $marker = '';
365  }
366 
367  // this one is needed when the text is inside an HTML markup
368  $htmlfix = '|<[^>]+$|^[^<>]*>';
369 
370  // disable convert to variants between <code> tags
371  $codefix = '<code>.+?<\/code>|';
372  // disable conversion of <script> tags
373  $scriptfix = '<script.*?>.*?<\/script>|';
374  // disable conversion of <pre> tags
375  $prefix = '<pre.*?>.*?<\/pre>|';
376 
377  $reg = '/' . $codefix . $scriptfix . $prefix .
378  '<[^>]+>|&[a-zA-Z#][a-z0-9]+;' . $marker . $htmlfix . '/s';
379  $startPos = 0;
380  $sourceBlob = '';
381  $literalBlob = '';
382 
383  // Guard against delimiter nulls in the input
384  $text = str_replace( "\000", '', $text );
385 
386  $markupMatches = null;
387  $elementMatches = null;
388  while ( $startPos < strlen( $text ) ) {
389  if ( preg_match( $reg, $text, $markupMatches, PREG_OFFSET_CAPTURE, $startPos ) ) {
390  $elementPos = $markupMatches[0][1];
391  $element = $markupMatches[0][0];
392  } else {
393  $elementPos = strlen( $text );
394  $element = '';
395  }
396 
397  // Queue the part before the markup for translation in a batch
398  $sourceBlob .= substr( $text, $startPos, $elementPos - $startPos ) . "\000";
399 
400  // Advance to the next position
401  $startPos = $elementPos + strlen( $element );
402 
403  // Translate any alt or title attributes inside the matched element
404  if ( $element !== ''
405  && preg_match( '/^(<[^>\s]*)\s([^>]*)(.*)$/', $element, $elementMatches )
406  ) {
407  $attrs = Sanitizer::decodeTagAttributes( $elementMatches[2] );
408  $changed = false;
409  foreach ( array( 'title', 'alt' ) as $attrName ) {
410  if ( !isset( $attrs[$attrName] ) ) {
411  continue;
412  }
413  $attr = $attrs[$attrName];
414  // Don't convert URLs
415  if ( !strpos( $attr, '://' ) ) {
416  $attr = $this->recursiveConvertTopLevel( $attr, $toVariant );
417  }
418 
419  // Remove HTML tags to avoid disrupting the layout
420  $attr = preg_replace( '/<[^>]+>/', '', $attr );
421  if ( $attr !== $attrs[$attrName] ) {
422  $attrs[$attrName] = $attr;
423  $changed = true;
424  }
425  }
426  if ( $changed ) {
427  $element = $elementMatches[1] . Html::expandAttributes( $attrs ) .
428  $elementMatches[3];
429  }
430  }
431  $literalBlob .= $element . "\000";
432  }
433 
434  // Do the main translation batch
435  $translatedBlob = $this->translate( $sourceBlob, $toVariant );
436 
437  // Put the output back together
438  $translatedIter = StringUtils::explode( "\000", $translatedBlob );
439  $literalIter = StringUtils::explode( "\000", $literalBlob );
440  $output = '';
441  while ( $translatedIter->valid() && $literalIter->valid() ) {
442  $output .= $translatedIter->current();
443  $output .= $literalIter->current();
444  $translatedIter->next();
445  $literalIter->next();
446  }
447 
448  wfProfileOut( __METHOD__ );
449  return $output;
450  }
451 
461  public function translate( $text, $variant ) {
462  wfProfileIn( __METHOD__ );
463  // If $text is empty or only includes spaces, do nothing
464  // Otherwise translate it
465  if ( trim( $text ) ) {
466  $this->loadTables();
467  $text = $this->mTables[$variant]->replace( $text );
468  }
469  wfProfileOut( __METHOD__ );
470  return $text;
471  }
472 
479  public function autoConvertToAllVariants( $text ) {
480  wfProfileIn( __METHOD__ );
481  $this->loadTables();
482 
483  $ret = array();
484  foreach ( $this->mVariants as $variant ) {
485  $ret[$variant] = $this->translate( $text, $variant );
486  }
487 
488  wfProfileOut( __METHOD__ );
489  return $ret;
490  }
491 
497  protected function applyManualConv( $convRule ) {
498  // Use syntax -{T|zh-cn:TitleCN; zh-tw:TitleTw}- to custom
499  // title conversion.
500  // Bug 24072: $mConvRuleTitle was overwritten by other manual
501  // rule(s) not for title, this breaks the title conversion.
502  $newConvRuleTitle = $convRule->getTitle();
503  if ( $newConvRuleTitle ) {
504  // So I add an empty check for getTitle()
505  $this->mConvRuleTitle = $newConvRuleTitle;
506  }
507 
508  // merge/remove manual conversion rules to/from global table
509  $convTable = $convRule->getConvTable();
510  $action = $convRule->getRulesAction();
511  foreach ( $convTable as $variant => $pair ) {
512  if ( !$this->validateVariant( $variant ) ) {
513  continue;
514  }
515 
516  if ( $action == 'add' ) {
517  foreach ( $pair as $from => $to ) {
518  // to ensure that $from and $to not be left blank
519  // so $this->translate() could always return a string
520  if ( $from || $to ) {
521  // more efficient than array_merge(), about 2.5 times.
522  $this->mTables[$variant]->setPair( $from, $to );
523  }
524  }
525  } elseif ( $action == 'remove' ) {
526  $this->mTables[$variant]->removeArray( $pair );
527  }
528  }
529  }
530 
538  public function convertTitle( $title ) {
539  $variant = $this->getPreferredVariant();
540  $index = $title->getNamespace();
541  if ( $index !== NS_MAIN ) {
542  $text = $this->convertNamespace( $index, $variant ) . ':';
543  } else {
544  $text = '';
545  }
546  $text .= $this->translate( $title->getText(), $variant );
547  return $text;
548  }
549 
557  public function convertNamespace( $index, $variant = null ) {
558  if ( $variant === null ) {
559  $variant = $this->getPreferredVariant();
560  }
561  if ( $index === NS_MAIN ) {
562  return '';
563  } else {
564  // First check if a message gives a converted name in the target variant.
565  $nsConvMsg = wfMessage( 'conversion-ns' . $index )->inLanguage( $variant );
566  if ( $nsConvMsg->exists() ) {
567  return $nsConvMsg->plain();
568  }
569  // Then check if a message gives a converted name in content language
570  // which needs extra translation to the target variant.
571  $nsConvMsg = wfMessage( 'conversion-ns' . $index )->inContentLanguage();
572  if ( $nsConvMsg->exists() ) {
573  return $this->translate( $nsConvMsg->plain(), $variant );
574  }
575  // No message exists, retrieve it from the target variant's namespace names.
576  $langObj = $this->mLangObj->factory( $variant );
577  return $langObj->getFormattedNsText( $index );
578  }
579  }
580 
595  public function convert( $text ) {
596  $variant = $this->getPreferredVariant();
597  return $this->convertTo( $text, $variant );
598  }
599 
607  public function convertTo( $text, $variant ) {
608  global $wgDisableLangConversion;
609  if ( $wgDisableLangConversion ) {
610  return $text;
611  }
612  // Reset converter state for a new converter run.
613  $this->mConvRuleTitle = false;
614  return $this->recursiveConvertTopLevel( $text, $variant );
615  }
616 
626  protected function recursiveConvertTopLevel( $text, $variant, $depth = 0 ) {
627  $startPos = 0;
628  $out = '';
629  $length = strlen( $text );
630  $shouldConvert = !$this->guessVariant( $text, $variant );
631 
632  while ( $startPos < $length ) {
633  $pos = strpos( $text, '-{', $startPos );
634 
635  if ( $pos === false ) {
636  // No more markup, append final segment
637  $fragment = substr( $text, $startPos );
638  $out .= $shouldConvert ? $this->autoConvert( $fragment, $variant ) : $fragment;
639  return $out;
640  }
641 
642  // Markup found
643  // Append initial segment
644  $fragment = substr( $text, $startPos, $pos - $startPos );
645  $out .= $shouldConvert ? $this->autoConvert( $fragment, $variant ) : $fragment;
646 
647  // Advance position
648  $startPos = $pos;
649 
650  // Do recursive conversion
651  $out .= $this->recursiveConvertRule( $text, $variant, $startPos, $depth + 1 );
652  }
653 
654  return $out;
655  }
656 
668  protected function recursiveConvertRule( $text, $variant, &$startPos, $depth = 0 ) {
669  // Quick sanity check (no function calls)
670  if ( $text[$startPos] !== '-' || $text[$startPos + 1] !== '{' ) {
671  throw new MWException( __METHOD__ . ': invalid input string' );
672  }
673 
674  $startPos += 2;
675  $inner = '';
676  $warningDone = false;
677  $length = strlen( $text );
678 
679  while ( $startPos < $length ) {
680  $m = false;
681  preg_match( '/-\{|\}-/', $text, $m, PREG_OFFSET_CAPTURE, $startPos );
682  if ( !$m ) {
683  // Unclosed rule
684  break;
685  }
686 
687  $token = $m[0][0];
688  $pos = $m[0][1];
689 
690  // Markup found
691  // Append initial segment
692  $inner .= substr( $text, $startPos, $pos - $startPos );
693 
694  // Advance position
695  $startPos = $pos;
696 
697  switch ( $token ) {
698  case '-{':
699  // Check max depth
700  if ( $depth >= $this->mMaxDepth ) {
701  $inner .= '-{';
702  if ( !$warningDone ) {
703  $inner .= '<span class="error">' .
704  wfMessage( 'language-converter-depth-warning' )
705  ->numParams( $this->mMaxDepth )->inContentLanguage()->text() .
706  '</span>';
707  $warningDone = true;
708  }
709  $startPos += 2;
710  continue;
711  }
712  // Recursively parse another rule
713  $inner .= $this->recursiveConvertRule( $text, $variant, $startPos, $depth + 1 );
714  break;
715  case '}-':
716  // Apply the rule
717  $startPos += 2;
718  $rule = new ConverterRule( $inner, $this );
719  $rule->parse( $variant );
720  $this->applyManualConv( $rule );
721  return $rule->getDisplay();
722  default:
723  throw new MWException( __METHOD__ . ': invalid regex match' );
724  }
725  }
726 
727  // Unclosed rule
728  if ( $startPos < $length ) {
729  $inner .= substr( $text, $startPos );
730  }
731  $startPos = $length;
732  return '-{' . $this->autoConvert( $inner, $variant );
733  }
734 
746  public function findVariantLink( &$link, &$nt, $ignoreOtherCond = false ) {
747  # If the article has already existed, there is no need to
748  # check it again, otherwise it may cause a fault.
749  if ( is_object( $nt ) && $nt->exists() ) {
750  return;
751  }
752 
753  global $wgDisableLangConversion, $wgDisableTitleConversion, $wgRequest;
754  $isredir = $wgRequest->getText( 'redirect', 'yes' );
755  $action = $wgRequest->getText( 'action' );
756  $linkconvert = $wgRequest->getText( 'linkconvert', 'yes' );
757  $disableLinkConversion = $wgDisableLangConversion
758  || $wgDisableTitleConversion;
759  $linkBatch = new LinkBatch();
760 
761  $ns = NS_MAIN;
762 
763  if ( $disableLinkConversion ||
764  ( !$ignoreOtherCond &&
765  ( $isredir == 'no'
766  || $action == 'edit'
767  || $action == 'submit'
768  || $linkconvert == 'no' ) ) ) {
769  return;
770  }
771 
772  if ( is_object( $nt ) ) {
773  $ns = $nt->getNamespace();
774  }
775 
776  $variants = $this->autoConvertToAllVariants( $link );
777  if ( !$variants ) { // give up
778  return;
779  }
780 
781  $titles = array();
782 
783  foreach ( $variants as $v ) {
784  if ( $v != $link ) {
785  $varnt = Title::newFromText( $v, $ns );
786  if ( !is_null( $varnt ) ) {
787  $linkBatch->addObj( $varnt );
788  $titles[] = $varnt;
789  }
790  }
791  }
792 
793  // fetch all variants in single query
794  $linkBatch->execute();
795 
796  foreach ( $titles as $varnt ) {
797  if ( $varnt->getArticleID() > 0 ) {
798  $nt = $varnt;
799  $link = $varnt->getText();
800  break;
801  }
802  }
803  }
804 
810  public function getExtraHashOptions() {
811  $variant = $this->getPreferredVariant();
812  return '!' . $variant;
813  }
814 
825  public function guessVariant( $text, $variant ) {
826  return false;
827  }
828 
836  function loadDefaultTables() {
837  $name = get_class( $this );
838  throw new MWException( "Must implement loadDefaultTables() method in class $name" );
839  }
840 
846  function loadTables( $fromCache = true ) {
848 
849  if ( $this->mTablesLoaded ) {
850  return;
851  }
852 
853  wfProfileIn( __METHOD__ );
854  $this->mTablesLoaded = true;
855  $this->mTables = false;
856  if ( $fromCache ) {
857  wfProfileIn( __METHOD__ . '-cache' );
858  $this->mTables = $wgLangConvMemc->get( $this->mCacheKey );
859  wfProfileOut( __METHOD__ . '-cache' );
860  }
861  if ( !$this->mTables || !array_key_exists( self::CACHE_VERSION_KEY, $this->mTables ) ) {
862  wfProfileIn( __METHOD__ . '-recache' );
863  // not in cache, or we need a fresh reload.
864  // We will first load the default tables
865  // then update them using things in MediaWiki:Conversiontable/*
866  $this->loadDefaultTables();
867  foreach ( $this->mVariants as $var ) {
868  $cached = $this->parseCachedTable( $var );
869  $this->mTables[$var]->mergeArray( $cached );
870  }
871 
872  $this->postLoadTables();
873  $this->mTables[self::CACHE_VERSION_KEY] = true;
874 
875  $wgLangConvMemc->set( $this->mCacheKey, $this->mTables, 43200 );
876  wfProfileOut( __METHOD__ . '-recache' );
877  }
878  wfProfileOut( __METHOD__ );
879  }
880 
884  function postLoadTables() { }
885 
891  function reloadTables() {
892  if ( $this->mTables ) {
893  unset( $this->mTables );
894  }
895  $this->mTablesLoaded = false;
896  $this->loadTables( false );
897  }
898 
918  function parseCachedTable( $code, $subpage = '', $recursive = true ) {
919  static $parsed = array();
920 
921  $key = 'Conversiontable/' . $code;
922  if ( $subpage ) {
923  $key .= '/' . $subpage;
924  }
925  if ( array_key_exists( $key, $parsed ) ) {
926  return array();
927  }
928 
929  $parsed[$key] = true;
930 
931  if ( $subpage === '' ) {
932  $txt = MessageCache::singleton()->getMsgFromNamespace( $key, $code );
933  } else {
934  $txt = false;
936  if ( $title && $title->exists() ) {
937  $revision = Revision::newFromTitle( $title );
938  if ( $revision ) {
939  if ( $revision->getContentModel() == CONTENT_MODEL_WIKITEXT ) {
940  $txt = $revision->getContent( Revision::RAW )->getNativeData();
941  }
942 
943  // @todo in the future, use a specialized content model, perhaps based on json!
944  }
945  }
946  }
947 
948  # Nothing to parse if there's no text
949  if ( $txt === false || $txt === null || $txt === '' ) {
950  return array();
951  }
952 
953  // get all subpage links of the form
954  // [[MediaWiki:Conversiontable/zh-xx/...|...]]
955  $linkhead = $this->mLangObj->getNsText( NS_MEDIAWIKI ) .
956  ':Conversiontable';
957  $subs = StringUtils::explode( '[[', $txt );
958  $sublinks = array();
959  foreach ( $subs as $sub ) {
960  $link = explode( ']]', $sub, 2 );
961  if ( count( $link ) != 2 ) {
962  continue;
963  }
964  $b = explode( '|', $link[0], 2 );
965  $b = explode( '/', trim( $b[0] ), 3 );
966  if ( count( $b ) == 3 ) {
967  $sublink = $b[2];
968  } else {
969  $sublink = '';
970  }
971 
972  if ( $b[0] == $linkhead && $b[1] == $code ) {
973  $sublinks[] = $sublink;
974  }
975  }
976 
977  // parse the mappings in this page
978  $blocks = StringUtils::explode( '-{', $txt );
979  $ret = array();
980  $first = true;
981  foreach ( $blocks as $block ) {
982  if ( $first ) {
983  // Skip the part before the first -{
984  $first = false;
985  continue;
986  }
987  $mappings = explode( '}-', $block, 2 );
988  $stripped = str_replace( array( "'", '"', '*', '#' ), '', $mappings[0] );
989  $table = StringUtils::explode( ';', $stripped );
990  foreach ( $table as $t ) {
991  $m = explode( '=>', $t, 3 );
992  if ( count( $m ) != 2 ) {
993  continue;
994  }
995  // trim any trailling comments starting with '//'
996  $tt = explode( '//', $m[1], 2 );
997  $ret[trim( $m[0] )] = trim( $tt[0] );
998  }
999  }
1000 
1001  // recursively parse the subpages
1002  if ( $recursive ) {
1003  foreach ( $sublinks as $link ) {
1004  $s = $this->parseCachedTable( $code, $link, $recursive );
1005  $ret = array_merge( $ret, $s );
1006  }
1007  }
1008 
1009  if ( $this->mUcfirst ) {
1010  foreach ( $ret as $k => $v ) {
1011  $ret[$this->mLangObj->ucfirst( $k )] = $this->mLangObj->ucfirst( $v );
1012  }
1013  }
1014  return $ret;
1015  }
1016 
1025  public function markNoConversion( $text, $noParse = false ) {
1026  # don't mark if already marked
1027  if ( strpos( $text, '-{' ) || strpos( $text, '}-' ) ) {
1028  return $text;
1029  }
1030 
1031  $ret = "-{R|$text}-";
1032  return $ret;
1033  }
1034 
1043  function convertCategoryKey( $key ) {
1044  return $key;
1045  }
1046 
1063  function OnPageContentSaveComplete( $page, $user, $content, $summary, $isMinor,
1064  $isWatch, $section, $flags, $revision ) {
1065  $titleobj = $page->getTitle();
1066  if ( $titleobj->getNamespace() == NS_MEDIAWIKI ) {
1067  $title = $titleobj->getDBkey();
1068  $t = explode( '/', $title, 3 );
1069  $c = count( $t );
1070  if ( $c > 1 && $t[0] == 'Conversiontable' ) {
1071  if ( $this->validateVariant( $t[1] ) ) {
1072  $this->reloadTables();
1073  }
1074  }
1075  }
1076  return true;
1077  }
1078 
1088  public function armourMath( $text ) {
1089  // convert '-{' and '}-' to '-&#123;' and '&#125;-' to prevent
1090  // any unwanted markup appearing in the math image tag.
1091  $text = strtr( $text, array( '-{' => '-&#123;', '}-' => '&#125;-' ) );
1092  return $text;
1093  }
1094 
1098  function getVarSeparatorPattern() {
1099  if ( is_null( $this->mVarSeparatorPattern ) ) {
1100  // varsep_pattern for preg_split:
1101  // text should be splited by ";" only if a valid variant
1102  // name exist after the markup, for example:
1103  // -{zh-hans:<span style="font-size:120%;">xxx</span>;zh-hant:\
1104  // <span style="font-size:120%;">yyy</span>;}-
1105  // we should split it as:
1106  // array(
1107  // [0] => 'zh-hans:<span style="font-size:120%;">xxx</span>'
1108  // [1] => 'zh-hant:<span style="font-size:120%;">yyy</span>'
1109  // [2] => ''
1110  // )
1111  $pat = '/;\s*(?=';
1112  foreach ( $this->mVariants as $variant ) {
1113  // zh-hans:xxx;zh-hant:yyy
1114  $pat .= $variant . '\s*:|';
1115  // xxx=>zh-hans:yyy; xxx=>zh-hant:zzz
1116  $pat .= '[^;]*?=>\s*' . $variant . '\s*:|';
1117  }
1118  $pat .= '\s*$)/';
1119  $this->mVarSeparatorPattern = $pat;
1120  }
1121  return $this->mVarSeparatorPattern;
1122  }
1123 }
ConverterRule
Parser for rules of language conversion , parse rules in -{ }- tag.
Definition: ConverterRule.php:27
$wgUser
$wgUser
Definition: Setup.php:572
Title\newFromText
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:189
php
skin txt MediaWiki includes four core it has been set as the default in MediaWiki since the replacing Monobook it had been been the default skin since before being replaced by Vector largely rewritten in while keeping its appearance Several legacy skins were removed in the as the burden of supporting them became too heavy to bear Those in etc for skin dependent CSS etc for skin dependent JavaScript These can also be customised on a per user by etc This feature has led to a wide variety of user styles becoming that gallery is a good place to ending in php
Definition: skin.txt:62
LinkBatch
Class representing a list of titles The execute() method checks them all for existence and adds them ...
Definition: LinkBatch.php:30
wfProfileIn
wfProfileIn( $functionname)
Begin profiling of a function.
Definition: Profiler.php:33
$ret
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
Definition: hooks.txt:1530
$from
$from
Definition: importImages.php:90
Html\expandAttributes
static expandAttributes( $attribs)
Given an associative array of element attributes, generate a string to stick after the element name i...
Definition: Html.php:383
$s
$s
Definition: mergeMessageFileList.php:156
$wgContLang
this class mediates it Skin Encapsulates a look and feel for the wiki All of the functions that render HTML and make choices about how to render it are here and are called from various other places when and is meant to be subclassed with other skins that may override some of its functions The User object contains a reference to a and so rather than having a global skin object we just rely on the global User and get the skin with $wgUser and also has some character encoding functions and other locale stuff The current user interface language is instantiated as and the content language as $wgContLang
Definition: design.txt:56
CONTENT_MODEL_WIKITEXT
const CONTENT_MODEL_WIKITEXT
Definition: Defines.php:283
$flags
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition: hooks.txt:2124
$link
set to $title object and return false for a match for latest after cache objects are set use the ContentHandler facility to handle CSS and JavaScript for highlighting & $link
Definition: hooks.txt:2160
NS_MAIN
const NS_MAIN
Definition: Defines.php:79
MWException
MediaWiki exception.
Definition: MWException.php:26
wfMemcKey
wfMemcKey()
Get a cache key.
Definition: GlobalFunctions.php:3635
$out
$out
Definition: UtfNormalGenerate.php:167
$titles
linkcache txt The LinkCache class maintains a list of article titles and the information about whether or not the article exists in the database This is used to mark up links when displaying a page If the same link appears more than once on any page then it only has to be looked up once In most cases link lookups are done in batches with the LinkBatch class or the equivalent in so the link cache is mostly useful for short snippets of parsed and for links in the navigation areas of the skin The link cache was formerly used to track links used in a document for the purposes of updating the link tables This application is now deprecated To create a you can use the following $titles
Definition: linkcache.txt:17
Language\fetchLanguageNames
static fetchLanguageNames( $inLanguage=null, $include='mw')
Get an array of language names, indexed by code.
Definition: Language.php:875
StringUtils\explode
static explode( $separator, $subject)
Workalike for explode() with limited memory usage.
Definition: StringUtils.php:310
wfProfileOut
wfProfileOut( $functionname='missing')
Stop profiling of a function.
Definition: Profiler.php:46
wfMessage
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned and may include noclasses after processing after in associative array form externallinks including delete and has completed for all link tables default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "&lt
$wgLangConvMemc
$wgLangConvMemc
Definition: Setup.php:536
array
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
MessageCache\singleton
static singleton()
Get the signleton instance of this class.
Definition: MessageCache.php:101
$section
$section
Definition: Utf8Test.php:88
Revision\newFromTitle
static newFromTitle( $title, $id=0, $flags=0)
Load either the current, or a specified, revision that's attached to a given title.
Definition: Revision.php:106
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:422
$languages
$languages
Definition: rebuildLanguage.php:129
$title
presenting them properly to the user as errors is done by the caller $title
Definition: hooks.txt:1324
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:336
Revision\RAW
const RAW
Definition: Revision.php:74
$user
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account $user
Definition: hooks.txt:237
$summary
$summary
Definition: importImages.php:120
$wgParser
$wgParser
Definition: Setup.php:587
Makefile.translate
def translate(text, conv_table)
Definition: Makefile.py:216
$output
& $output
Definition: hooks.txt:375
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
Sanitizer\decodeTagAttributes
static decodeTagAttributes( $text)
Return an associative array of attribute names and values from a partial tag string.
Definition: Sanitizer.php:1183
NS_MEDIAWIKI
const NS_MEDIAWIKI
Definition: Defines.php:87
$t
$t
Definition: testCompression.php:65
$changed
$changed
Definition: UtfNormalGenerate.php:130