Revert previous commit, breaks cache.
[lhc/web/wiklou.git] / includes / Title.php
index 79c875c..42fc685 100644 (file)
@@ -94,15 +94,16 @@ class Title {
        }
 
        /**
-        * Create a new Title from text, such as what one would
-        * find in a link. Decodes any HTML entities in the text.
+        * Create a new Title from text, such as what one would find in a link. De-
+        * codes any HTML entities in the text.
         *
-        * @param $text \type{\string} the link text; spaces, prefixes,
-        *      and an initial ':' indicating the main namespace
-        *      are accepted
-        * @param $defaultNamespace \type{\int} the namespace to use if
-        *      none is specified by a prefix
-        * @return \type{Title} the new object, or NULL on an error
+        * @param $text             string  The link text; spaces, prefixes, and an
+        *   initial ':' indicating the main namespace are accepted.
+        * @param $defaultNamespace int     The namespace to use if none is speci-
+        *   fied by a prefix.  If you want to force a specific namespace even if
+        *   $text might begin with a namespace prefix, use makeTitle() or
+        *   makeTitleSafe().
+        * @return Title  The new object, or null on an error.
         */
        public static function newFromText( $text, $defaultNamespace = NS_MAIN ) {
                if( is_object( $text ) ) {
@@ -404,100 +405,10 @@ class Title {
         * @return \type{\string} the associated URL, containing "$1", 
         *      which should be replaced by an article title
         * @static (arguably)
+        * @deprecated See Interwiki class
         */
        public function getInterwikiLink( $key )  {
-               global $wgMemc, $wgInterwikiExpiry;
-               global $wgInterwikiCache, $wgContLang;
-               $fname = 'Title::getInterwikiLink';
-
-               if ( count( Title::$interwikiCache ) >= self::CACHE_MAX ) {
-                       // Don't use infinite memory
-                       reset( Title::$interwikiCache );
-                       unset( Title::$interwikiCache[ key( Title::$interwikiCache ) ] );
-               }
-
-               $key = $wgContLang->lc( $key );
-
-               $k = wfMemcKey( 'interwiki', $key );
-               if( array_key_exists( $k, Title::$interwikiCache ) ) {
-                       return Title::$interwikiCache[$k]->iw_url;
-               }
-
-               if ($wgInterwikiCache) {
-                       return Title::getInterwikiCached( $key );
-               }
-
-               $s = $wgMemc->get( $k );
-               # Ignore old keys with no iw_local
-               if( $s && isset( $s->iw_local ) && isset($s->iw_trans)) {
-                       Title::$interwikiCache[$k] = $s;
-                       return $s->iw_url;
-               }
-
-               $dbr = wfGetDB( DB_SLAVE );
-               $res = $dbr->select( 'interwiki',
-                       array( 'iw_url', 'iw_local', 'iw_trans' ),
-                       array( 'iw_prefix' => $key ), $fname );
-               if( !$res ) {
-                       return '';
-               }
-
-               $s = $dbr->fetchObject( $res );
-               if( !$s ) {
-                       # Cache non-existence: create a blank object and save it to memcached
-                       $s = (object)false;
-                       $s->iw_url = '';
-                       $s->iw_local = 0;
-                       $s->iw_trans = 0;
-               }
-               $wgMemc->set( $k, $s, $wgInterwikiExpiry );
-               Title::$interwikiCache[$k] = $s;
-
-               return $s->iw_url;
-       }
-
-       /**
-        * Fetch interwiki prefix data from local cache in constant database.
-        *
-        * @note More logic is explained in DefaultSettings.
-        *
-        * @param $key \type{\string} Database key
-        * @return \type{\string} URL of interwiki site
-        */
-       public static function getInterwikiCached( $key ) {
-               global $wgInterwikiCache, $wgInterwikiScopes, $wgInterwikiFallbackSite;
-               static $db, $site;
-
-               if (!$db)
-                       $db=dba_open($wgInterwikiCache,'r','cdb');
-               /* Resolve site name */
-               if ($wgInterwikiScopes>=3 and !$site) {
-                       $site = dba_fetch('__sites:' . wfWikiID(), $db);
-                       if ($site=="")
-                               $site = $wgInterwikiFallbackSite;
-               }
-               $value = dba_fetch( wfMemcKey( $key ), $db);
-               if ($value=='' and $wgInterwikiScopes>=3) {
-                       /* try site-level */
-                       $value = dba_fetch("_{$site}:{$key}", $db);
-               }
-               if ($value=='' and $wgInterwikiScopes>=2) {
-                       /* try globals */
-                       $value = dba_fetch("__global:{$key}", $db);
-               }
-               if ($value=='undef')
-                       $value='';
-               $s = (object)false;
-               $s->iw_url = '';
-               $s->iw_local = 0;
-               $s->iw_trans = 0;
-               if ($value!='') {
-                       list($local,$url)=explode(' ',$value,2);
-                       $s->iw_url=$url;
-                       $s->iw_local=(int)$local;
-               }
-               Title::$interwikiCache[wfMemcKey( 'interwiki', $key )] = $s;
-               return $s->iw_url;
+               return Interwiki::fetch( $key )->getURL( );
        }
 
        /**
@@ -509,10 +420,7 @@ class Title {
         */
        public function isLocal() {
                if ( $this->mInterwiki != '' ) {
-                       # Make sure key is loaded into cache
-                       $this->getInterwikiLink( $this->mInterwiki );
-                       $k = wfMemcKey( 'interwiki', $this->mInterwiki );
-                       return (bool)(Title::$interwikiCache[$k]->iw_local);
+                       return Interwiki::fetch( $this->mInterwiki )->isLocal();
                } else {
                        return true;
                }
@@ -527,10 +435,8 @@ class Title {
        public function isTrans() {
                if ($this->mInterwiki == '')
                        return false;
-               # Make sure key is loaded into cache
-               $this->getInterwikiLink( $this->mInterwiki );
-               $k = wfMemcKey( 'interwiki', $this->mInterwiki );
-               return (bool)(Title::$interwikiCache[$k]->iw_trans);
+               
+               return Interwiki::fetch( $this->mInterwiki )->isTranscludable();
        }
 
        /**
@@ -769,7 +675,8 @@ class Title {
                        $query = wfArrayToCGI( $query );
                }
 
-               if ( '' == $this->mInterwiki ) {
+               $interwiki = Interwiki::fetch( $this->mInterwiki );
+               if ( !$interwiki ) {
                        $url = $this->getLocalUrl( $query, $variant );
 
                        // Ugly quick hack to avoid duplicate prefixes (bug 4571 etc)
@@ -778,7 +685,7 @@ class Title {
                                $url = $wgServer . $url;
                        }
                } else {
-                       $baseUrl = $this->getInterwikiLink( $this->mInterwiki );
+                       $baseUrl = $interwiki->getURL( );
 
                        $namespace = wfUrlencode( $this->getNsText() );
                        if ( '' != $namespace ) {
@@ -1114,7 +1021,8 @@ class Title {
                        $errors[] = array( 'confirmedittext' );
                }
 
-               if ( $user->isBlockedFrom( $this ) && $action != 'createaccount' ) {
+               // Edit blocks should not affect reading. Account creation blocks handled at userlogin.
+               if ( $user->isBlockedFrom( $this ) && $action != 'read' && $action != 'createaccount' ) {
                        $block = $user->mBlock;
 
                        // This is from OutputPage::blockedPage
@@ -1314,8 +1222,43 @@ class Title {
                                ( !$this->isTalkPage() && !$user->isAllowed( 'createpage' ) ) ) {
                                $errors[] = $user->isAnon() ? array ('nocreatetext') : array ('nocreate-loggedin');
                        }
-               } elseif( $action == 'move' && !( $this->isMovable() && $user->isAllowed( 'move' ) ) ) {
-                       $errors[] = $user->isAnon() ? array ( 'movenologintext' ) : array ('movenotallowed');
+
+               } elseif ( $action == 'move' ) {
+                       if ( !$user->isAllowed( 'move' ) ) {
+                               // User can't move anything
+                               $errors[] = $user->isAnon() ? array ( 'movenologintext' ) : array ('movenotallowed');
+                       } elseif ( !$user->isAllowed( 'move-rootuserpages' ) 
+                                       && $this->getNamespace() == NS_USER && !$this->isSubpage() ) 
+                       {
+                               // Show user page-specific message only if the user can move other pages
+                               $errors[] = array( 'cant-move-user-page' );
+                       }
+
+                       // Check for immobile pages
+                       if ( !MWNamespace::isMovable( $this->getNamespace() ) ) {
+                               // Specific message for this case
+                               $errors[] = array( 'immobile-source-namespace', $this->getNsText() );
+                       } elseif ( !$this->isMovable() ) {
+                               // Less specific message for rarer cases
+                               $errors[] = array( 'immobile-page' );
+                       }
+
+               } elseif ( $action == 'move-target' ) {
+                       if ( !$user->isAllowed( 'move' ) ) {
+                               // User can't move anything
+                               $errors[] = $user->isAnon() ? array ( 'movenologintext' ) : array ('movenotallowed');
+                       } elseif ( !$user->isAllowed( 'move-rootuserpages' ) 
+                                       && $this->getNamespace() == NS_USER && !$this->isSubpage() ) 
+                       {
+                               // Show user page-specific message only if the user can move other pages
+                               $errors[] = array( 'cant-move-to-user-page' );
+                       }
+                       if ( !MWNamespace::isMovable( $this->getNamespace() ) ) {
+                               $errors[] = array( 'immobile-target-namespace', $this->getNsText() );
+                       } elseif ( !$this->isMovable() ) {
+                               $errors[] = array( 'immobile-target-page' );
+                       }
+
                } elseif ( !$user->isAllowed( $action ) ) {
                        $return = null;
                        $groups = array_map( array( 'User', 'makeGroupLinkWiki' ),
@@ -1403,7 +1346,8 @@ class Title {
                $log = new LogPage( 'protect' );
 
                if( $create_perm ) {
-                       $log->addEntry( $this->mRestrictions['create'] ? 'modify' : 'protect', $this, trim( $reason . " [create=$create_perm] $expiry_description" ) );
+                       $params = array("[create=$create_perm] $expiry_description",'');
+                       $log->addEntry( $this->mRestrictions['create'] ? 'modify' : 'protect', $this, trim( $reason ), $params );
                } else {
                        $log->addEntry( 'unprotect', $this, $reason );
                }
@@ -1894,6 +1838,18 @@ class Title {
                                : array();
        }
 
+       /**
+        * Get the expiry time for the restriction against a given action
+        * @return 14-char timestamp, or 'infinity' if the page is protected forever 
+        * or not protected at all, or false if the action is not recognised.
+        */
+       public function getRestrictionExpiry( $action ) {
+               if( !$this->mRestrictionsLoaded ) {
+                       $this->loadRestrictions();
+               }
+               return isset( $this->mRestrictionsExpiry[$action] ) ? $this->mRestrictionsExpiry[$action] : false;
+       }
+
        /**
         * Is there a version of this page in the deletion archive?
         * @return \type{\int} the number of archived revisions
@@ -2135,7 +2091,7 @@ class Title {
                                        # Ordinary namespace
                                        $dbkey = $m[2];
                                        $this->mNamespace = $ns;
-                               } elseif( $this->getInterwikiLink( $p ) ) {
+                               } elseif( Interwiki::isValidInterwiki( $p ) ) {
                                        if( !$firstPass ) {
                                                # Can't make a local interwiki link to an interwiki link.
                                                # That's just crazy!
@@ -2460,6 +2416,8 @@ class Title {
         * @return \type{\mixed} True on success, getUserPermissionsErrors()-like array on failure
         */
        public function isValidMoveOperation( &$nt, $auth = true, $reason = '' ) {
+               global $wgUser;
+
                $errors = array();      
                if( !$nt ) {
                        // Normally we'd add this to $errors, but we'll get
@@ -2469,8 +2427,11 @@ class Title {
                if( $this->equals( $nt ) ) {
                        $errors[] = array('selfmove');
                }
-               if( !$this->isMovable() || !$nt->isMovable() ) {
-                       $errors[] = array('immobile_namespace');
+               if( !$this->isMovable() ) {
+                       $errors[] = array( 'immobile-source-namespace', $this->getNsText() );
+               }
+               if ( !$nt->isMovable() ) {
+                       $errors[] = array('immobile-target-namespace', $nt->getNsText() );
                }
 
                $oldid = $this->getArticleID();
@@ -2502,11 +2463,10 @@ class Title {
                }
 
                if ( $auth ) {
-                       global $wgUser;
                        $errors = wfArrayMerge($errors, 
                                        $this->getUserPermissionsErrors('move', $wgUser),
                                        $this->getUserPermissionsErrors('edit', $wgUser),
-                                       $nt->getUserPermissionsErrors('move', $wgUser),
+                                       $nt->getUserPermissionsErrors('move-target', $wgUser),
                                        $nt->getUserPermissionsErrors('edit', $wgUser));
                }
 
@@ -2516,7 +2476,6 @@ class Title {
                        $errors[] = array('spamprotectiontext');
                }
                
-               global $wgUser;
                $err = null;
                if( !wfRunHooks( 'AbortMove', array( $this, $nt, $wgUser, &$err, $reason ) ) ) {
                        $errors[] = array('hookaborted', $err);
@@ -2559,6 +2518,7 @@ class Title {
                }
 
                $pageid = $this->getArticleID();
+               $protected = $this->isProtected();
                if( $nt->exists() ) {
                        $err = $this->moveOverExistingRedirect( $nt, $reason, $createRedirect );
                        $pageCountChange = ($createRedirect ? 0 : -1);
@@ -2593,8 +2553,29 @@ class Title {
                                'cl_sortkey' => $this->getPrefixedText() ),
                        __METHOD__ );
 
-               # Update watchlists
+               if( $protected ) {
+                       # Protect the redirect title as the title used to be...
+                       $dbw->insertSelect( 'page_restrictions', 'page_restrictions',
+                               array( 
+                                       'pr_page'    => $redirid,
+                                       'pr_type'    => 'pr_type',
+                                       'pr_level'   => 'pr_level',
+                                       'pr_cascade' => 'pr_cascade',
+                                       'pr_user'    => 'pr_user',
+                                       'pr_expiry'  => 'pr_expiry'
+                               ),
+                               array( 'pr_page' => $pageid ),
+                               __METHOD__,
+                               array( 'IGNORE' )
+                       );
+                       # Update the protection log
+                       $log = new LogPage( 'protect' );
+                       $comment = wfMsgForContent('prot_1movedto2',$this->getPrefixedText(), $nt->getPrefixedText() );
+                       if( $reason ) $comment .= ': ' . $reason;
+                       $log->addEntry( 'move_prot', $nt, $comment, array($this->getPrefixedText()) ); // FIXME: $params?
+               }
 
+               # Update watchlists
                $oldnamespace = $this->getNamespace() & ~1;
                $newnamespace = $nt->getNamespace() & ~1;
                $oldtitle = $this->getDBkey();
@@ -2667,7 +2648,6 @@ class Title {
                $latest = $this->getLatestRevID();
 
                $dbw = wfGetDB( DB_MASTER );
-               $dbw->begin();
 
                # Delete the old redirect. We don't save it to history since
                # by definition if we've got here it's rather uninteresting.
@@ -2747,7 +2727,6 @@ class Title {
                                }
                        }
                }
-               $dbw->commit();
 
                # Log the move
                $log = new LogPage( 'move' );
@@ -2784,7 +2763,6 @@ class Title {
                $latest = $this->getLatestRevId();
                
                $dbw = wfGetDB( DB_MASTER );
-               $dbw->begin();
                $now = $dbw->timestamp();
 
                # Save a null revision in the page's history notifying of the move
@@ -2844,7 +2822,6 @@ class Title {
                                }
                        }
                }
-               $dbw->commit();
 
                # Log the move
                $log = new LogPage( 'move' );