00001 <?php
00002 # Copyright (C) 2005-2007 Brion Vibber <brion@pobox.com>
00003 # http://www.mediawiki.org/
00004 #
00005 # This program is free software; you can redistribute it and/or modify
00006 # it under the terms of the GNU General Public License as published by
00007 # the Free Software Foundation; either version 2 of the License, or
00008 # (at your option) any later version.
00009 #
00010 # This program is distributed in the hope that it will be useful,
00011 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00013 # GNU General Public License for more details.
00014 #
00015 # You should have received a copy of the GNU General Public License along
00016 # with this program; if not, write to the Free Software Foundation, Inc.,
00017 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00018 # http://www.gnu.org/copyleft/gpl.html
00019
00025 $options = array( 'fix', 'suffix', 'help' );
00026
00028 require_once( 'commandLine.inc' );
00029
00030 if(isset( $options['help'] ) ) {
00031 print <<<ENDS
00032 usage: namespaceDupes.php [--fix] [--suffix=<text>] [--help]
00033 --help : this help message
00034 --fix : attempt to automatically fix errors
00035 --suffix=<text> : dupes will be renamed with correct namespace with <text>
00036 appended after the article name.
00037 --prefix=<text> : Do an explicit check for the given title prefix
00038 in place of the standard namespace list.
00039 --verbose : Display output for checked namespaces without conflicts
00040
00041 ENDS;
00042 die;
00043 }
00044
00045 class NamespaceConflictChecker {
00046 function NamespaceConflictChecker( $db, $verbose=false ) {
00047 $this->db = $db;
00048 $this->verbose = $verbose;
00049 }
00050
00051 function checkAll( $fix, $suffix = '' ) {
00052 global $wgContLang, $wgNamespaceAliases, $wgCanonicalNamespaceNames;
00053 global $wgCapitalLinks;
00054
00055 $spaces = array();
00056
00057
00058
00059 foreach( $this->getInterwikiList() as $prefix ) {
00060 $name = $wgContLang->ucfirst( $prefix );
00061 $spaces[$name] = 0;
00062 }
00063
00064
00065 foreach( $wgCanonicalNamespaceNames as $ns => $name ) {
00066
00067 if( $name !== '' ) {
00068 $spaces[$name] = $ns;
00069 }
00070 }
00071 foreach( $wgContLang->getNamespaces() as $ns => $name ) {
00072 if( $name !== '' ) {
00073 $spaces[$name] = $ns;
00074 }
00075 }
00076 foreach( $wgNamespaceAliases as $name => $ns ) {
00077 $spaces[$name] = $ns;
00078 }
00079 foreach( $wgContLang->namespaceAliases as $name => $ns ) {
00080 $spaces[$name] = $ns;
00081 }
00082
00083
00084
00085 foreach( $spaces as $name => $ns ) {
00086 $moreNames = array();
00087 $moreNames[] = $wgContLang->uc( $name );
00088 $moreNames[] = $wgContLang->ucfirst( $wgContLang->lc( $name ) );
00089 $moreNames[] = $wgContLang->ucwords( $name );
00090 $moreNames[] = $wgContLang->ucwords( $wgContLang->lc( $name ) );
00091 $moreNames[] = $wgContLang->ucwordbreaks( $name );
00092 $moreNames[] = $wgContLang->ucwordbreaks( $wgContLang->lc( $name ) );
00093 if( !$wgCapitalLinks ) {
00094 foreach( $moreNames as $altName ) {
00095 $moreNames[] = $wgContLang->lcfirst( $altName );
00096 }
00097 $moreNames[] = $wgContLang->lcfirst( $name );
00098 }
00099 foreach( array_unique( $moreNames ) as $altName ) {
00100 if( $altName !== $name ) {
00101 $spaces[$altName] = $ns;
00102 }
00103 }
00104 }
00105
00106 ksort( $spaces );
00107 asort( $spaces );
00108
00109 $ok = true;
00110 foreach( $spaces as $name => $ns ) {
00111 $ok = $this->checkNamespace( $ns, $name, $fix, $suffix ) && $ok;
00112 }
00113 return $ok;
00114 }
00115
00116 private function getInterwikiList() {
00117 $result = $this->db->select( 'interwiki', array( 'iw_prefix' ) );
00118 while( $row = $this->db->fetchObject( $result ) ) {
00119 $prefixes[] = $row->iw_prefix;
00120 }
00121 $this->db->freeResult( $result );
00122 return $prefixes;
00123 }
00124
00125 function checkNamespace( $ns, $name, $fix, $suffix = '' ) {
00126 if( $ns == 0 ) {
00127 $header = "Checking interwiki prefix: \"$name\"\n";
00128 } else {
00129 $header = "Checking namespace $ns: \"$name\"\n";
00130 }
00131
00132 $conflicts = $this->getConflicts( $ns, $name );
00133 $count = count( $conflicts );
00134 if( $count == 0 ) {
00135 if( $this->verbose ) {
00136 echo $header;
00137 echo "... no conflicts detected!\n";
00138 }
00139 return true;
00140 }
00141
00142 echo $header;
00143 echo "... $count conflicts detected:\n";
00144 $ok = true;
00145 foreach( $conflicts as $row ) {
00146 $resolvable = $this->reportConflict( $row, $suffix );
00147 $ok = $ok && $resolvable;
00148 if( $fix && ( $resolvable || $suffix != '' ) ) {
00149 $ok = $this->resolveConflict( $row, $resolvable, $suffix ) && $ok;
00150 }
00151 }
00152 return $ok;
00153 }
00154
00158 function checkPrefix( $key, $prefix, $fix, $suffix = '' ) {
00159 echo "Checking prefix \"$prefix\" vs namespace $key\n";
00160 return $this->checkNamespace( $key, $prefix, $fix, $suffix );
00161 }
00162
00163 function getConflicts( $ns, $name ) {
00164 $page = 'page';
00165 $table = $this->db->tableName( $page );
00166
00167 $prefix = $this->db->strencode( $name );
00168 $likeprefix = str_replace( '_', '\\_', $prefix);
00169 $encNamespace = $this->db->addQuotes( $ns );
00170
00171 $titleSql = "TRIM(LEADING '$prefix:' FROM {$page}_title)";
00172 if( $ns == 0 ) {
00173
00174 $titleSql = "CONCAT('$prefix-',$titleSql)";
00175 }
00176
00177 $sql = "SELECT {$page}_id AS id,
00178 {$page}_title AS oldtitle,
00179 $encNamespace AS namespace,
00180 $titleSql AS title
00181 FROM {$table}
00182 WHERE {$page}_namespace=0
00183 AND {$page}_title LIKE '$likeprefix:%'";
00184
00185 $result = $this->db->query( $sql, 'NamespaceConflictChecker::getConflicts' );
00186
00187 $set = array();
00188 while( $row = $this->db->fetchObject( $result ) ) {
00189 $set[] = $row;
00190 }
00191 $this->db->freeResult( $result );
00192
00193 return $set;
00194 }
00195
00196 function reportConflict( $row, $suffix ) {
00197 $newTitle = Title::makeTitleSafe( $row->namespace, $row->title );
00198 if( is_null($newTitle) || !$newTitle->canExist() ) {
00199
00200
00201 printf( "... %d (0,\"%s\")\n",
00202 $row->id,
00203 $row->oldtitle );
00204 echo "... *** cannot resolve automatically; illegal title ***\n";
00205 return false;
00206 }
00207
00208 printf( "... %d (0,\"%s\") -> (%d,\"%s\") [[%s]]\n",
00209 $row->id,
00210 $row->oldtitle,
00211 $newTitle->getNamespace(),
00212 $newTitle->getDBkey(),
00213 $newTitle->getPrefixedText() );
00214
00215 $id = $newTitle->getArticleId();
00216 if( $id ) {
00217 echo "... *** cannot resolve automatically; page exists with ID $id ***\n";
00218 return false;
00219 } else {
00220 return true;
00221 }
00222 }
00223
00224 function resolveConflict( $row, $resolvable, $suffix ) {
00225 if( !$resolvable ) {
00226 echo "... *** old title {$row->title}\n";
00227 while( true ) {
00228 $row->title .= $suffix;
00229 echo "... *** new title {$row->title}\n";
00230 $title = Title::makeTitleSafe( $row->namespace, $row->title );
00231 if ( ! $title ) {
00232 echo "... !!! invalid title\n";
00233 return false;
00234 }
00235 if ( $id = $title->getArticleId() ) {
00236 echo "... *** page exists with ID $id ***\n";
00237 } else {
00238 break;
00239 }
00240 }
00241 echo "... *** using suffixed form [[" . $title->getPrefixedText() . "]] ***\n";
00242 }
00243 $tables = array( 'page' );
00244 foreach( $tables as $table ) {
00245 $this->resolveConflictOn( $row, $table );
00246 }
00247 return true;
00248 }
00249
00250 function resolveConflictOn( $row, $table ) {
00251 echo "... resolving on $table... ";
00252 $newTitle = Title::makeTitleSafe( $row->namespace, $row->title );
00253 $this->db->update( $table,
00254 array(
00255 "{$table}_namespace" => $newTitle->getNamespace(),
00256 "{$table}_title" => $newTitle->getDBkey(),
00257 ),
00258 array(
00259 "{$table}_namespace" => 0,
00260 "{$table}_title" => $row->oldtitle,
00261 ),
00262 __METHOD__ );
00263 echo "ok.\n";
00264 return true;
00265 }
00266 }
00267
00268
00269
00270
00271 $wgTitle = Title::newFromText( 'Namespace title conflict cleanup script' );
00272
00273 $verbose = isset( $options['verbose'] );
00274 $fix = isset( $options['fix'] );
00275 $suffix = isset( $options['suffix'] ) ? $options['suffix'] : '';
00276 $prefix = isset( $options['prefix'] ) ? $options['prefix'] : '';
00277 $key = isset( $options['key'] ) ? intval( $options['key'] ) : 0;
00278
00279 $dbw = wfGetDB( DB_MASTER );
00280 $duper = new NamespaceConflictChecker( $dbw, $verbose );
00281
00282 if( $prefix ) {
00283 $retval = $duper->checkPrefix( $key, $prefix, $fix, $suffix );
00284 } else {
00285 $retval = $duper->checkAll( $fix, $suffix );
00286 }
00287
00288 if( $retval ) {
00289 echo "\nLooks good!\n";
00290 exit( 0 );
00291 } else {
00292 echo "\nOh noeees\n";
00293 exit( -1 );
00294 }
00295
00296