Extension:DeletePagePermanently

This extension can delete pages in every namespace configured without leaving any trace in the database. This extension will automatically delete talk pages and subpages, if any.

The extension DeletePagePermanently adds a new tab that allows you to delete a page permanently from the database. '''Although version 2.0 is safer than version 1.0 it can still harm your wiki, especially when used wrong. Test the extension carefully and backup your database as the extension is still in beta status.'''

Since version 2.1 it is possible to use the function from other PHP files.

Although this extension deletes the image page, the image will remain in the image folder, undeleted

Installation
The extension was designed for MediaWiki 1.13 but it should be compatible with later version as long as the database layout is the same as MediaWiki 1.13.0 for the tables.

As the Table structure of MediaWiki 1.14 is similar to 1.13 it also works here, I tested and did not figure out any problems.

Download
Please cut and paste the code found below and place it in:

and

Note: $IP stands for the root directory of your MediaWiki installation, the same directory that holds the LocalSettings.php file.

Install
To install this extension, add the following to LocalSettings.php:

User rights
You can configure the user rights in LocalSettings.php:

Defaults from DeletePagePermanently.php:

The extension introduces a new user rights deleteperm.

Namespaces
Since version 2.0 is the possibility to configure the namespaces in which pages can be deleted. To configure this, add some lines to your LocalSettings.php:

Built-in namespaces are described on meta-wiki.

DeletePagePermanently.php
For MediaWiki 1.16.x  see below.

