MediaWiki  REL1_24
namespaceDupes.php
Go to the documentation of this file.
00001 <?php
00027 require_once __DIR__ . '/Maintenance.php';
00028 
00035 class NamespaceConflictChecker extends Maintenance {
00036 
00040     protected $db;
00041 
00042     public function __construct() {
00043         parent::__construct();
00044         $this->mDescription = "";
00045         $this->addOption( 'fix', 'Attempt to automatically fix errors' );
00046         $this->addOption( 'suffix', "Dupes will be renamed with correct namespace with " .
00047             "<text> appended after the article name", false, true );
00048         $this->addOption( 'prefix', "Do an explicit check for the given title prefix " .
00049             "appended after the article name", false, true );
00050     }
00051 
00052     public function execute() {
00053         $this->db = wfGetDB( DB_MASTER );
00054 
00055         $fix = $this->hasOption( 'fix' );
00056         $suffix = $this->getOption( 'suffix', '' );
00057         $prefix = $this->getOption( 'prefix', '' );
00058         $key = intval( $this->getOption( 'key', 0 ) );
00059 
00060         if ( $prefix ) {
00061             $retval = $this->checkPrefix( $key, $prefix, $fix, $suffix );
00062         } else {
00063             $retval = $this->checkAll( $fix, $suffix );
00064         }
00065 
00066         if ( $retval ) {
00067             $this->output( "\nLooks good!\n" );
00068         } else {
00069             $this->output( "\nOh noeees\n" );
00070         }
00071     }
00072 
00080     private function checkAll( $fix, $suffix = '' ) {
00081         global $wgContLang, $wgNamespaceAliases, $wgCapitalLinks;
00082 
00083         $spaces = array();
00084 
00085         // List interwikis first, so they'll be overridden
00086         // by any conflicting local namespaces.
00087         foreach ( $this->getInterwikiList() as $prefix ) {
00088             $name = $wgContLang->ucfirst( $prefix );
00089             $spaces[$name] = 0;
00090         }
00091 
00092         // Now pull in all canonical and alias namespaces...
00093         foreach ( MWNamespace::getCanonicalNamespaces() as $ns => $name ) {
00094             // This includes $wgExtraNamespaces
00095             if ( $name !== '' ) {
00096                 $spaces[$name] = $ns;
00097             }
00098         }
00099         foreach ( $wgContLang->getNamespaces() as $ns => $name ) {
00100             if ( $name !== '' ) {
00101                 $spaces[$name] = $ns;
00102             }
00103         }
00104         foreach ( $wgNamespaceAliases as $name => $ns ) {
00105             $spaces[$name] = $ns;
00106         }
00107         foreach ( $wgContLang->getNamespaceAliases() as $name => $ns ) {
00108             $spaces[$name] = $ns;
00109         }
00110 
00111         // We'll need to check for lowercase keys as well,
00112         // since we're doing case-sensitive searches in the db.
00113         foreach ( $spaces as $name => $ns ) {
00114             $moreNames = array();
00115             $moreNames[] = $wgContLang->uc( $name );
00116             $moreNames[] = $wgContLang->ucfirst( $wgContLang->lc( $name ) );
00117             $moreNames[] = $wgContLang->ucwords( $name );
00118             $moreNames[] = $wgContLang->ucwords( $wgContLang->lc( $name ) );
00119             $moreNames[] = $wgContLang->ucwordbreaks( $name );
00120             $moreNames[] = $wgContLang->ucwordbreaks( $wgContLang->lc( $name ) );
00121             if ( !$wgCapitalLinks ) {
00122                 foreach ( $moreNames as $altName ) {
00123                     $moreNames[] = $wgContLang->lcfirst( $altName );
00124                 }
00125                 $moreNames[] = $wgContLang->lcfirst( $name );
00126             }
00127             foreach ( array_unique( $moreNames ) as $altName ) {
00128                 if ( $altName !== $name ) {
00129                     $spaces[$altName] = $ns;
00130                 }
00131             }
00132         }
00133 
00134         ksort( $spaces );
00135         asort( $spaces );
00136 
00137         $ok = true;
00138         foreach ( $spaces as $name => $ns ) {
00139             $ok = $this->checkNamespace( $ns, $name, $fix, $suffix ) && $ok;
00140         }
00141 
00142         return $ok;
00143     }
00144 
00150     private function getInterwikiList() {
00151         $result = Interwiki::getAllPrefixes();
00152         $prefixes = array();
00153         foreach ( $result as $row ) {
00154             $prefixes[] = $row['iw_prefix'];
00155         }
00156 
00157         return $prefixes;
00158     }
00159 
00168     private function checkNamespace( $ns, $name, $fix, $suffix = '' ) {
00169         $conflicts = $this->getConflicts( $ns, $name );
00170         $count = count( $conflicts );
00171         if ( $count == 0 ) {
00172             return true;
00173         }
00174 
00175         $ok = true;
00176         foreach ( $conflicts as $row ) {
00177             $resolvable = $this->reportConflict( $row, $suffix );
00178             $ok = $ok && $resolvable;
00179             if ( $fix && ( $resolvable || $suffix != '' ) ) {
00180                 $ok = $this->resolveConflict( $row, $resolvable, $suffix ) && $ok;
00181             }
00182         }
00183 
00184         return $ok;
00185     }
00186 
00195     private function checkPrefix( $key, $prefix, $fix, $suffix = '' ) {
00196         $this->output( "Checking prefix \"$prefix\" vs namespace $key\n" );
00197 
00198         return $this->checkNamespace( $key, $prefix, $fix, $suffix );
00199     }
00200 
00210     private function getConflicts( $ns, $name ) {
00211         $titleSql = "TRIM(LEADING {$this->db->addQuotes( "$name:" )} FROM page_title)";
00212         if ( $ns == 0 ) {
00213             // An interwiki; try an alternate encoding with '-' for ':'
00214             $titleSql = $this->db->buildConcat( array(
00215                 $this->db->addQuotes( "$name-" ),
00216                 $titleSql,
00217             ) );
00218         }
00219 
00220         return iterator_to_array( $this->db->select( 'page',
00221             array(
00222                 'id' => 'page_id',
00223                 'oldtitle' => 'page_title',
00224                 'namespace' => $this->db->addQuotes( $ns ) . ' + page_namespace',
00225                 'title' => $titleSql,
00226                 'oldnamespace' => 'page_namespace',
00227             ),
00228             array(
00229                 'page_namespace' => array( 0, 1 ),
00230                 'page_title' . $this->db->buildLike( "$name:", $this->db->anyString() ),
00231             ),
00232             __METHOD__
00233         ) );
00234     }
00235 
00243     private function reportConflict( $row, $suffix ) {
00244         $newTitle = Title::makeTitleSafe( $row->namespace, $row->title );
00245         if ( is_null( $newTitle ) || !$newTitle->canExist() ) {
00246             // Title is also an illegal title...
00247             // For the moment we'll let these slide to cleanupTitles or whoever.
00248             $this->output( sprintf( "... %d (%d,\"%s\")\n",
00249                 $row->id,
00250                 $row->oldnamespace,
00251                 $row->oldtitle ) );
00252             $this->output( "...  *** cannot resolve automatically; illegal title ***\n" );
00253 
00254             return false;
00255         }
00256 
00257         $this->output( sprintf( "... %d (%d,\"%s\") -> (%d,\"%s\") [[%s]]\n",
00258             $row->id,
00259             $row->oldnamespace,
00260             $row->oldtitle,
00261             $newTitle->getNamespace(),
00262             $newTitle->getDBkey(),
00263             $newTitle->getPrefixedText() ) );
00264 
00265         $id = $newTitle->getArticleID();
00266         if ( $id ) {
00267             $this->output( "...  *** cannot resolve automatically; page exists with ID $id ***\n" );
00268 
00269             return false;
00270         } else {
00271             return true;
00272         }
00273     }
00274 
00283     private function resolveConflict( $row, $resolvable, $suffix ) {
00284         if ( !$resolvable ) {
00285             $this->output( "...  *** old title {$row->title}\n" );
00286             while ( true ) {
00287                 $row->title .= $suffix;
00288                 $this->output( "...  *** new title {$row->title}\n" );
00289                 $title = Title::makeTitleSafe( $row->namespace, $row->title );
00290                 if ( !$title ) {
00291                     $this->output( "... !!! invalid title\n" );
00292 
00293                     return false;
00294                 }
00295                 $id = $title->getArticleID();
00296                 if ( $id ) {
00297                     $this->output( "...  *** page exists with ID $id ***\n" );
00298                 } else {
00299                     break;
00300                 }
00301             }
00302             $this->output( "...  *** using suffixed form [[" . $title->getPrefixedText() . "]] ***\n" );
00303         }
00304         $this->resolveConflictOn( $row, 'page', 'page' );
00305 
00306         return true;
00307     }
00308 
00317     private function resolveConflictOn( $row, $table, $prefix ) {
00318         $this->output( "... resolving on $table... " );
00319         $newTitle = Title::makeTitleSafe( $row->namespace, $row->title );
00320         $this->db->update( $table,
00321             array(
00322                 "{$prefix}_namespace" => $newTitle->getNamespace(),
00323                 "{$prefix}_title" => $newTitle->getDBkey(),
00324             ),
00325             array(
00326                 // "{$prefix}_namespace" => 0,
00327                 // "{$prefix}_title" => $row->oldtitle,
00328                 "{$prefix}_id" => $row->id,
00329             ),
00330             __METHOD__ );
00331         $this->output( "ok.\n" );
00332 
00333         return true;
00334     }
00335 }
00336 
00337 $maintClass = "NamespaceConflictChecker";
00338 require_once RUN_MAINTENANCE_IF_MAIN;