Index: includes/Category.php =================================================================== --- includes/Category.php (revision 92077) +++ includes/Category.php (working copy) @@ -306,4 +306,73 @@ return $ret; } + + /** + * Delete this category from the database if it has no members and no + * description page. + * + * @return bool True on successful delete, false on failed delete or + * no action taken + */ + public function deleteIfUnused() { + $title = $this->getTitle(); + + // Does the category description page exist? If so, skip. + if ( !is_null( $title ) + && $title->exists() ) + { + return false; + } + + // Are there any members of the category? If so, skip. + if ( $this->getPageCount() != 0 + || $this->getSubcatCount() != 0 + || $this->getFileCount() != 0 ) + { + return false; + } + + // Make sure the counts are correct, just in case we end up in a race condition + // Overhead shouldn't be significant because: + // 1) Not many categories will need to be rechecked to begin with + // 2) Those that are will either be empty or small + $this->refreshCounts(); + if ( $this->getPageCount() != 0 + || $this->getSubcatCount() != 0 + || $this->getFileCount() != 0 ) + { + return false; + } + + return $this->deleteFromTable(); + } + + + /** + * Delete this category from the database (by cat_id) + * + * @return bool True on success, false on failure + */ + public function deleteFromTable() { + if ( wfReadOnly() ) { + return false; + } + + // It would be great if we knew what category we were deleting + if ( $this->mID === null ) { + // Make sure it's still there + if ( !$this->initialize() ) { + return false; + } + } + + $dbw = wfGetDB( DB_MASTER ); + $dbw->begin(); + + // Delete. + $ret = $dbw->delete( 'category', array( 'cat_id' => $this->mID ), __METHOD__ ); + $dbw->commit(); + + return $ret; + } } Index: maintenance/cleanupCategories.php =================================================================== --- maintenance/cleanupCategories.php (revision 0) +++ maintenance/cleanupCategories.php (revision 0) @@ -0,0 +1,101 @@ + + * http://www.mediawiki.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @author Dan Collins + * @ingroup Maintenance + */ + +require_once( dirname( __FILE__ ) . '/cleanupTable.inc' ); + +class CategoryCleanup extends TableCleanup { + protected $defaultParams = array( + 'table' => 'category', + 'conds' => array(), + 'index' => 'cat_id', + 'callback' => 'processRow', + ); + + public function __construct() { + parent::__construct(); + $this->mDescription = "Script to clean up categories with no members"; + } + + protected function processRow( $row ) { + global $wgContLang; + + // Create the necessary objects + $category = Category::newFromName( $row->cat_title ); + $title = $category->getTitle(); + + // Does the category description page exist? If so, skip. + if ( !is_null( $title ) + && $title->exists() ) + { + $this->output( "DEBUG: $row->cat_title has a description page\n" ); + return $this->progress( 0 ); // all is fine + } + + // Are there any members of the category? If so, skip. + if ( $row->cat_pages != 0 + || $row->cat_subcats != 0 + || $row->cat_files != 0 ) + { + $this->output( "DEBUG: $row->cat_title has members:\n" ); + $this->output( "DEBUG: $row->cat_pages pages, $row->cat_subcats subcats, $row->cat_files files\n" ); + return $this->progress( 0 ); // all is fine + } + + // Make sure the counts are correct, just in case we end up in a race condition + // Overhead shouldn't be significant because: + // 1) Not many categories will need to be rechecked to begin with + // 2) Those that are will either be empty or small + $category->refreshCounts(); + if ( $category->getPageCount() != 0 + || $category->getSubcatCount() != 0 + || $category->getFileCount() != 0 ) + { + $this->output( "DEBUG: $row->cat_title has members after refresh\n" ); + return $this->progress( 0 ); + } + + $this->output( "category $row->cat_title has no members or description page\n" ); + $this->removeCategory( $row, $category ); + return $this->progress( 1 ); + } + + protected function removeCategory( $row, $category ) { + if ( $this->dryrun ) { + $this->output( "DRY RUN: would remove $row->cat_id ($row->cat_title)\n" ); + } else { + $this->output( "Removing $row->cat_id ($row->cat_title)\n" ); + $category->deleteFromTable(); + } + } +} + +$maintClass = "CategoryCleanup"; +require_once( RUN_MAINTENANCE_IF_MAIN );