Revert r91561
[lhc/web/wiklou.git] / includes / Title.php
index ed6e735..418740d 100644 (file)
@@ -757,6 +757,20 @@ class Title {
                return $this->mPrefixedText;
        }
 
+       /**
+        * Return the prefixed title with spaces _without_ the interwiki prefix
+        * 
+        * @return \type{\string} the title, prefixed by the namespace but not by the interwiki prefix, with spaces
+        */
+       public function getSemiPrefixedText() {
+               if ( !isset( $this->mSemiPrefixedText ) ){
+                       $s = ( $this->mNamespace === NS_MAIN ? '' : $this->getNsText() . ':' ) . $this->mTextform;
+                       $s = str_replace( '_', ' ', $s );
+                       $this->mSemiPrefixedText = $s;
+               }
+               return $this->mSemiPrefixedText; 
+       }
+
        /**
         * Get the prefixed title with spaces, plus any fragment
         * (part beginning with '#')
@@ -835,38 +849,18 @@ class Title {
         * @return String the URL
         */
        public function getFullURL( $query = '', $variant = false ) {
-               global $wgServer, $wgRequest;
-
-               if ( is_array( $query ) ) {
-                       $query = wfArrayToCGI( $query );
-               }
+               # Hand off all the decisions on urls to getLocalURL
+               $url = $this->getLocalURL( $query, $variant );
 
-               $interwiki = Interwiki::fetch( $this->mInterwiki );
-               if ( !$interwiki ) {
-                       $url = $this->getLocalURL( $query, $variant );
-
-                       // Ugly quick hack to avoid duplicate prefixes (bug 4571 etc)
-                       // Correct fix would be to move the prepending elsewhere.
-                       if ( $wgRequest->getVal( 'action' ) != 'render' ) {
-                               $url = $wgServer . $url;
-                       }
-               } else {
-                       $baseUrl = $interwiki->getURL();
-
-                       $namespace = wfUrlencode( $this->getNsText() );
-                       if ( $namespace != '' ) {
-                               # Can this actually happen? Interwikis shouldn't be parsed.
-                               # Yes! It can in interwiki transclusion. But... it probably shouldn't.
-                               $namespace .= ':';
-                       }
-                       $url = str_replace( '$1', $namespace . $this->mUrlform, $baseUrl );
-                       $url = wfAppendQuery( $url, $query );
-               }
+               # Expand the url to make it a full url. Note that getLocalURL has the
+               # potential to output full urls for a variety of reasons, so we use
+               # wfExpandUrl instead of simply prepending $wgServer
+               $url = wfExpandUrl( $url, PROTO_RELATIVE );
 
                # Finally, add the fragment.
                $url .= $this->getFragmentForURL();
 
-               wfRunHooks( 'GetFullURL', array( &$this, &$url, $query ) );
+               wfRunHooks( 'GetFullURL', array( &$this, &$url, $query, $variant ) );
                return $url;
        }
 
@@ -889,15 +883,16 @@ class Title {
                        $query = wfArrayToCGI( $query );
                }
 
