MediaWiki  REL1_20
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 
00153         private function getInterwikiList() {
00154                 $result = $this->db->select( 'interwiki', array( 'iw_prefix' ) );
00155                 $prefixes = array();
00156                 foreach ( $result as $row ) {
00157                         $prefixes[] = $row->iw_prefix;
00158                 }
00159                 return $prefixes;
00160         }
00161 
00170         private function checkNamespace( $ns, $name, $fix, $suffix = '' ) {
00171                 $conflicts = $this->getConflicts( $ns, $name );
00172                 $count = count( $conflicts );
00173                 if ( $count == 0 ) {
00174                         return true;
00175                 }
00176 
00177                 $ok = true;
00178                 foreach ( $conflicts as $row ) {
00179                         $resolvable = $this->reportConflict( $row, $suffix );
00180                         $ok = $ok && $resolvable;
00181                         if ( $fix && ( $resolvable || $suffix != '' ) ) {
00182                                 $ok = $this->resolveConflict( $row, $resolvable, $suffix ) && $ok;
00183                         }
00184                 }
00185                 return $ok;
00186         }
00187 
00196         private function checkPrefix( $key, $prefix, $fix, $suffix = '' ) {
00197                 $this->output( "Checking prefix \"$prefix\" vs namespace $key\n" );
00198                 return $this->checkNamespace( $key, $prefix, $fix, $suffix );
00199         }
00200 
00210         private function getConflicts( $ns, $name ) {
00211                 $page  = 'page';
00212                 $table = $this->db->tableName( $page );
00213 
00214                 $prefix     = $this->db->strencode( $name );
00215                 $encNamespace = $this->db->addQuotes( $ns );
00216 
00217                 $titleSql = "TRIM(LEADING '$prefix:' FROM {$page}_title)";
00218                 if ( $ns == 0 ) {
00219                         // An interwiki; try an alternate encoding with '-' for ':'
00220                         $titleSql = $this->db->buildConcat( array( "'$prefix-'", $titleSql ) );
00221                 }
00222 
00223                 $sql = "SELECT {$page}_id    AS id,
00224                                            {$page}_title AS oldtitle,
00225                                            $encNamespace + {$page}_namespace AS namespace,
00226                                    $titleSql     AS title,
00227                                    {$page}_namespace AS oldnamespace
00228                                   FROM {$table}
00229                                  WHERE ( {$page}_namespace=0 OR {$page}_namespace=1 )
00230                                    AND {$page}_title " . $this->db->buildLike( $name . ':', $this->db->anyString() );
00231 
00232                 $result = $this->db->query( $sql, __METHOD__ );
00233 
00234                 $set = array();
00235                 foreach ( $result as $row ) {
00236                         $set[] = $row;
00237                 }
00238                 return $set;
00239         }
00240 
00246         private function reportConflict( $row, $suffix ) {
00247                 $newTitle = Title::makeTitleSafe( $row->namespace, $row->title );
00248                 if ( is_null( $newTitle ) || !$newTitle->canExist() ) {
00249                         // Title is also an illegal title...
00250                         // For the moment we'll let these slide to cleanupTitles or whoever.
00251                         $this->output( sprintf( "... %d (%d,\"%s\")\n",
00252                                 $row->id,
00253                                 $row->oldnamespace,
00254                                 $row->oldtitle ) );
00255                         $this->output( "...  *** cannot resolve automatically; illegal title ***\n" );
00256                         return false;
00257                 }
00258 
00259                 $this->output( sprintf( "... %d (%d,\"%s\") -> (%d,\"%s\") [[%s]]\n",
00260                         $row->id,
00261                         $row->oldnamespace,
00262                         $row->oldtitle,
00263                         $newTitle->getNamespace(),
00264                         $newTitle->getDBkey(),
00265                         $newTitle->getPrefixedText() ) );
00266 
00267                 $id = $newTitle->getArticleID();
00268                 if ( $id ) {
00269                         $this->output( "...  *** cannot resolve automatically; page exists with ID $id ***\n" );
00270                         return false;
00271                 } else {
00272                         return true;
00273                 }
00274         }
00275 
00284         private function resolveConflict( $row, $resolvable, $suffix ) {
00285                 if ( !$resolvable ) {
00286                         $this->output( "...  *** old title {$row->title}\n" );
00287                         while ( true ) {
00288                                 $row->title .= $suffix;
00289                                 $this->output( "...  *** new title {$row->title}\n" );
00290                                 $title = Title::makeTitleSafe( $row->namespace, $row->title );
00291                                 if ( !$title ) {
00292                                         $this->output( "... !!! invalid title\n" );
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                 return true;
00306         }
00307 
00316         private function resolveConflictOn( $row, $table, $prefix ) {
00317                 $this->output( "... resolving on $table... " );
00318                 $newTitle = Title::makeTitleSafe( $row->namespace, $row->title );
00319                 $this->db->update( $table,
00320                         array(
00321                                 "{$prefix}_namespace" => $newTitle->getNamespace(),
00322                                 "{$prefix}_title"     => $newTitle->getDBkey(),
00323                         ),
00324                         array(
00325                                 // "{$prefix}_namespace" => 0,
00326                                 // "{$prefix}_title"     => $row->oldtitle,
00327                                 "{$prefix}_id"           => $row->id,
00328                         ),
00329                         __METHOD__ );
00330                 $this->output( "ok.\n" );
00331                 return true;
00332         }
00333 }
00334 
00335 $maintClass = "NamespaceConflictChecker";
00336 require_once( RUN_MAINTENANCE_IF_MAIN );