"); 					return false;		}		# Perform actual deletion		elseif ($action == 'delete_permanently' )		{			$ns = $wgArticle->mTitle->getNamespace;			$t   = $wgArticle->mTitle->getDBkey;			$id  = $wgArticle->mTitle->getArticleID;			if ( $t == '' || $id == 0 || $wgDeletePagePermanentlyNamespaces[$ns] != true || $ns == NS_SPECIAL)			{				$wgOut->addHTML(wfMsgHtml('del_impossible')); 				return false;			}			#delete the page and the talk page too			$this->deletePermanently($wgArticle->mTitle);			$this->deletePermanentlyTalkPage($wgArticle->mTitle);			# delete subpages and associeted talk pages			if($wgArticle->mTitle->hasSubpages)			{				$subPagesList = $wgArticle->mTitle->getSubpages;				if($subPagesList != array)				{					foreach( $subPagesList as $subpage )					{						$this->deletePermanently($subpage);						$this->deletePermanentlyTalkPage($subpage);					}				}			}			$wgOut->addHTML(wfMsgHtml('del_done')); return false; }		//$wgOut->addHTML(wfMsgHtml('del_not_done')); return true; }	function deletePermanently($title){ global $wgOut; $ns  = $title->getNamespace; $t   = $title->getDBkey; $id  = $title->getArticleID; $cats = $title->getParentCategories; $dbw = wfGetDB( DB_MASTER ); $dbw->begin; ####		## First delete entries, which are in direct relation with the page: ####		# delete redirect... $dbw->delete( 'redirect', array( 'rd_from' => $id ), __METHOD__); # delete external link... $dbw->delete( 'externallinks', array( 'el_from' => $id ), __METHOD__); # delete language link... $dbw->delete( 'langlinks', array( 'll_from' => $id ), __METHOD__); # delete search index... $dbw->delete( 'searchindex', array( 'si_page' => $id ), __METHOD__); # Delete restrictions for the page $dbw->delete( 'page_restrictions', array ( 'pr_page' => $id ), __METHOD__ ); # Delete page Links $dbw->delete( 'pagelinks', array ( 'pl_from' => $id ), __METHOD__ ); # delete category links $dbw->delete( 'categorylinks', array( 'cl_from' => $id ), __METHOD__); # delete template links $dbw->delete( 'templatelinks', array( 'tl_from' => $id ), __METHOD__); # read text entries for all revisions and delete them. $res = $dbw->select ('revision', 'rev_text_id', "rev_page=$id"); while( $row = $dbw->fetchObject($res) ) {			$value = $row->rev_text_id; $dbw->delete('text', array('old_id'=>$value), __METHOD__); }		# In the table 'revision' : Delete all the revision of the page where 'rev_page' = $id $dbw->delete('revision', array('rev_page'=>$id), __METHOD__); # delete image links $dbw->delete( 'imagelinks', array( 'il_from' => $id ), __METHOD__); ####		## then delete entries which are not in direct relation with the page: ####		# Clean up recentchanges entries... $dbw->delete( 'recentchanges', array( 'rc_namespace' => $ns, 'rc_title' => $t ), __METHOD__ ); # read text entries for all archived pages and delete them. $res = $dbw->select ('archive', 'ar_text_id', array( 'ar_namespace' => $ns, 'ar_title' => $t )); while( $row = $dbw->fetchObject($res) ) {			$value = $row->ar_text_id; $dbw->delete('text', array('old_id'=>$value), __METHOD__); }		# Clean archive entries... $dbw->delete( 'archive', array( 'ar_namespace' => $ns, 'ar_title' => $t ), __METHOD__ ); # Clean up log entries... $dbw->delete( 'logging', array( 'log_namespace' => $ns, 'log_title' => $t ), __METHOD__ ); # Clean up watchlist... $dbw->delete( 'watchlist', array( 'wl_namespace' => $ns, 'wl_title' => $t ), __METHOD__ ); # In the table 'page' : Delete the page entry $dbw->delete( 'page', array( 'page_id' => $id ), __METHOD__); ####		## If the article belongs to a category, update category counts ####		if(!empty($cats)) {						foreach($cats as $parentcat => $currentarticle) {				$catname = split(':', $parentcat, 2); $cat = Category::newFromName($catname[1]); $cat->refreshCounts; }		}		####		## If an image is beeing deleted, some extra work needs to be done ####		if ($ns == NS_IMAGE) {			$file = wfFindFile($t); if ($file) {				# Get all filenames of old versions: $fields = OldLocalFile::selectFields; $res = $dbw->select ('oldimage', $fields, array( 'oi_name' => $t )); while( $row = $dbw->fetchObject($res) ) {					$oldLocalFile = OldLocalFile::newFromRow($row, $file->repo); $path = $oldLocalFile->getArchivePath.'/'.$oldLocalFile->getArchiveName; try {						# Using the FileStore to delete the file $transaction = FileStore::deleteFile( $path ); $transaction->commit; }					catch (Exception $e) {						$wgOut->addHTML($e->getMessage); }				}				$path = $file->getPath; try {					# Using the FileStore to delete the file itself $transaction = FileStore::deleteFile( $path ); $transaction->commit; }				catch (Exception $e) {					$wgOut->addHTML($e->getMessage); }			}			# clean the filearchive for the given filename: $fa_archive_name = array; $res = $dbw->select ('filearchive', 'fa_storage_key', array( 'fa_name' => $t )); while( $row = $dbw->fetchObject($res) ) {				$key = $row->fa_storage_key; # Using the FileStore to delete the file $store = FileStore::get( 'deleted' ); $transaction = $store->delete($key); $transaction->commit; }			# Delete old db entries of the image: $dbw->delete( 'oldimage', array( 'oi_name' => $t ), __METHOD__ ); # Delete archive entries of the image: $dbw->delete( 'filearchive', array( 'fa_name' => $t ), __METHOD__ ); # Delete image entry: $dbw->delete( 'image', array( 'img_name' => $t ), __METHOD__ ); $dbw->commit; $linkCache = LinkCache::singleton; $linkCache->clear; }	}	function deletePermanentlyTalkPage($title) {		global $wgDeletePagePermanentlyNamespaces; $talkPage = $title->getTalkPage; $id      = $title->getArticleID; $ns      = $title->getNamespace; if ( $id != 0 && $wgDeletePagePermanentlyNamespaces[$ns] == true ) {			$this->deletePermanently($talkPage); }	} }

MediaWiki 1.16.x
If you are using MediaWiki 1.16.x, please use the following code as DeletePagePermanently.php

");			return false;		}		# Perform actual deletion		elseif ($action == 'delete_permanently') {			$ns = $wgArticle->mTitle->getNamespace;			$t = $wgArticle->mTitle->getDBkey;			$id = $wgArticle->mTitle->getArticleID;

if ($t == '' || $id == 0 || $wgDeletePagePermanentlyNamespaces[$ns] != true || $ns == NS_SPECIAL) { $wgOut->addHTML(wfMsgHtml('del_impossible')); return false; }

$this->deletePermanently($wgArticle->mTitle); $wgOut->addHTML(wfMsgHtml('del_done')); return false; }		//$wgOut->addHTML(wfMsgHtml('del_not_done')); return true; }

