MediaWiki  REL1_19
namespaceDupes.php
Go to the documentation of this file.
00001 <?php
00026 require_once( dirname( __FILE__ ) . '/Maintenance.php' );
00027 
00028 class NamespaceConflictChecker extends Maintenance {
00029 
00033         protected $db;
00034 
00035         public function __construct() {
00036                 parent::__construct();
00037                 $this->mDescription = "";
00038                 $this->addOption( 'fix', 'Attempt to automatically fix errors' );
00039                 $this->addOption( 'suffix', "Dupes will be renamed with correct namespace with " .
00040                                                                         "<text> appended after the article name", false, true );
00041                 $this->addOption( 'prefix', "Do an explicit check for the given title prefix " .
00042                                                                         "appended after the article name", false, true );
00043         }
00044 
00045         public function execute() {
00046                 global $wgTitle;
00047 
00048                 $this->db = wfGetDB( DB_MASTER );
00049                 $wgTitle = Title::newFromText( 'Namespace title conflict cleanup script' );
00050 
00051                 $fix = $this->hasOption( 'fix' );
00052                 $suffix = $this->getOption( 'suffix', '' );
00053                 $prefix = $this->getOption( 'prefix', '' );
00054                 $key = intval( $this->getOption( 'key', 0 ) );
00055 
00056                 if ( $prefix ) {
00057                         $retval = $this->checkPrefix( $key, $prefix, $fix, $suffix );
00058                 } else {
00059                         $retval = $this->checkAll( $fix, $suffix );
00060                 }
00061 
00062                 if ( $retval ) {
00063                         $this->output( "\nLooks good!\n" );
00064                 } else {
00065                         $this->output( "\nOh noeees\n" );
00066                 }
00067         }
00068 
00076         private function checkAll( $fix, $suffix = '' ) {
00077                 global $wgContLang, $wgNamespaceAliases, $wgCapitalLinks;
00078 
00079                 $spaces = array();
00080 
00081                 // List interwikis first, so they'll be overridden
00082                 // by any conflicting local namespaces.
00083                 foreach ( $this->getInterwikiList() as $prefix ) {
00084                         $name = $wgContLang->ucfirst( $prefix );
00085                         $spaces[$name] = 0;
00086                 }
00087 
00088                 // Now pull in all canonical and alias namespaces...
00089                 foreach ( MWNamespace::getCanonicalNamespaces() as $ns => $name ) {
00090                         // This includes $wgExtraNamespaces
00091                         if ( $name !== '' ) {
00092                                 $spaces[$name] = $ns;
00093                         }
00094                 }
00095                 foreach ( $wgContLang->getNamespaces() as $ns => $name ) {
00096                         if ( $name !== '' ) {
00097                                 $spaces[$name] = $ns;
00098                         }
00099                 }
00100                 foreach ( $wgNamespaceAliases as $name => $ns ) {
00101                         $spaces[$name] = $ns;
00102                 }
00103                 foreach ( $wgContLang->getNamespaceAliases() as $name => $ns ) {
00104                         $spaces[$name] = $ns;
00105                 }
00106 
00107                 // We'll need to check for lowercase keys as well,
00108                 // since we're doing case-sensitive searches in the db.
00109                 foreach ( $spaces as $name => $ns ) {
00110                         $moreNames = array();
00111                         $moreNames[] = $wgContLang->uc( $name );
00112                         $moreNames[] = $wgContLang->ucfirst( $wgContLang->lc( $name ) );
00113                         $moreNames[] = $wgContLang->ucwords( $name );
00114                         $moreNames[] = $wgContLang->ucwords( $wgContLang->lc( $name ) );
00115                         $moreNames[] = $wgContLang->ucwordbreaks( $name );
00116                         $moreNames[] = $wgContLang->ucwordbreaks( $wgContLang->lc( $name ) );
00117                         if ( !$wgCapitalLinks ) {
00118                                 foreach ( $moreNames as $altName ) {
00119                                         $moreNames[] = $wgContLang->lcfirst( $altName );
00120                                 }
00121                                 $moreNames[] = $wgContLang->lcfirst( $name );
00122                         }
00123                         foreach ( array_unique( $moreNames ) as $altName ) {
00124                                 if ( $altName !== $name ) {
00125                                         $spaces[$altName] = $ns;
00126                                 }
00127                         }
00128                 }
00129 
00130                 ksort( $spaces );
00131                 asort( $spaces );
00132 
00133                 $ok = true;
00134                 foreach ( $spaces as $name => $ns ) {
00135                         $ok = $this->checkNamespace( $ns, $name, $fix, $suffix ) && $ok;
00136                 }
00137                 return $ok;
00138         }
00139 
00146         private function getInterwikiList() {
00147                 $result = $this->db->select( 'interwiki', array( 'iw_prefix' ) );
00148                 $prefixes = array();
00149                 foreach ( $result as $row ) {
00150                         $prefixes[] = $row->iw_prefix;
00151                 }
00152                 return $prefixes;
00153         }
00154 
00163         private function checkNamespace( $ns, $name, $fix, $suffix = '' ) {
00164                 $conflicts = $this->getConflicts( $ns, $name );
00165                 $count = count( $conflicts );
00166                 if ( $count == 0 ) {
00167                         return true;
00168                 }
00169 
00170                 $ok = true;
00171                 foreach ( $conflicts as $row ) {
00172                         $resolvable = $this->reportConflict( $row, $suffix );
00173                         $ok = $ok && $resolvable;
00174                         if ( $fix && ( $resolvable || $suffix != '' ) ) {
00175                                 $ok = $this->resolveConflict( $row, $resolvable, $suffix ) && $ok;
00176                         }
00177                 }
00178                 return $ok;
00179         }
00180 
00189         private function checkPrefix( $key, $prefix, $fix, $suffix = '' ) {
00190                 $this->output( "Checking prefix \"$prefix\" vs namespace $key\n" );
00191                 return $this->checkNamespace( $key, $prefix, $fix, $suffix );
00192         }
00193 
00203         private function getConflicts( $ns, $name ) {
00204                 $page  = 'page';
00205                 $table = $this->db->tableName( $page );
00206 
00207                 $prefix     = $this->db->strencode( $name );
00208                 $encNamespace = $this->db->addQuotes( $ns );
00209 
00210                 $titleSql = "TRIM(LEADING '$prefix:' FROM {$page}_title)";
00211                 if ( $ns == 0 ) {
00212                         // An interwiki; try an alternate encoding with '-' for ':'
00213                         $titleSql = $this->db->buildConcat( array( "'$prefix-'", $titleSql ) );
00214                 }
00215 
00216                 $sql = "SELECT {$page}_id    AS id,
00217                                            {$page}_title AS oldtitle,
00218                                            $encNamespace + {$page}_namespace AS namespace,
00219                                    $titleSql     AS title,
00220                                    {$page}_namespace AS oldnamespace
00221                                   FROM {$table}
00222                                  WHERE ( {$page}_namespace=0 OR {$page}_namespace=1 )
00223                                    AND {$page}_title " . $this->db->buildLike( $name . ':', $this->db->anyString() );
00224 
00225                 $result = $this->db->query( $sql, __METHOD__ );
00226 
00227                 $set = array();
00228                 foreach ( $result as $row ) {
00229                         $set[] = $row;
00230                 }
00231                 return $set;
00232         }
00233 
00239         private function reportConflict( $row, $suffix ) {
00240                 $newTitle = Title::makeTitleSafe( $row->namespace, $row->title );
00241                 if ( is_null( $newTitle ) || !$newTitle->canExist() ) {
00242                         // Title is also an illegal title...
00243                         // For the moment we'll let these slide to cleanupTitles or whoever.
00244                         $this->output( sprintf( "... %d (%d,\"%s\")\n",
00245                                 $row->id,
00246                                 $row->oldnamespace,
00247                                 $row->oldtitle ) );
00248                         $this->output( "...  *** cannot resolve automatically; illegal title ***\n" );
00249                         return false;
00250                 }
00251 
00252                 $this->output( sprintf( "... %d (%d,\"%s\") -> (%d,\"%s\") [[%s]]\n",
00253                         $row->id,
00254                         $row->oldnamespace,
00255                         $row->oldtitle,
00256                         $newTitle->getNamespace(),
00257                         $newTitle->getDBkey(),
00258                         $newTitle->getPrefixedText() ) );
00259 
00260                 $id = $newTitle->getArticleId();
00261                 if ( $id ) {
00262                         $this->output( "...  *** cannot resolve automatically; page exists with ID $id ***\n" );
00263                         return false;
00264                 } else {
00265                         return true;
00266                 }
00267         }
00268 
00277         private function resolveConflict( $row, $resolvable, $suffix ) {
00278                 if ( !$resolvable ) {
00279                         $this->output( "...  *** old title {$row->title}\n" );
00280                         while ( true ) {
00281                                 $row->title .= $suffix;
00282                                 $this->output( "...  *** new title {$row->title}\n" );
00283                                 $title = Title::makeTitleSafe( $row->namespace, $row->title );
00284                                 if ( !$title ) {
00285                                         $this->output( "... !!! invalid title\n" );
00286                                         return false;
00287                                 }
00288                                 $id = $title->getArticleId();
00289                                 if ( $id ) {
00290                                         $this->output( "...  *** page exists with ID $id ***\n" );
00291                                 } else {
00292                                         break;
00293                                 }
00294                         }
00295                         $this->output( "...  *** using suffixed form [[" . $title->getPrefixedText() . "]] ***\n" );
00296                 }
00297                 $this->resolveConflictOn( $row, 'page', 'page' );
00298                 return true;
00299         }
00300 
00309         private function resolveConflictOn( $row, $table, $prefix ) {
00310                 $this->output( "... resolving on $table... " );
00311                 $newTitle = Title::makeTitleSafe( $row->namespace, $row->title );
00312                 $this->db->update( $table,
00313                         array(
00314                                 "{$prefix}_namespace" => $newTitle->getNamespace(),
00315                                 "{$prefix}_title"     => $newTitle->getDBkey(),
00316                         ),
00317                         array(
00318                                 // "{$prefix}_namespace" => 0,
00319                                 // "{$prefix}_title"     => $row->oldtitle,
00320                                 "{$prefix}_id"           => $row->id,
00321                         ),
00322                         __METHOD__ );
00323                 $this->output( "ok.\n" );
00324                 return true;
00325         }
00326 }
00327 
00328 $maintClass = "NamespaceConflictChecker";
00329 require_once( RUN_MAINTENANCE_IF_MAIN );