-               if ( $this->isExternal() ) {
-                       $url = $this->getFullURL();
-                       if ( $query ) {
-                               // This is currently only used for edit section links in the
-                               // context of interwiki transclusion. In theory we should
-                               // append the query to the end of any existing query string,
-                               // but interwiki transclusion is already broken in that case.
-                               $url .= "?$query";
+               $interwiki = Interwiki::fetch( $this->mInterwiki );
+               if ( $interwiki ) {
+                       $namespace = $this->getNsText();
+                       if ( $namespace != '' ) {
+                               # Can this actually happen? Interwikis shouldn't be parsed.
+                               # Yes! It can in interwiki transclusion. But... it probably shouldn't.
+                               $namespace .= ':';
                        }
+                       $url = $interwiki->getURL( $namespace . $this->getDBkey() );
+                       $url = wfAppendQuery( $url, $query );
                } else {
                        $dbkey = wfUrlencode( $this->getPrefixedDBkey() );
                        if ( $query == '' ) {
@@ -911,6 +906,7 @@ class Title {
                                        $url = str_replace( '$1', $dbkey, $url  );
                                } else {
                                        $url = str_replace( '$1', $dbkey, $wgArticlePath );
+                                       wfRunHooks( 'GetLocalURL::Article', array( &$this, &$url ) );
                                }
                        } else {
                                global $wgActionPaths;
@@ -940,13 +936,15 @@ class Title {
                                }
                        }
 
+                       wfRunHooks( 'GetLocalURL::Internal', array( &$this, &$url, $query, $variant ) );
+
                        // @todo FIXME: This causes breakage in various places when we
                        // actually expected a local URL and end up with dupe prefixes.
                        if ( $wgRequest->getVal( 'action' ) == 'render' ) {
                                $url = $wgServer . $url;
                        }
                }
-               wfRunHooks( 'GetLocalURL', array( &$this, &$url, $query ) );
+               wfRunHooks( 'GetLocalURL', array( &$this, &$url, $query, $variant ) );
                return $url;
        }
 
@@ -1001,21 +999,53 @@ class Title {
        public function escapeFullURL( $query = '' ) {
                return htmlspecialchars( $this->getFullURL( $query ) );
        }
+       
+       /**
+        * HTML-escaped version of getCanonicalURL()
+        */
+       public function escapeCanonicalURL( $query = '', $variant = false ) {
+               return htmlspecialchars( $this->getCanonicalURL( $query, $variant ) );
+       }
 
        /**
         * Get the URL form for an internal link.
         * - Used in various Squid-related code, in case we have a different
         * internal hostname for the server from the exposed one.
+        * 
+        * This uses $wgInternalServer to qualify the path, or $wgServer
+        * if $wgInternalServer is not set. If the server variable used is
+        * protocol-relative, the URL will be expanded to http://
         *
         * @param $query String an optional query string
         * @param $variant String language variant of url (for sr, zh..)
         * @return String the URL
         */
        public function getInternalURL( $query = '', $variant = false ) {
-               global $wgInternalServer, $wgServer;
-               $server = $wgInternalServer !== false ? $wgInternalServer : $wgServer;
-               $url = $server . $this->getLocalURL( $query, $variant );
-               wfRunHooks( 'GetInternalURL', array( &$this, &$url, $query ) );
+               if ( $this->isExternal( ) ) {
+                       $server = '';
+               } else {
+                       global $wgInternalServer, $wgServer;
+                       $server = $wgInternalServer !== false ? $wgInternalServer : $wgServer;
+               }
+               $url = wfExpandUrl( $server . $this->getLocalURL( $query, $variant ), PROTO_HTTP );
+               wfRunHooks( 'GetInternalURL', array( &$this, &$url, $query, $variant ) );
+               return $url;
+       }
+
+       /**
+        * Get the URL for a canonical link, for use in things like IRC and
+        * e-mail notifications. Uses $wgCanonicalServer and the
+        * GetCanonicalURL hook.
+        * 
+        * NOTE: Unlike getInternalURL(), the canonical URL includes the fragment
+        * 
+        * @param $query string An optional query string
+        * @param $variant string Language variant of URL (for sr, zh, ...)
+        * @return string The URL
+        */
+       public function getCanonicalURL( $query = '', $variant = false ) {
+               $url = wfExpandUrl( $this->getLocalURL( $query, $variant ) . $this->getFragmentForURL(), PROTO_CANONICAL );
+               wfRunHooks( 'GetCanonicalURL', array( &$this, &$url, $query, $variant ) );
                return $url;
        }
 
@@ -1524,7 +1554,7 @@ class Title {
                                $errors[] = array( 'immobile-source-namespace', $this->getNsText() );
                        } elseif ( !$this->isMovable() ) {
                                // Less specific message for rarer cases
-                               $errors[] = array( 'immobile-page' );
+                               $errors[] = array( 'immobile-source-page' );
                        }
                } elseif ( $action == 'move-target' ) {
                        if ( !MWNamespace::isMovable( $this->mNamespace ) ) {
@@ -1576,7 +1606,7 @@ class Title {
                        if ( $reason == '' ) {
                                $reason = wfMsg( 'blockednoreason' );
                        }
-                       $ip = wfGetIP();
+                       $ip = $user->getRequest()->getIP();
 
                        if ( is_numeric( $id ) ) {
                                $name = User::whoIs( $id );
@@ -1755,7 +1785,14 @@ class Title {
         * @return Bool TRUE or FALSE
         */
        public function isMovable() {
-               return MWNamespace::isMovable( $this->getNamespace() ) && $this->getInterwiki() == '';
+               if ( !MWNamespace::isMovable( $this->getNamespace() ) || $this->getInterwiki() != '' ) {
+                       // Interwiki title or immovable namespace. Hooks don't get to override here
+                       return false;
+               }
+               
+               $result = true;
+               wfRunHooks( 'TitleIsMovable', array( $this, &$result ) );
+               return $result;
        }
 
        /**
@@ -1861,6 +1898,7 @@ class Title {
         * acidentally creating new bugs where $title->equals( Title::newFromText() )
         * ends up reporting something differently than $title->isMainPage();
         *
+        * @since 1.18
         * @return Bool
         */
        public function isMainPage() {
@@ -1944,7 +1982,7 @@ class Title {
 
        /**
         * Does that page contain wikitext, or it is JS, CSS or whatever?
-        * 
+        *
         * @return Bool
         */
        public function isWikitextPage() {
@@ -2353,37 +2391,21 @@ class Title {
        /**
         * Is there a version of this page in the deletion archive?
         *
-        * @param $includeSuppressed Boolean Include suppressed revisions?
         * @return Int the number of archived revisions
         */
-       public function isDeleted( $includeSuppressed = false ) {
+       public function isDeleted() {
                if ( $this->getNamespace() < 0 ) {
                        $n = 0;
                } else {
                        $dbr = wfGetDB( DB_SLAVE );
-                       $conditions = array( 'ar_namespace' => $this->getNamespace(), 'ar_title' => $this->getDBkey() );
-
-                       if( !$includeSuppressed ) {
-                               $suppressedTextBits = Revision::DELETED_TEXT | Revision::DELETED_RESTRICTED;
-                               $conditions[] = $dbr->bitAnd('ar_deleted', $suppressedTextBits ) .
-                               ' != ' . $suppressedTextBits;
-                       }
 
                        $n = $dbr->selectField( 'archive', 'COUNT(*)',
-                               $conditions,
+                               array( 'ar_namespace' => $this->getNamespace(), 'ar_title' => $this->getDBkey() ),
                                __METHOD__
                        );
                        if ( $this->getNamespace() == NS_FILE ) {
-                               $fconditions = array( 'fa_name' => $this->getDBkey() );
-                               if( !$includeSuppressed ) {
-                                       $suppressedTextBits = File::DELETED_FILE | File::DELETED_RESTRICTED;
-                                       $fconditions[] = $dbr->bitAnd('fa_deleted', $suppressedTextBits ) .
-                                       ' != ' . $suppressedTextBits;
-                               }
-
-                               $n += $dbr->selectField( 'filearchive',
-                                       'COUNT(*)',
-                                       $fconditions,
+                               $n += $dbr->selectField( 'filearchive', 'COUNT(*)',
+                                       array( 'fa_name' => $this->getDBkey() ),
                                        __METHOD__
                                );
                        }
@@ -2514,7 +2536,7 @@ class Title {
         */
        public function resetArticleID( $newid ) {
                $linkCache = LinkCache::singleton();
-               $linkCache->clearBadLink( $this->getPrefixedDBkey() );
+               $linkCache->clearLink( $this );
 
                if ( $newid === false ) {
                        $this->mArticleID = -1;
@@ -2823,6 +2845,10 @@ class Title {
                $this->mFragment = str_replace( '_', ' ', substr( $fragment, 1 ) );
        }
 
+       public function setInterwiki( $interwiki ) {
+               $this->mInterwiki = $interwiki;
+       }
+
        /**
         * Get a Title object associated with the talk page of this article
         *
@@ -3127,6 +3153,8 @@ class Title {
         * @return Mixed true on success, getUserPermissionsErrors()-like array on failure
         */
        public function moveTo( &$nt, $auth = true, $reason = '', $createRedirect = true ) {
+               global $wgEnableInterwikiTemplatesTracking, $wgGlobalDatabase;
+
                $err = $this->isValidMoveOperation( $nt, $auth, $reason );
                if ( is_array( $err ) ) {
                        return $err;
@@ -3151,7 +3179,7 @@ class Title {
                $pageCountChange = ( $createRedirect ? 1 : 0 ) - ( $nt->exists() ? 1 : 0 );
 
                // Do the actual move
-               $err = $this->moveToInternal( $nt, $reason, $createRedirect );
+               $err = $this->moveOverExistingRedirect( $nt, $reason, $createRedirect );
                if ( is_array( $err ) ) {
                        # @todo FIXME: What about the File we have already moved?
                        $dbw->rollback();
@@ -3183,6 +3211,15 @@ class Title {
                        );
                }
 
+               if ( $wgEnableInterwikiTemplatesTracking && $wgGlobalDatabase ) {
+                       $dbw2 = wfGetDB( DB_MASTER, array(), $wgGlobalDatabase );
+                       $dbw2->update( 'globaltemplatelinks',
+                                               array(  'gtl_from_namespace' => $nt->getNamespace(),
+                                                               'gtl_from_title' => $nt->getText() ),
+                                               array ( 'gtl_from_page' => $pageid ),
+                                               __METHOD__ );
+               }
+
                if ( $protected ) {
                        # Protect the redirect title as the title used to be...
                        $dbw->insertSelect( 'page_restrictions', 'page_restrictions',
@@ -3276,14 +3313,31 @@ class Title {
         * @param $createRedirect Bool Whether to leave a redirect at the old title.  Ignored
         *   if the user doesn't have the suppressredirect right
         */
-       private function moveToInternal( &$nt, $reason = '', $createRedirect = true ) {
-               global $wgUser, $wgContLang;
+       private function moveOverExistingRedirect( &$nt, $reason = '', $createRedirect = true ) {
+               global $wgUser, $wgContLang, $wgEnableInterwikiTemplatesTracking, $wgGlobalDatabase;
 
-               $moveOverRedirect = $nt->exists();
+               if ( $nt->exists() ) {
+                       $moveOverRedirect = true;
+                       $logType = 'move_redir';
+               } else {
+                       $moveOverRedirect = false;
+                       $logType = 'move';
+               }
 
-               $commentMsg = ( $moveOverRedirect ? '1movedto2_redir' : '1movedto2' );
-               $comment = wfMsgForContent( $commentMsg, $this->getPrefixedText(), $nt->getPrefixedText() );
+               $redirectSuppressed = !$createRedirect && $wgUser->isAllowed( 'suppressredirect' );
 
+               $logEntry = new ManualLogEntry( 'move', $logType );
+               $logEntry->setPerformer( $wgUser );
+               $logEntry->setTarget( $this );
+               $logEntry->setComment( $reason );
+               $logEntry->setParameters( array(
+                       '4::target' => $nt->getPrefixedText(),
+                       '5::noredir' => $redirectSuppressed ? '1': '0',
+               ) );
+
+               $formatter = LogFormatter::newFromEntry( $logEntry );
+               $formatter->setContext( RequestContext::newExtraneousContext( $this ) );
+               $comment = $formatter->getPlainActionText();
                if ( $reason ) {
                        $comment .= wfMsgForContent( 'colon-separator' ) . $reason;
                }
@@ -3320,12 +3374,21 @@ class Title {
                                $dbw->delete( 'externallinks', array( 'el_from' => $newid ), __METHOD__ );
                                $dbw->delete( 'langlinks', array( 'll_from' => $newid ), __METHOD__ );
                                $dbw->delete( 'redirect', array( 'rd_from' => $newid ), __METHOD__ );
+                               $dbw->delete( 'page_props', array( 'pp_page' => $newid ), __METHOD__ );
                        }
                        // If the target page was recently created, it may have an entry in recentchanges still
                        $dbw->delete( 'recentchanges',
                                array( 'rc_timestamp' => $rcts, 'rc_namespace' => $newns, 'rc_title' => $newdbk, 'rc_new' => 1 ),
                                __METHOD__
                        );
+                       
+                        if ( $wgEnableInterwikiTemplatesTracking && $wgGlobalDatabase ) {
+                               $dbw2 = wfGetDB( DB_MASTER, array(), $wgGlobalDatabase );
+                               $dbw2->delete( 'globaltemplatelinks',
+                                                       array(  'gtl_from_wiki' => wfGetID(),
+                                                                       'gtl_from_page' => $newid ),
+                                                       __METHOD__ );
+                       }
                }
 
                # Save a null revision in the page's history notifying of the move
@@ -3381,16 +3444,13 @@ class Title {
                                                'pl_title'     => $nt->getDBkey() ),
                                        __METHOD__ );
                        }
-                       $redirectSuppressed = false;
                } else {
                        $this->resetArticleID( 0 );
-                       $redirectSuppressed = true;
                }
 
                # Log the move
-               $log = new LogPage( 'move' );
-               $logType = ( $moveOverRedirect ? 'move_redir' : 'move' );
-               $log->addEntry( $logType, $this, $reason, array( 1 => $nt->getPrefixedText(), 2 => $redirectSuppressed ) );
+               $logid = $logEntry->insert();
+               $logEntry->publish( $logid );
 
                # Purge caches for old and new titles
                if ( $moveOverRedirect ) {
@@ -3798,6 +3858,18 @@ class Title {
                        && $this->getDBkey() === $title->getDBkey();
        }
 
+       /**
+        * Check if this title is a subpage of another title
+        *
+        * @param $title Title
+        * @return Bool
+        */
+       public function isSubpageOf( Title $title ) {
+               return $this->getInterwiki() === $title->getInterwiki()
+                       && $this->getNamespace() == $title->getNamespace()
+                       && strpos( $this->getDBkey(), $title->getDBkey() . '/' ) === 0;
+       }
+
        /**
         * Callback for usort() to do title sorts by (namespace, title)
         *
@@ -4315,37 +4387,3 @@ class Title {
                return wfGetLangObj( $pageLang );
        }
 }
-
-/**
- * A BadTitle is generated in MediaWiki::parseTitle() if the title is invalid; the
- * software uses this to display an error page.  Internally it's basically a Title
- * for an empty special page
- */
-class BadTitle extends Title {
-       public function __construct(){
-               $this->mTextform = '';
-               $this->mUrlform = '';
-               $this->mDbkeyform = '';
-               $this->mNamespace = NS_SPECIAL; // Stops talk page link, etc, being shown
-       }
-
-       public function exists(){
-               return false;
-       }
-
-       public function getPrefixedText(){
-               return '';
-       }
-
-       public function getText(){
-               return '';
-       }
-
-       public function getPrefixedURL(){
-               return '';
-       }
-
-       public function getPrefixedDBKey(){
-               return '';
-       }
-}