MediaWiki  REL1_22
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         global $wgTitle;
00054 
00055         $this->db = wfGetDB( DB_MASTER );
00056         $wgTitle = Title::newFromText( 'Namespace title conflict cleanup script' );
00057 
00058         $fix = $this->hasOption( 'fix' );
00059         $suffix = $this->getOption( 'suffix', '' );
00060         $prefix = $this->getOption( 'prefix', '' );
00061         $key = intval( $this->getOption( 'key', 0 ) );
00062 
00063         if ( $prefix ) {
00064             $retval = $this->checkPrefix( $key, $prefix, $fix, $suffix );
00065         } else {
00066             $retval = $this->checkAll( $fix, $suffix );
00067         }
00068 
00069         if ( $retval ) {
00070             $this->output( "\nLooks good!\n" );
00071         } else {
00072             $this->output( "\nOh noeees\n" );
00073         }
00074     }
00075 
00083     private function checkAll( $fix, $suffix = '' ) {
00084         global $wgContLang, $wgNamespaceAliases, $wgCapitalLinks;
00085 
00086         $spaces = array();
00087 
00088         // List interwikis first, so they'll be overridden
00089         // by any conflicting local namespaces.
00090         foreach ( $this->getInterwikiList() as $prefix ) {
00091             $name = $wgContLang->ucfirst( $prefix );
00092             $spaces[$name] = 0;
00093         }
00094 
00095         // Now pull in all canonical and alias namespaces...
00096         foreach ( MWNamespace::getCanonicalNamespaces() as $ns => $name ) {
00097             // This includes $wgExtraNamespaces
00098             if ( $name !== '' ) {
00099                 $spaces[$name] = $ns;
00100             }
00101         }
00102         foreach ( $wgContLang->getNamespaces() as $ns => $name ) {
00103             if ( $name !== '' ) {
00104                 $spaces[$name] = $ns;
00105             }
00106         }
00107         foreach ( $wgNamespaceAliases as $name => $ns ) {
00108             $spaces[$name] = $ns;
00109         }
00110         foreach ( $wgContLang->getNamespaceAliases() as $name => $ns ) {
00111             $spaces[$name] = $ns;
00112         }
00113 
00114         // We'll need to check for lowercase keys as well,
00115         // since we're doing case-sensitive searches in the db.
00116         foreach ( $spaces as $name => $ns ) {
00117             $moreNames = array();
00118             $moreNames[] = $wgContLang->uc( $name );
00119             $moreNames[] = $wgContLang->ucfirst( $wgContLang->lc( $name ) );
00120             $moreNames[] = $wgContLang->ucwords( $name );
00121             $moreNames[] = $wgContLang->ucwords( $wgContLang->lc( $name ) );
00122             $moreNames[] = $wgContLang->ucwordbreaks( $name );
00123             $moreNames[] = $wgContLang->ucwordbreaks( $wgContLang->lc( $name ) );
00124             if ( !$wgCapitalLinks ) {
00125                 foreach ( $moreNames as $altName ) {
00126                     $moreNames[] = $wgContLang->lcfirst( $altName );
00127                 }
00128                 $moreNames[] = $wgContLang->lcfirst( $name );
00129             }
00130             foreach ( array_unique( $moreNames ) as $altName ) {
00131                 if ( $altName !== $name ) {
00132                     $spaces[$altName] = $ns;
00133                 }
00134             }
00135         }
00136 
00137         ksort( $spaces );
00138         asort( $spaces );
00139 
00140         $ok = true;
00141         foreach ( $spaces as $name => $ns ) {
00142             $ok = $this->checkNamespace( $ns, $name, $fix, $suffix ) && $ok;
00143         }
00144         return $ok;
00145     }
00146 
00152     private function getInterwikiList() {
00153         $result = Interwiki::getAllPrefixes();
00154         $prefixes = array();
00155         foreach ( $result as $row ) {
00156             $prefixes[] = $row['iw_prefix'];
00157         }
00158         return $prefixes;
00159     }
00160 
00169     private function checkNamespace( $ns, $name, $fix, $suffix = '' ) {
00170         $conflicts = $this->getConflicts( $ns, $name );
00171         $count = count( $conflicts );
00172         if ( $count == 0 ) {
00173             return true;
00174         }
00175 
00176         $ok = true;
00177         foreach ( $conflicts as $row ) {
00178             $resolvable = $this->reportConflict( $row, $suffix );
00179             $ok = $ok && $resolvable;
00180             if ( $fix && ( $resolvable || $suffix != '' ) ) {
00181                 $ok = $this->resolveConflict( $row, $resolvable, $suffix ) && $ok;
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         return $this->checkNamespace( $key, $prefix, $fix, $suffix );
00198     }
00199 
00209     private function getConflicts( $ns, $name ) {
00210         $page = 'page';
00211         $table = $this->db->tableName( $page );
00212 
00213         $prefix = $this->db->strencode( $name );
00214         $encNamespace = $this->db->addQuotes( $ns );
00215 
00216         $titleSql = "TRIM(LEADING '$prefix:' FROM {$page}_title)";
00217         if ( $ns == 0 ) {
00218             // An interwiki; try an alternate encoding with '-' for ':'
00219             $titleSql = $this->db->buildConcat( array( "'$prefix-'", $titleSql ) );
00220         }
00221 
00222         $sql = "SELECT {$page}_id    AS id,
00223                        {$page}_title AS oldtitle,
00224                        $encNamespace + {$page}_namespace AS namespace,
00225                    $titleSql     AS title,
00226                    {$page}_namespace AS oldnamespace
00227                   FROM {$table}
00228                  WHERE ( {$page}_namespace=0 OR {$page}_namespace=1 )
00229                    AND {$page}_title " . $this->db->buildLike( $name . ':', $this->db->anyString() );
00230 
00231         $result = $this->db->query( $sql, __METHOD__ );
00232 
00233         $set = array();
00234         foreach ( $result as $row ) {
00235             $set[] = $row;
00236         }
00237         return $set;
00238     }
00239 
00245     private function reportConflict( $row, $suffix ) {
00246         $newTitle = Title::makeTitleSafe( $row->namespace, $row->title );
00247         if ( is_null( $newTitle ) || !$newTitle->canExist() ) {
00248             // Title is also an illegal title...
00249             // For the moment we'll let these slide to cleanupTitles or whoever.
00250             $this->output( sprintf( "... %d (%d,\"%s\")\n",
00251                 $row->id,
00252                 $row->oldnamespace,
00253                 $row->oldtitle ) );
00254             $this->output( "...  *** cannot resolve automatically; illegal title ***\n" );
00255             return false;
00256         }
00257 
00258         $this->output( sprintf( "... %d (%d,\"%s\") -> (%d,\"%s\") [[%s]]\n",
00259             $row->id,
00260             $row->oldnamespace,
00261             $row->oldtitle,
00262             $newTitle->getNamespace(),
00263             $newTitle->getDBkey(),
00264             $newTitle->getPrefixedText() ) );
00265 
00266         $id = $newTitle->getArticleID();
00267         if ( $id ) {
00268             $this->output( "...  *** cannot resolve automatically; page exists with ID $id ***\n" );
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                     return false;
00293                 }
00294                 $id = $title->getArticleID();
00295                 if ( $id ) {
00296                     $this->output( "...  *** page exists with ID $id ***\n" );
00297                 } else {
00298                     break;
00299                 }
00300             }
00301             $this->output( "...  *** using suffixed form [[" . $title->getPrefixedText() . "]] ***\n" );
00302         }
00303         $this->resolveConflictOn( $row, 'page', 'page' );
00304         return true;
00305     }
00306 
00315     private function resolveConflictOn( $row, $table, $prefix ) {
00316         $this->output( "... resolving on $table... " );
00317         $newTitle = Title::makeTitleSafe( $row->namespace, $row->title );
00318         $this->db->update( $table,
00319             array(
00320                 "{$prefix}_namespace" => $newTitle->getNamespace(),
00321                 "{$prefix}_title" => $newTitle->getDBkey(),
00322             ),
00323             array(
00324                 // "{$prefix}_namespace" => 0,
00325                 // "{$prefix}_title" => $row->oldtitle,
00326                 "{$prefix}_id" => $row->id,
00327             ),
00328             __METHOD__ );
00329         $this->output( "ok.\n" );
00330         return true;
00331     }
00332 }
00333 
00334 $maintClass = "NamespaceConflictChecker";
00335 require_once RUN_MAINTENANCE_IF_MAIN;