Merge "Revert "Revert "Add new recentchanges field rc_source to replace rc_type"""
[lhc/web/wiklou.git] / includes / Title.php
index 13350cf..21872e4 100644 (file)
@@ -491,6 +491,108 @@ class Title {
                return $rxTc;
        }
 
+       /**
+        * Utility method for converting a character sequence from bytes to Unicode.
+        *
+        * Primary usecase being converting $wgLegalTitleChars to a sequence usable in
+        * javascript, as PHP uses UTF-8 bytes where javascript uses Unicode code units.
+        *
+        * @param string $byteClass
+        * @return string
+        */
+       public static function convertByteClassToUnicodeClass( $byteClass ) {
+               $length = strlen( $byteClass );
+               // Input token queue
+               $x0 = $x1 = $x2 = '';
+               // Decoded queue
+               $d0 = $d1 = $d2 = '';
+               // Decoded integer codepoints
+               $ord0 = $ord1 = $ord2 = 0;
+               // Re-encoded queue
+               $r0 = $r1 = $r2 = '';
+               // Output
+               $out = '';
+               // Flags
+               $allowUnicode = false;
+               for ( $pos = 0; $pos < $length; $pos++ ) {
+                       // Shift the queues down
+                       $x2 = $x1;
+                       $x1 = $x0;
+                       $d2 = $d1;
+                       $d1 = $d0;
+                       $ord2 = $ord1;
+                       $ord1 = $ord0;
+                       $r2 = $r1;
+                       $r1 = $r0;
+                       // Load the current input token and decoded values
+                       $inChar = $byteClass[$pos];
+                       if ( $inChar == '\\' ) {
+                               if ( preg_match( '/x([0-9a-fA-F]{2})/A', $byteClass, $m, 0, $pos + 1 ) ) {
+                                       $x0 = $inChar . $m[0];
+                                       $d0 = chr( hexdec( $m[1] ) );
+                                       $pos += strlen( $m[0] );
+                               } elseif ( preg_match( '/[0-7]{3}/A', $byteClass, $m, 0, $pos + 1 ) ) {
+                                       $x0 = $inChar . $m[0];
+                                       $d0 = chr( octdec( $m[0] ) );
+                                       $pos += strlen( $m[0] );
+                               } elseif ( $pos + 1 >= $length ) {
+                                       $x0 = $d0 = '\\';
+                               } else {
+                                       $d0 = $byteClass[$pos + 1];
+                                       $x0 = $inChar . $d0;
+                                       $pos += 1;
+                               }
+                       } else {
+                               $x0 = $d0 = $inChar;
+                       }
+                       $ord0 = ord( $d0 );
+                       // Load the current re-encoded value
+                       if ( $ord0 < 32 || $ord0 == 0x7f ) {
+                               $r0 = sprintf( '\x%02x', $ord0 );
+                       } elseif ( $ord0 >= 0x80 ) {
+                               // Allow unicode if a single high-bit character appears
+                               $r0 = sprintf( '\x%02x', $ord0 );
+                               $allowUnicode = true;
+                       } elseif ( strpos( '-\\[]^', $d0 ) !== false ) {
+                               $r0 = '\\' . $d0;
+                       } else {
+                               $r0 = $d0;
+                       }
+                       // Do the output
+                       if ( $x0 !== '' && $x1 === '-' && $x2 !== '' ) {
+                               // Range
+                               if ( $ord2 > $ord0 ) {
+                                       // Empty range
+                               } elseif ( $ord0 >= 0x80 ) {
+                                       // Unicode range
+                                       $allowUnicode = true;
+                                       if ( $ord2 < 0x80 ) {
+                                               // Keep the non-unicode section of the range
+                                               $out .= "$r2-\\x7F";
+                                       }
+                               } else {
+                                       // Normal range
+                                       $out .= "$r2-$r0";
+                               }
+                               // Reset state to the initial value
+                               $x0 = $x1 = $d0 = $d1 = $r0 = $r1 = '';
+                       } elseif ( $ord2 < 0x80 ) {
+                               // ASCII character
+                               $out .= $r2;
+                       }
+               }
+               if ( $ord1 < 0x80 ) {
+                       $out .= $r1;
+               }
+               if ( $ord0 < 0x80 ) {
+                       $out .= $r0;
+               }
+               if ( $allowUnicode ) {
+                       $out .= '\u0080-\uFFFF';
+               }
+               return $out;
+       }
+
        /**
         * Get a string representation of a title suitable for
         * including in a search index
@@ -2356,31 +2458,31 @@ class Title {
        }
 
        /**
-        * Is this page "semi-protected" - the *only* protection is autoconfirm?
+        * Is this page "semi-protected" - the *only* protection levels are listed
+        * in $wgSemiprotectedRestrictionLevels?
         *
         * @param string $action Action to check (default: edit)
         * @return Bool
         */
        public function isSemiProtected( $action = 'edit' ) {
-               if ( $this->exists() ) {
-                       $restrictions = $this->getRestrictions( $action );
-                       if ( count( $restrictions ) > 0 ) {
-                               foreach ( $restrictions as $restriction ) {
-                                       if ( strtolower( $restriction ) != 'editsemiprotected' &&
-                                               strtolower( $restriction ) != 'autoconfirmed' // BC
-                                       ) {
-                                               return false;
-                                       }
-                               }
-                       } else {
-                               # Not protected
-                               return false;
-                       }
-                       return true;
-               } else {
-                       # If it doesn't exist, it can't be protected
+               global $wgSemiprotectedRestrictionLevels;
+
+               $restrictions = $this->getRestrictions( $action );
+               $semi = $wgSemiprotectedRestrictionLevels;
+               if ( !$restrictions || !$semi ) {
+                       // Not protected, or all protection is full protection
                        return false;
                }
+
+               // Remap autoconfirmed to editsemiprotected for BC
+               foreach ( array_keys( $semi, 'autoconfirmed' ) as $key ) {
+                       $semi[$key] = 'editsemiprotected';
+               }
+               foreach ( array_keys( $restrictions, 'autoconfirmed' ) as $key ) {
+                       $restrictions[$key] = 'editsemiprotected';
+               }
+
+               return !array_diff( $restrictions, $semi );
        }
 
        /**
@@ -3063,7 +3165,7 @@ class Title {
                        return false;
                }
 
-               if ( false !== strpos( $dbkey, UTF8_REPLACEMENT ) ) {
+               if ( strpos( $dbkey, UTF8_REPLACEMENT ) !== false ) {
                        # Contained illegal UTF-8 sequences or forbidden Unicode chars.
                        return false;
                }
@@ -3200,6 +3302,7 @@ class Title {
 
                # Can't make a link to a namespace alone... "empty" local links can only be
                # self-links with a fragment identifier.
+               # TODO: Why do we exclude NS_MAIN (bug 54044)
                if ( $dbkey == '' && $this->mInterwiki == '' && $this->mNamespace != NS_MAIN ) {
                        return false;
                }
@@ -3738,7 +3841,8 @@ class Title {
 
                if ( $createRedirect ) {
                        $contentHandler = ContentHandler::getForTitle( $this );
-                       $redirectContent = $contentHandler->makeRedirectContent( $nt );
+                       $redirectContent = $contentHandler->makeRedirectContent( $nt,
+                               wfMessage( 'move-redirect-text' )->inContentLanguage()->plain() );
 
                        // NOTE: If this page's content model does not support redirects, $redirectContent will be null.
                } else {
@@ -3799,7 +3903,12 @@ class Title {
                        __METHOD__
                );
 
-               $this->resetArticleID( 0 );
+               // clean up the old title before reset article id - bug 45348
+               if ( !$redirectContent ) {
+                       WikiPage::onArticleDelete( $this );
+               }
+
+               $this->resetArticleID( 0 ); // 0 == non existing
                $nt->resetArticleID( $oldid );
                $newpage->loadPageData( WikiPage::READ_LOCKING ); // bug 46397
 
@@ -3815,13 +3924,12 @@ class Title {
                }
 
                # Recreate the redirect, this time in the other direction.
-               if ( !$redirectContent ) {
-                       WikiPage::onArticleDelete( $this );
-               } else {
+               if ( $redirectContent ) {
                        $redirectArticle = WikiPage::factory( $this );
                        $redirectArticle->loadFromRow( false, WikiPage::READ_LOCKING ); // bug 46397
                        $newid = $redirectArticle->insertOn( $dbw );
                        if ( $newid ) { // sanity
+                               $this->resetArticleID( $newid );
                                $redirectRevision = new Revision( array(
                                        'title' => $this, // for determining the default content model
                                        'page' => $newid,