function deletePermanently($title) { global $wgOut;

$ns = $title->getNamespace; $t = $title->getDBkey; $id = $title->getArticleID; $cats = $title->getParentCategories;

$dbw = wfGetDB(DB_MASTER);

$dbw->begin;

####		## First delete entries, which are in direct relation with the page: ####

# delete redirect... $dbw->delete('redirect', array ('rd_from' => $id), __METHOD__);

# delete external link... $dbw->delete('externallinks', array ('el_from' => $id), __METHOD__);

# delete language link... $dbw->delete('langlinks', array ('ll_from' => $id), __METHOD__);

# delete search index... $dbw->delete('searchindex', array ('si_page' => $id), __METHOD__);

# Delete restrictions for the page $dbw->delete('page_restrictions', array ('pr_page' => $id), __METHOD__);

# Delete page Links $dbw->delete('pagelinks', array ('pl_from' => $id), __METHOD__);

# delete category links $dbw->delete('categorylinks', array ('cl_from' => $id), __METHOD__);

# delete template links $dbw->delete('templatelinks', array ('tl_from' => $id), __METHOD__);

# read text entries for all revisions and delete them. $res = $dbw->select('revision', 'rev_text_id', "rev_page=$id");

while ($row = $dbw->fetchObject($res)) { $value = $row->rev_text_id; $dbw->delete('text', array ('old_id' => $value), __METHOD__); }

# In the table 'revision' : Delete all the revision of the page where 'rev_page' = $id $dbw->delete('revision', array ('rev_page' => $id), __METHOD__);

# delete image links $dbw->delete('imagelinks', array ('il_from' => $id), __METHOD__);

####		## then delete entries which are not in direct relation with the page: ####

# Clean up recentchanges entries... $dbw->delete('recentchanges', array ( 'rc_namespace' => $ns, 'rc_title' => $t ), __METHOD__);

# read text entries for all archived pages and delete them. $res = $dbw->select('archive', 'ar_text_id', array ( 'ar_namespace' => $ns, 'ar_title' => $t ));

while ($row = $dbw->fetchObject($res)) { $value = $row->ar_text_id; $dbw->delete('text', array ('old_id' => $value), __METHOD__); }

# Clean archive entries... $dbw->delete('archive', array ( 'ar_namespace' => $ns, 'ar_title' => $t ), __METHOD__);

# Clean up log entries... $dbw->delete('logging', array ( 'log_namespace' => $ns, 'log_title' => $t ), __METHOD__);

# Clean up watchlist... $dbw->delete('watchlist', array ( 'wl_namespace' => $ns, 'wl_title' => $t ), __METHOD__);

# In the table 'page' : Delete the page entry $dbw->delete('page', array ('page_id' => $id), __METHOD__);

####		## If the article belongs to a category, update category counts ####		if (!empty($cats)) { foreach ($cats as $parentcat => $currentarticle) { $catname = split(':', $parentcat, 2); $cat = Category::newFromName($catname[1]); $cat->refreshCounts; }		}

####		## If an image is beeing deleted, some extra work needs to be done ####		if ($ns == NS_IMAGE) {

$file = wfFindFile($t);

if ($file) { # Get all filenames of old versions: $fields = OldLocalFile::selectFields; $res = $dbw->select('oldimage', $fields, array ('oi_name' => $t));

while ($row = $dbw->fetchObject($res)) { $oldLocalFile = OldLocalFile::newFromRow($row, $file->repo); $path = $oldLocalFile->getArchivePath. '/' . $oldLocalFile->getArchiveName;

try { @unlink($path); } catch (Exception $e) { $wgOut->addHTML($e->getMessage); } }

$path = $file->getPath;

try { $file->purgeThumbnails; @unlink($path); } catch (Exception $e) { $wgOut->addHTML($e->getMessage); } }

# clean the filearchive for the given filename: $dbw->delete('filearchive', array ('fa_name' => $t), __METHOD__);

# Delete old db entries of the image: $dbw->delete('oldimage', array ('oi_name' => $t), __METHOD__);

# Delete archive entries of the image: $dbw->delete('filearchive', array ('fa_name' => $t), __METHOD__);

# Delete image entry: $dbw->delete('image', array ('img_name' => $t), __METHOD__);

$dbw->commit;

$linkCache = LinkCache::singleton; $linkCache->clear; }	} }

History
The original Version of this extension was created by Ludovic MOUTON. The original Version can be found at Extension:DeletePagePermanently/1.0