MediaWiki
REL1_20
|
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 );