MediaWiki  REL1_23
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         return $ok;
00142     }
00143 
00149     private function getInterwikiList() {
00150         $result = Interwiki::getAllPrefixes();
00151         $prefixes = array();
00152         foreach ( $result as $row ) {
00153             $prefixes[] = $row['iw_prefix'];
00154         }
00155         return $prefixes;
00156     }
00157 
00166     private function checkNamespace( $ns, $name, $fix, $suffix = '' ) {
00167         $conflicts = $this->getConflicts( $ns, $name );
00168         $count = count( $conflicts );
00169         if ( $count == 0 ) {
00170             return true;
00171         }
00172 
00173         $ok = true;
00174         foreach ( $conflicts as $row ) {
00175             $resolvable = $this->reportConflict( $row, $suffix );
00176             $ok = $ok && $resolvable;
00177             if ( $fix && ( $resolvable || $suffix != '' ) ) {
00178                 $ok = $this->resolveConflict( $row, $resolvable, $suffix ) && $ok;
00179             }
00180         }
00181         return $ok;
00182     }
00183 
00192     private function checkPrefix( $key, $prefix, $fix, $suffix = '' ) {
00193         $this->output( "Checking prefix \"$prefix\" vs namespace $key\n" );
00194         return $this->checkNamespace( $key, $prefix, $fix, $suffix );
00195     }
00196 
00206     private function getConflicts( $ns, $name ) {
00207         $page = 'page';
00208         $table = $this->db->tableName( $page );
00209 
00210         $prefix = $this->db->strencode( $name );
00211         $encNamespace = $this->db->addQuotes( $ns );
00212 
00213         $titleSql = "TRIM(LEADING '$prefix:' FROM {$page}_title)";
00214         if ( $ns == 0 ) {
00215             // An interwiki; try an alternate encoding with '-' for ':'
00216             $titleSql = $this->db->buildConcat( array( "'$prefix-'", $titleSql ) );
00217         }
00218 
00219         $sql = "SELECT {$page}_id    AS id,
00220                        {$page}_title AS oldtitle,
00221                        $encNamespace + {$page}_namespace AS namespace,
00222                    $titleSql     AS title,
00223                    {$page}_namespace AS oldnamespace
00224                   FROM {$table}
00225                  WHERE ( {$page}_namespace=0 OR {$page}_namespace=1 )
00226                    AND {$page}_title " . $this->db->buildLike( $name . ':', $this->db->anyString() );
00227 
00228         $result = $this->db->query( $sql, __METHOD__ );
00229 
00230         $set = array();
00231         foreach ( $result as $row ) {
00232             $set[] = $row;
00233         }
00234         return $set;
00235     }
00236 
00242     private function reportConflict( $row, $suffix ) {
00243         $newTitle = Title::makeTitleSafe( $row->namespace, $row->title );
00244         if ( is_null( $newTitle ) || !$newTitle->canExist() ) {
00245             // Title is also an illegal title...
00246             // For the moment we'll let these slide to cleanupTitles or whoever.
00247             $this->output( sprintf( "... %d (%d,\"%s\")\n",
00248                 $row->id,
00249                 $row->oldnamespace,
00250                 $row->oldtitle ) );
00251             $this->output( "...  *** cannot resolve automatically; illegal title ***\n" );
00252             return false;
00253         }
00254 
00255         $this->output( sprintf( "... %d (%d,\"%s\") -> (%d,\"%s\") [[%s]]\n",
00256             $row->id,
00257             $row->oldnamespace,
00258             $row->oldtitle,
00259             $newTitle->getNamespace(),
00260             $newTitle->getDBkey(),
00261             $newTitle->getPrefixedText() ) );
00262 
00263         $id = $newTitle->getArticleID();
00264         if ( $id ) {
00265             $this->output( "...  *** cannot resolve automatically; page exists with ID $id ***\n" );
00266             return false;
00267         } else {
00268             return true;
00269         }
00270     }
00271 
00280     private function resolveConflict( $row, $resolvable, $suffix ) {
00281         if ( !$resolvable ) {
00282             $this->output( "...  *** old title {$row->title}\n" );
00283             while ( true ) {
00284                 $row->title .= $suffix;
00285                 $this->output( "...  *** new title {$row->title}\n" );
00286                 $title = Title::makeTitleSafe( $row->namespace, $row->title );
00287                 if ( !$title ) {
00288                     $this->output( "... !!! invalid title\n" );
00289                     return false;
00290                 }
00291                 $id = $title->getArticleID();
00292                 if ( $id ) {
00293                     $this->output( "...  *** page exists with ID $id ***\n" );
00294                 } else {
00295                     break;
00296                 }
00297             }
00298             $this->output( "...  *** using suffixed form [[" . $title->getPrefixedText() . "]] ***\n" );
00299         }
00300         $this->resolveConflictOn( $row, 'page', 'page' );
00301         return true;
00302     }
00303 
00312     private function resolveConflictOn( $row, $table, $prefix ) {
00313         $this->output( "... resolving on $table... " );
00314         $newTitle = Title::makeTitleSafe( $row->namespace, $row->title );
00315         $this->db->update( $table,
00316             array(
00317                 "{$prefix}_namespace" => $newTitle->getNamespace(),
00318                 "{$prefix}_title" => $newTitle->getDBkey(),
00319             ),
00320             array(
00321                 // "{$prefix}_namespace" => 0,
00322                 // "{$prefix}_title" => $row->oldtitle,
00323                 "{$prefix}_id" => $row->id,
00324             ),
00325             __METHOD__ );
00326         $this->output( "ok.\n" );
00327         return true;
00328     }
00329 }
00330 
00331 $maintClass = "NamespaceConflictChecker";
00332 require_once RUN_MAINTENANCE_IF_MAIN;