Introduce $wgBetterDirectionality that lets us work on support for rtl ui in ltr...
[lhc/web/wiklou.git] / includes / GlobalFunctions.php
index 5d339fc..132519c 100644 (file)
@@ -9,7 +9,6 @@ if ( !defined( 'MEDIAWIKI' ) ) {
  */
 
 require_once dirname(__FILE__) . '/normal/UtfNormalUtil.php';
-require_once dirname(__FILE__) . '/XmlFunctions.php';
 
 // Hide compatibility functions from Doxygen
 /// @cond
@@ -173,54 +172,6 @@ if( !function_exists( 'mb_strrpos' ) ) {
        }
 }
 
-if ( !function_exists( 'array_diff_key' ) ) {
-       /**
-        * Exists in PHP 5.1.0+
-        * Not quite compatible, two-argument version only
-        * Null values will cause problems due to this use of isset()
-        */
-       function array_diff_key( $left, $right ) {
-               $result = $left;
-               foreach ( $left as $key => $unused ) {
-                       if ( isset( $right[$key] ) ) {
-                               unset( $result[$key] );
-                       }
-               }
-               return $result;
-       }
-}
-
-if ( !function_exists( 'array_intersect_key' ) ) {
-       /**
-       * Exists in 5.1.0+
-       * Define our own array_intersect_key function
-       */
-       function array_intersect_key( $isec, $keys ) {
-               $argc = func_num_args();
-
-               if ( $argc > 2 ) {
-                       for ( $i = 1; $isec && $i < $argc; $i++ ) {
-                               $arr = func_get_arg( $i );
-
-                               foreach ( array_keys( $isec ) as $key ) {
-                                       if ( !isset( $arr[$key] ) )
-                                               unset( $isec[$key] );
-                               }
-                       }
-
-                       return $isec;
-               } else {
-                       $res = array();
-                       foreach ( array_keys( $isec ) as $key ) {
-                               if ( isset( $keys[$key] ) )
-                                       $res[$key] = $isec[$key];
-                       }
-
-                       return $res;
-               }
-       }
-}
-
 // Support for Wietse Venema's taint feature
 if ( !function_exists( 'istainted' ) ) {
        function istainted( $var ) {
@@ -299,16 +250,27 @@ function wfRandom() {
  *
  * ;:@$!*(),/
  *
+ * However, IIS7 redirects fail when the url contains a colon (Bug 22709), 
+ * so no fancy : for IIS7.
+ * 
  * %2F in the page titles seems to fatally break for some reason.
  *
  * @param $s String:
  * @return string
 */
 function wfUrlencode( $s ) {
+       static $needle;
+       if ( is_null( $needle ) ) {
+               $needle = array( '%3B','%40','%24','%21','%2A','%28','%29','%2C','%2F' );
+               if (! isset($_SERVER['SERVER_SOFTWARE']) || ( strpos($_SERVER['SERVER_SOFTWARE'], "Microsoft-IIS/7") === false)) {
+                       $needle[] = '%3A';
+               }
+       }               
+       
        $s = urlencode( $s );
        $s = str_ireplace(
-               array( '%3B','%3A','%40','%24','%21','%2A','%28','%29','%2C','%2F' ),
-               array(   ';',  ':',  '@',  '$',  '!',  '*',  '(',  ')',  ',',  '/' ),
+               $needle,
+               array( ';',  '@',  '$',  '!',  '*',  '(',  ')',  ',',  '/',  ':' ),
                $s
        );
 
@@ -496,7 +458,7 @@ function wfLogProfilingData() {
        global $wgRequestTime, $wgDebugLogFile, $wgDebugRawPage, $wgRequest;
        global $wgProfiler, $wgProfileLimit, $wgUser;
        # Profiling must actually be enabled...
-       if( !is_null( $wgProfiler ) ) return;
+       if( is_null( $wgProfiler ) ) return;
        # Get total page request time
        $now = wfTime();
        $elapsed = $now - $wgRequestTime;
@@ -566,34 +528,44 @@ function wfReadOnlyReason() {
  *                    functionality), or if it is true then use the wikis
  * @return Language object
  */
-function wfGetLangObj( $langcode = false ){
+function wfGetLangObj( $langcode = false ) {
        # Identify which language to get or create a language object for.
-       if( $langcode instanceof Language )
-               # Great, we already have the object!
+       # Using is_object here due to Stub objects.
+       if( is_object( $langcode ) ) {
+               # Great, we already have the object (hopefully)!
                return $langcode;
+       }
                
-       global $wgContLang;
-       if( $langcode === $wgContLang->getCode() || $langcode === true )
+       global $wgContLang, $wgLanguageCode;
+       if( $langcode === true || $langcode === $wgLanguageCode ) {
                # $langcode is the language code of the wikis content language object.
                # or it is a boolean and value is true
                return $wgContLang;
+       }
        
        global $wgLang;
-       if( $langcode === $wgLang->getCode() || $langcode === false )
+       if( $langcode === false || $langcode === $wgLang->getCode() ) {
                # $langcode is the language code of user language object.
                # or it was a boolean and value is false
                return $wgLang;
+       }
 
        $validCodes = array_keys( Language::getLanguageNames() );
-       if( in_array( $langcode, $validCodes ) )
+       if( in_array( $langcode, $validCodes ) ) {
                # $langcode corresponds to a valid language.
                return Language::factory( $langcode );
+       }
 
        # $langcode is a string, but not a valid language code; use content language.
        wfDebug( "Invalid language code passed to wfGetLangObj, falling back to content language.\n" );
        return $wgContLang;
 }
 
+function wfUILang() {
+       global $wgBetterDirectionality;
+       return wfGetLangObj( $wgBetterDirectionality ? false: true );
+}
+
 /**
  * Get a message from anywhere, for the current user language.
  *
@@ -638,8 +610,8 @@ function wfMsgNoTrans( $key ) {
  *
  * Be wary of this distinction: If you use wfMsg() where you should
  * use wfMsgForContent(), a user of the software may have to
- * customize over 70 messages in order to, e.g., fix a link in every
- * possible language.
+ * customize potentially hundreds of messages in
+ * order to, e.g., fix a link in every possible language.
  *
  * @param $key String: lookup key for the message, usually
  *    defined in languages/Language.php
@@ -698,8 +670,8 @@ function wfMsgNoDBForContent( $key ) {
  * @param $key String: key to get.
  * @param $args
  * @param $useDB Boolean
- * @param $transform Boolean: Whether or not to transform the message.
  * @param $forContent Mixed: Language code, or false for user lang, true for content lang.
+ * @param $transform Boolean: Whether or not to transform the message.
  * @return String: the requested message.
  */
 function wfMsgReal( $key, $args, $useDB = true, $forContent = false, $transform = true ) {
@@ -730,33 +702,22 @@ function wfMsgWeirdKey( $key ) {
  *                  behaves as a content language switch if it is a boolean.
  * @param $transform Boolean: whether to parse magic words, etc.
  * @return string
- * @private
  */
 function wfMsgGetKey( $key, $useDB, $langCode = false, $transform = true ) {
        global $wgContLang, $wgMessageCache;
 
        wfRunHooks('NormalizeMessageKey', array(&$key, &$useDB, &$langCode, &$transform));
        
-       # If $wgMessageCache isn't initialised yet, try to return something sensible.
-       if( is_object( $wgMessageCache ) ) {
-               $message = $wgMessageCache->get( $key, $useDB, $langCode );
-               if ( $transform ) {
-                       $message = $wgMessageCache->transform( $message );
-               }
-       } else {
-               $lang = wfGetLangObj( $langCode );
-
-               # MessageCache::get() does this already, Language::getMessage() doesn't
-               # ISSUE: Should we try to handle "message/lang" here too?
-               $key = str_replace( ' ' , '_' , $wgContLang->lcfirst( $key ) );
-
-               if( is_object( $lang ) ) {
-                       $message = $lang->getMessage( $key );
-               } else {
-                       $message = false;
-               }
+       if ( !is_object( $wgMessageCache ) ) {
+               throw new MWException( "Trying to get message before message cache is initialised" );
        }
 
+       $message = $wgMessageCache->get( $key, $useDB, $langCode );
+       if( $message === false ){
+               $message = '&lt;' . htmlspecialchars( $key ) . '&gt;';
+       } elseif ( $transform ) {
+               $message = $wgMessageCache->transform( $message );
+       }
        return $message;
 }
 
@@ -831,7 +792,7 @@ function wfMsgWikiHtml( $key ) {
  *   <i>parseinline</i>: parses wikitext to html and removes the surrounding
  *       p's added by parser or tidy
  *   <i>escape</i>: filters message through htmlspecialchars
- *   <i>escapenoentities</i>: same, but allows entity references like &nbsp; through
+ *   <i>escapenoentities</i>: same, but allows entity references like &#160; through
  *   <i>replaceafter</i>: parameters are substituted after parsing or escaping
  *   <i>parsemag</i>: transform the message using magic phrases
  *   <i>content</i>: fetch message for content language instead of interface
@@ -1399,16 +1360,19 @@ function wfAppendQuery( $url, $query ) {
 
 /**
  * Expand a potentially local URL to a fully-qualified URL.  Assumes $wgServer
- * is correct.  Also doesn't handle any type of relative URL except one
- * starting with a single "/": this won't work with current-path-relative URLs
- * like "subdir/foo.html", protocol-relative URLs like
- * "//en.wikipedia.org/wiki/", etc.  TODO: improve this!
+ * and $wgProto are correct.
+ *
+ * @todo this won't work with current-path-relative URLs
+ * like "subdir/foo.html", etc.
  *
  * @param $url String: either fully-qualified or a local path + query
  * @return string Fully-qualified URL
  */
 function wfExpandUrl( $url ) {
-       if( substr( $url, 0, 1 ) == '/' ) {
+       if( substr( $url, 0, 2 ) == '//' ) {
+               global $wgProto;
+               return $wgProto . ':' . $url;
+       } elseif( substr( $url, 0, 1 ) == '/' ) {
                global $wgServer;
                return $wgServer . $url;
        } else {
@@ -1460,13 +1424,17 @@ function wfEscapeShellArg( ) {
                                }
                                $delim = !$delim;
                        }
+                       
                        // Double the backslashes before the end of the string, because
                        // we will soon add a quote
                        $m = array();
                        if ( preg_match( '/^(.*?)(\\\\+)$/', $arg, $m ) ) {
                                $arg = $m[1] . str_replace( '\\', '\\\\', $m[2] );
                        }
-
+                       
+                       // The caret is also an special character
+                       $arg = str_replace( "^", "^^", $arg );
+                       
                        // Add surrounding quotes
                        $retVal .= '"' . $arg . '"';
                } else {
@@ -1529,7 +1497,7 @@ function wfMerge( $old, $mine, $yours, &$result ){
        pclose( $handle );
        unlink( $mytextName ); unlink( $oldtextName ); unlink( $yourtextName );
 
-       if ( $result === '' && $old !== '' && $conflict == false ) {
+       if ( $result === '' && $old !== '' && !$conflict ) {
                wfDebug( "Unexpected null result from diff3. Command: $cmd\n" );
                $conflict = true;
        }
@@ -1844,7 +1812,7 @@ function wfSuppressWarnings( $end = false ) {
                }
        } else {
                if ( !$suppressCount ) {
-                       $originalLevel = error_reporting( E_ALL & ~( E_WARNING | E_NOTICE ) );
+                       $originalLevel = error_reporting( E_ALL & ~( E_WARNING | E_NOTICE | E_USER_WARNING | E_USER_NOTICE ) );
                }
                ++$suppressCount;
        }
@@ -2063,7 +2031,7 @@ function wfGetCachedNotice( $name ) {
                        $notice = '';
                }
        }
-
+       $notice = '<div id="localNotice">'.$notice.'</div>';
        wfProfileOut( $fname );
        return $notice;
 }
@@ -2122,6 +2090,7 @@ function wfGetSiteNotice() {
  * @deprecated
  */
 function &wfGetMimeMagic() {
+       wfDeprecated( __FUNCTION__ );
        return MimeMagic::singleton();
 }
 
@@ -2173,7 +2142,11 @@ function wfMkdirParents( $dir, $mode = null, $caller = null ) {
        if ( is_null( $mode ) )
                $mode = $wgDirectoryMode;
 
+       // Turn off the normal warning, we're doing our own below
+       wfSuppressWarnings();
        $ok = mkdir( $dir, $mode, true );  // PHP5 <3
+       wfRestoreWarnings();
+
        if( !$ok ) {
                // PHP doesn't report the path in its warning message, so add our own to aid in diagnosis.
                trigger_error( __FUNCTION__ . ": failed to mkdir \"$dir\" mode $mode", E_USER_WARNING );
@@ -2250,12 +2223,12 @@ function wfAppendToArrayIfNotDefault( $key, $value, $default, &$changed ) {
  * looked up didn't exist but a XHTML string, this function checks for the
  * nonexistance of messages by looking at wfMsg() output
  *
- * @param $msg      String: the message key looked up
- * @param $wfMsgOut String: the output of wfMsg*()
- * @return Boolean
+ * @param $key      String: the message key looked up
+ * @return Boolean True if the message *doesn't* exist.
  */
-function wfEmptyMsg( $msg, $wfMsgOut ) {
-       return $wfMsgOut === htmlspecialchars( "<$msg>" );
+function wfEmptyMsg( $key ) {
+       global $wgMessageCache;
+       return $wgMessageCache->get( $key, /*useDB*/true, /*content*/false ) === false;
 }
 
 /**
@@ -2333,6 +2306,30 @@ function wfIniGetBool( $setting ) {
                || preg_match( "/^\s*[+-]?0*[1-9]/", $val ); // approx C atoi() function
 }
 
+/**
+ * Wrapper function for PHP's dl(). This doesn't work in most situations from
+ * PHP 5.3 onward, and is usually disabled in shared environments anyway.
+ *
+ * @param $extension String A PHP extension. The file suffix (.so or .dll)
+ *                          should be omitted
+ * @return Bool - Whether or not the extension is loaded
+ */
+function wfDl( $extension ) {
+       if( extension_loaded( $extension ) ) {
+               return true;
+       }
+
+       $canDl = ( function_exists( 'dl' ) && is_callable( 'dl' )
+               && wfIniGetBool( 'enable_dl' ) && !wfIniGetBool( 'safe_mode' ) );
+
+       if( $canDl ) {
+               wfSuppressWarnings();
+               dl( $extension . '.' . PHP_SHLIB_SUFFIX );
+               wfRestoreWarnings();
+       }
+       return extension_loaded( $extension );
+}
+
 /**
  * Execute a shell command, with time and memory limits mirrored from the PHP
  * configuration if supported.
@@ -2456,13 +2453,6 @@ function wfUseMW( $req_ver ) {
                throw new MWException( "MediaWiki $req_ver required--this is only $wgVersion" );
 }
 
-/**
- * @deprecated use StringUtils::escapeRegexReplacement
- */
-function wfRegexReplacement( $string ) {
-       return StringUtils::escapeRegexReplacement( $string );
-}
-
 /**
  * Return the final portion of a pathname.
  * Reimplemented because PHP5's basename() is buggy with multibyte text.
@@ -2566,11 +2556,13 @@ function wfArrayMerge( $array1/* ... */ ) {
  *             array( 'y' )
  *     )
  */
-function wfMergeErrorArrays(/*...*/) {
+function wfMergeErrorArrays( /*...*/ ) {
        $args = func_get_args();
        $out = array();
        foreach ( $args as $errors ) {
                foreach ( $errors as $params ) {
+                       # FIXME: sometimes get nested arrays for $params,
+                       # which leads to E_NOTICEs
                        $spec = implode( "\t", $params );
                        $out[$spec] = $params;
                }
@@ -2672,13 +2664,6 @@ function wfDoUpdates()
        $wgPostCommitUpdateList = array();
 }
 
-/**
- * @deprecated use StringUtils::explodeMarkup
- */
-function wfExplodeMarkup( $separator, $text ) {
-       return StringUtils::explodeMarkup( $separator, $text );
-}
-
 /**
  * Convert an arbitrarily-long digit string from one numeric base
  * to another, optionally zero-padding to a minimum column width.
@@ -2793,24 +2778,6 @@ function wfCreateObject( $name, $p ){
        }
 }
 
-/**
- * Alias for modularized function
- * @deprecated Use Http::get() instead
- */
-function wfGetHTTP( $url ) {
-       wfDeprecated(__FUNCTION__);
-       return Http::get( $url );
-}
-
-/**
- * Alias for modularized function
- * @deprecated Use Http::isLocalURL() instead
- */
-function wfIsLocalURL( $url ) {
-       wfDeprecated(__FUNCTION__);
-       return Http::isLocalURL( $url );
-}
-
 function wfHttpOnlySafe() {
        global $wgHttpOnlyBlacklist;
        if( !version_compare("5.2", PHP_VERSION, "<") )
@@ -2890,11 +2857,19 @@ function wfGetCaller( $level = 2 ) {
 }
 
 /**
- * Return a string consisting all callers in stack, somewhat useful sometimes
- * for profiling specific points
+ * Return a string consisting of callers in the stack. Useful sometimes
+ * for profiling specific points.
+ *
+ * @param $limit The maximum depth of the stack frame to return, or false for
+ *               the entire stack.
  */
-function wfGetAllCallers() {
-       return implode('/', array_map('wfFormatStackFrame',array_reverse(wfDebugBacktrace())));
+function wfGetAllCallers( $limit = 3 ) {
+       $trace = array_reverse( wfDebugBacktrace() );
+       if ( !$limit || $limit > count( $trace ) - 1 ) {
+               $limit = count( $trace ) - 1;
+       }
+       $trace = array_slice( $trace, -$limit - 1, $limit );
+       return implode( '/', array_map( 'wfFormatStackFrame', $trace ) );
 }
 
 /**
@@ -3362,3 +3337,14 @@ function wfBCP47( $code ) {
        $langCode = implode ( '-' , $codeBCP );
        return $langCode;
 }
+
+function wfArrayMap( $function, $input ) {
+       $ret = array_map( $function, $input );
+       foreach ( $ret as $key => $value ) {
+               $taint = istainted( $input[$key] );
+               if ( $taint ) {
+                       taint( $ret[$key], $taint );
+               }
+       }
+       return $ret;
+}