Difference between revisions of "Extension:SimpleSecurity"
(Fetch hook working properly) |
|||
| Line 19: | Line 19: | ||
if (!defined('MEDIAWIKI')) die('Not an entry point.'); | if (!defined('MEDIAWIKI')) die('Not an entry point.'); | ||
| − | define('SIMPLESECURITY_VERSION', '4.1. | + | define('SIMPLESECURITY_VERSION', '4.1.1, 2008-07-21'); |
# Global security settings | # Global security settings | ||
| Line 25: | Line 25: | ||
$wgSecurityMagicGroup = "ifgroup"; # the name for doing a group-based conditional | $wgSecurityMagicGroup = "ifgroup"; # the name for doing a group-based conditional | ||
$wgSecurityLogActions = array('edit', 'download'); # Actions that should be logged | $wgSecurityLogActions = array('edit', 'download'); # Actions that should be logged | ||
| − | $wgSecurityUseDBHook = true; # | + | $wgSecurityUseDBHook = true; # Use the DatabaseFetchHook to validate database access |
$wgSecurityAllowUser = false; # Allow restrictions based on user not just group | $wgSecurityAllowUser = false; # Allow restrictions based on user not just group | ||
| Line 34: | Line 34: | ||
'history' => 'History' | 'history' => 'History' | ||
); | ); | ||
| + | $wgSecurityExtraActions = array('read' => 'Read'); | ||
# Extra groups available in protection form | # Extra groups available in protection form | ||
| Line 42: | Line 43: | ||
$wgHooks['LanguageGetMagic'][] = 'wfSimpleSecurityLanguageGetMagic'; | $wgHooks['LanguageGetMagic'][] = 'wfSimpleSecurityLanguageGetMagic'; | ||
$wgExtensionCredits['parserhook'][] = array( | $wgExtensionCredits['parserhook'][] = array( | ||
| − | 'name' => " | + | 'name' => "SimpleSecurity", |
'author' => '[http://www.organicdesign.co.nz/User:Nad User:Nad]', | 'author' => '[http://www.organicdesign.co.nz/User:Nad User:Nad]', | ||
'description' => 'Extends the MediaWiki article protection to allow restricting viewing of article content', | 'description' => 'Extends the MediaWiki article protection to allow restricting viewing of article content', | ||
| Line 50: | Line 51: | ||
class SimpleSecurity { | class SimpleSecurity { | ||
| + | |||
| + | var $enabled = true; | ||
/** | /** | ||
| Line 57: | Line 60: | ||
global $wgParser, $wgHooks, $wgLogTypes, $wgLogNames, $wgLogHeaders, $wgLogActions, $wgMessageCache, | global $wgParser, $wgHooks, $wgLogTypes, $wgLogNames, $wgLogHeaders, $wgLogActions, $wgMessageCache, | ||
$wgSecurityMagicIf, $wgSecurityMagicGroup, $wgSecurityExtraActions, $wgSecurityExtraGroups, | $wgSecurityMagicIf, $wgSecurityMagicGroup, $wgSecurityExtraActions, $wgSecurityExtraGroups, | ||
| − | $wgRestrictionTypes, $wgRestrictionLevels; | + | $wgRestrictionTypes, $wgRestrictionLevels, $wgGroupPermissions; |
| + | |||
| + | # $wgGroupPermissions has to have its default read entry removed because Title::userCanRead checks it directly | ||
| + | if ($this->default_read = isset($wgGroupPermissions['*']['read']) && $wgGroupPermissions['*']['read']) | ||
| + | $wgGroupPermissions['*']['read'] = false; | ||
# Add our parser-hooks | # Add our parser-hooks | ||
| Line 72: | Line 79: | ||
# Extend protection form groups, actions and messages | # Extend protection form groups, actions and messages | ||
$wgMessageCache->addMessages(array( 'protect-unchain' => "Modify actions individually" )); | $wgMessageCache->addMessages(array( 'protect-unchain' => "Modify actions individually" )); | ||
| + | #$wgMessageCache->addMessages(array('loginreqpagetext' => "Sorry, you'll need to $1 to an account with sufficient permissions to view this page.")); | ||
foreach ($wgSecurityExtraActions as $k => $v) { | foreach ($wgSecurityExtraActions as $k => $v) { | ||
if (empty($v)) $v = ucfirst($k); | if (empty($v)) $v = ucfirst($k); | ||
$wgRestrictionTypes[] = $k; | $wgRestrictionTypes[] = $k; | ||
$wgMessageCache->addMessages(array( "restriction-$k" => $v )); | $wgMessageCache->addMessages(array( "restriction-$k" => $v )); | ||
| + | #$wgGroupPermissions['sysop'][$k] = true; # Ensure sysops have the right to perform this extra action | ||
} | } | ||
| + | |||
foreach ($wgSecurityExtraGroups as $k => $v) { | foreach ($wgSecurityExtraGroups as $k => $v) { | ||
if (empty($v)) $v = ucfirst($k); | if (empty($v)) $v = ucfirst($k); | ||
$wgRestrictionLevels[] = $k; | $wgRestrictionLevels[] = $k; | ||
$wgMessageCache->addMessages(array( "protect-level-$k" => $v )); | $wgMessageCache->addMessages(array( "protect-level-$k" => $v )); | ||
| + | $wgGroupPermissions[$k]['not an action'] = true; # Ensure the new groups show up in rights management | ||
} | } | ||
} | } | ||
| Line 101: | Line 112: | ||
} | } | ||
| − | + | /** | |
| − | + | * User::getRights returns a list of rights (allowed actions) based on the current users group membership | |
| − | + | * Title::getRestrictions returns a list of groups who can perform a particular action | |
| − | public function onUserGetRights(&$user, &$rights) { | + | * So getRights should filter out any title-based restriction's actions which require groups that the user is not a member of |
| − | global $ | + | * Allows sysop access |
| − | if (is_object($wgTitle)) { | + | */ |
| − | + | public function onUserGetRights(&$user, &$rights, &$title = NULL) { | |
| − | + | global $wgGroupPermissions, $wgTitle, $wgRequest; | |
| − | + | if (!is_object($title)) $title = $wgTitle; | |
| − | + | $groups = $user->getEffectiveGroups(); | |
| − | + | ||
| − | + | # If current title is valid and we're not a sysop, process permissions | |
| + | if (is_object($title) && !in_array('sysop', $groups)) { | ||
| + | |||
| + | # Get list of groups required to read this title (empty list means no read restriction) | ||
| + | $ns = $title->getNamespace(); | ||
| + | if ($ns == NS_SPECIAL) { | ||
| + | |||
| + | # Ugly hack to prevent specialpage operations on unreadable pages | ||
| + | list($name, $par) = explode('/', $title->getDBkey(), 2); | ||
| + | if ($par) $t = Title::newFromText($par); | ||
| + | elseif ($wgRequest->getVal('target')) $t = Title::newFromText($wgRequest->getVal('target')); | ||
| + | elseif ($wgRequest->getVal('oldtitle')) $t = Title::newFromText($wgRequest->getVal('oldtitle')); | ||
| + | if (is_object($t)) $restrictions = $t->getRestrictions('read'); | ||
} | } | ||
| + | else $restrictions = $title->getRestrictions('read'); | ||
| + | |||
| + | # If there are read restrictions in place, then check if we're a member of any groups required for read access | ||
| + | if (count($restrictions) && count(array_intersect($restrictions, $groups)) < 1) | ||
| + | foreach ($rights as $i => $right) if ($right == 'read' || $right == 'move') unset($rights[$i]); | ||
| + | elseif ($this->default_read) $wgGroupPermissions['*']['read'] = $this->default_read; | ||
} | } | ||
return true; | return true; | ||
| − | + | } | |
| − | + | ||
| − | + | /** | |
| − | + | * Patches SQL queries to ensure that the old_id field is present in all requests for the old_text field so permissions can be validated | |
| − | static function | + | */ |
| − | + | static function patchSQL($match) { | |
| − | + | if (!preg_match("/old_text/", $match[0])) return $match[0]; | |
| − | + | $fields = str_replace(" ", "", $match[0]); | |
| − | + | return ($fields == "*" || preg_match("/old_id/", $fields)) ? $fields : "$fields,old_id"; | |
| − | + | } | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
/** | /** | ||
* Validate the passed database row and replace any invalid content | * Validate the passed database row and replace any invalid content | ||
| − | * - | + | * - called from DatabaseFetchHook whenever a row contains old_text |
| + | * - old_id is guaranteed to exist due to patchSQL method | ||
*/ | */ | ||
| − | + | static function validateRow(&$row) { | |
| − | # | + | global $wgUser, $wgSimpleSecurity; |
| + | $wgSimpleSecurity->enabled = false; | ||
| + | |||
| + | $groups = $wgUser->getEffectiveGroups(); | ||
| + | if (in_array('sysop', $groups)) return; | ||
| + | |||
| + | # Obtain a title object from the old_id | ||
| + | $dbr =& wfGetDB(DB_SLAVE); | ||
| + | $tbl = $dbr->tableName('revision'); | ||
| + | $rev = $dbr->selectRow($tbl, 'rev_page', "rev_text_id = {$row->old_id}", __METHOD__); | ||
| + | $title = Title::newFromID($rev->rev_page); | ||
| + | |||
| + | # Validate title | ||
| + | if (is_object($title) && !in_array('sysop', $groups)) { | ||
| + | $restrictions = $title->getRestrictions('read'); | ||
| + | if (count($restrictions) && count(array_intersect($restrictions, $groups)) < 1) | ||
| + | $row->old_text = wfMsg('badaccess-read'); | ||
| + | } | ||
| + | |||
| + | $wgSimpleSecurity->enabled = true; | ||
} | } | ||
| Line 150: | Line 194: | ||
/** | /** | ||
* Hooks into Database::query and Database::fetchObject via the LoadBalancer class | * Hooks into Database::query and Database::fetchObject via the LoadBalancer class | ||
| + | * - this is a global because PHP doesn't like nested class definitions | ||
*/ | */ | ||
| − | function | + | function wfAddDatabaseHooks() { |
global $wgLoadBalancer, $wgDBtype; | global $wgLoadBalancer, $wgDBtype; | ||
| Line 164: | Line 209: | ||
$type = ucfirst($wgDBtype); | $type = ucfirst($wgDBtype); | ||
eval("class Database{$type}2 extends Database{$type}".' { | eval("class Database{$type}2 extends Database{$type}".' { | ||
| − | |||
public function query($sql, $fname = "", $tempIgnore = false) { | public function query($sql, $fname = "", $tempIgnore = false) { | ||
global $wgSimpleSecurity; | global $wgSimpleSecurity; | ||
$count = false; | $count = false; | ||
| − | $patched = preg_replace_callback("/(?<=SELECT ).+?(?= FROM)/", " | + | if ($wgSimpleSecurity->enabled) |
| + | $patched = preg_replace_callback("/(?<=SELECT ).+?(?= FROM)/", "SimpleSecurity::patchSQL", $sql, 1, $count); | ||
return parent::query($count ? $patched : $sql, $fname, $tempIgnore); | return parent::query($count ? $patched : $sql, $fname, $tempIgnore); | ||
| − | + | } | |
| − | |||
function fetchObject(&$res) { | function fetchObject(&$res) { | ||
global $wgSimpleSecurity; | global $wgSimpleSecurity; | ||
$row = parent::fetchObject($res); | $row = parent::fetchObject($res); | ||
| − | $wgSimpleSecurity-> | + | if ($wgSimpleSecurity->enabled && isset($row->old_text)) SimpleSecurity::validateRow($row); |
return $row; | return $row; | ||
| − | + | } | |
| − | + | }'); | |
| − | |||
# Create a replica of the LoadBalancer class which uses the new Database subclass for its connection objects | # Create a replica of the LoadBalancer class which uses the new Database subclass for its connection objects | ||
| Line 195: | Line 238: | ||
$wgLoadBalancer = new LoadBalancer2($oldLoadBalancer->mServers); | $wgLoadBalancer = new LoadBalancer2($oldLoadBalancer->mServers); | ||
foreach (array_keys(get_class_vars('LoadBalancer')) as $k) $wgLoadBalancer->$k = $oldLoadBalancer->$k; | foreach (array_keys(get_class_vars('LoadBalancer')) as $k) $wgLoadBalancer->$k = $oldLoadBalancer->$k; | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
} | } | ||
| Line 211: | Line 244: | ||
*/ | */ | ||
function wfSetupSimpleSecurity() { | function wfSetupSimpleSecurity() { | ||
| − | global $wgSimpleSecurity, $wgLanguageCode, $wgMessageCache | + | global $wgSimpleSecurity, $wgLanguageCode, $wgMessageCache, $wgSecurityUseDBHook; |
# Hooks into Database::query and Database::fetchObject via the LoadBalancer class | # Hooks into Database::query and Database::fetchObject via the LoadBalancer class | ||
| − | if ($wgSecurityUseDBHook) | + | if ($wgSecurityUseDBHook) wfAddDatabaseHooks(); |
# Instantiate the SimpleSecurity singleton now that the environment is prepared | # Instantiate the SimpleSecurity singleton now that the environment is prepared | ||
| Line 227: | Line 260: | ||
'securitylogentry' => "", | 'securitylogentry' => "", | ||
'securityinfotoggle' => "This article exhibits security restrictions. Click this icon for more detail.", | 'securityinfotoggle' => "This article exhibits security restrictions. Click this icon for more detail.", | ||
| − | ' | + | 'badaccess-read' => "!!Permission denied!!", |
| − | ' | + | 'badaccess-special' => "You do not have permission to access or perform special page actions on '''$1'''" |
| − | + | )); | |
| − | |||
} | } | ||
} | } | ||
| Line 238: | Line 270: | ||
*/ | */ | ||
function wfSimpleSecurityLanguageGetMagic(&$magicWords, $langCode = 0) { | function wfSimpleSecurityLanguageGetMagic(&$magicWords, $langCode = 0) { | ||
| − | global $wgSecurityMagicIf,$wgSecurityMagicGroup; | + | global $wgSecurityMagicIf, $wgSecurityMagicGroup; |
$magicWords[$wgSecurityMagicIf] = array($langCode, $wgSecurityMagicIf); | $magicWords[$wgSecurityMagicIf] = array($langCode, $wgSecurityMagicIf); | ||
$magicWords[$wgSecurityMagicGroup] = array($langCode, $wgSecurityMagicGroup); | $magicWords[$wgSecurityMagicGroup] = array($langCode, $wgSecurityMagicGroup); | ||
return true; | return true; | ||
} | } | ||
Revision as of 05:53, 21 July 2008
<?php /**
* Simple Security extensionTemplate:Php
* - Extends the MediaWiki article protection to allow restricting viewing of article content * - Also adds #ifusercan and #ifgroup parser functions for rendering restriction-based content * * See http://www.mediawiki.org/Extension:Simple_Security for installation and usage details * See http://www.organicdesign.co.nz/Extension_talk:SimpleSecurity4.php for development notes and disucssion * Version 4.0.0 started 2007-10-11 * Version 4.1.0 started 2008-06-12 (development funded for a slimmed down functional version) * * @package MediaWiki * @subpackage Extensions * @author Aran Dunkley User:Nad * @copyright © 2007 Aran Dunkley * @licence GNU General Public Licence 2.0 or later */
if (!defined('MEDIAWIKI')) die('Not an entry point.');
define('SIMPLESECURITY_VERSION', '4.1.1, 2008-07-21');
- Global security settings
$wgSecurityMagicIf = "ifusercan"; # the name for doing a permission-based conditional $wgSecurityMagicGroup = "ifgroup"; # the name for doing a group-based conditional $wgSecurityLogActions = array('edit', 'download'); # Actions that should be logged $wgSecurityUseDBHook = true; # Use the DatabaseFetchHook to validate database access $wgSecurityAllowUser = false; # Allow restrictions based on user not just group
- Extra actions to allow control over in protection form
$wgSecurityExtraActions = array( 'read' => 'Read', 'source' => 'Source', 'history' => 'History' ); $wgSecurityExtraActions = array('read' => 'Read');
- Extra groups available in protection form
$wgSecurityExtraGroups = array();
array_unshift($wgExtensionFunctions, 'wfSetupSimpleSecurity'); # Put SimpleSecurity's setup function before all others
$wgHooks['LanguageGetMagic'][] = 'wfSimpleSecurityLanguageGetMagic'; $wgExtensionCredits['parserhook'][] = array( 'name' => "SimpleSecurity", 'author' => 'User:Nad', 'description' => 'Extends the MediaWiki article protection to allow restricting viewing of article content', 'url' => 'http://www.mediawiki.org/wiki/Extension:Simple_Security', 'version' => SIMPLESECURITY_VERSION );
class SimpleSecurity {
var $enabled = true;
/** * Constructor */ function __construct() { global $wgParser, $wgHooks, $wgLogTypes, $wgLogNames, $wgLogHeaders, $wgLogActions, $wgMessageCache, $wgSecurityMagicIf, $wgSecurityMagicGroup, $wgSecurityExtraActions, $wgSecurityExtraGroups, $wgRestrictionTypes, $wgRestrictionLevels, $wgGroupPermissions;
# $wgGroupPermissions has to have its default read entry removed because Title::userCanRead checks it directly if ($this->default_read = isset($wgGroupPermissions['*']['read']) && $wgGroupPermissions['*']['read']) $wgGroupPermissions['*']['read'] = false;
# Add our parser-hooks $wgParser->setFunctionHook($wgSecurityMagicIf, array($this, 'ifUserCan')); $wgParser->setFunctionHook($wgSecurityMagicGroup, array($this, 'ifGroup')); $wgHooks['UserGetRights'][] = $this;
# Add a new log type $wgLogTypes[] = 'security'; $wgLogNames ['security'] = 'securitylogpage'; $wgLogHeaders['security'] = 'securitylogpagetext'; $wgLogActions['security/deny'] = 'securitylogentry';
# Extend protection form groups, actions and messages $wgMessageCache->addMessages(array( 'protect-unchain' => "Modify actions individually" )); #$wgMessageCache->addMessages(array('loginreqpagetext' => "Sorry, you'll need to $1 to an account with sufficient permissions to view this page.")); foreach ($wgSecurityExtraActions as $k => $v) { if (empty($v)) $v = ucfirst($k); $wgRestrictionTypes[] = $k; $wgMessageCache->addMessages(array( "restriction-$k" => $v )); #$wgGroupPermissions['sysop'][$k] = true; # Ensure sysops have the right to perform this extra action }
foreach ($wgSecurityExtraGroups as $k => $v) { if (empty($v)) $v = ucfirst($k); $wgRestrictionLevels[] = $k; $wgMessageCache->addMessages(array( "protect-level-$k" => $v )); $wgGroupPermissions[$k]['not an action'] = true; # Ensure the new groups show up in rights management } }
/** * Process the ifUserCan conditional security directive */ public function ifUserCan(&$parser, $action, $title, $then, $else = ) { return $title->userCan($action) ? $then : $else; }
/** * Process the ifGroup conditional security directive * - evaluates to true if current uset belongs to any of the comma-separated users and/or groups in the first parameter */ public function ifGroup(&$parser, $groups, $then, $else = ) { global $wgUser; $intersection = array_intersect(array_map('strtolower', split(',', $groups)), $wgUser->getEffectiveGroups()); return count($intersection) > 0 ? $then : $else; }
/** * User::getRights returns a list of rights (allowed actions) based on the current users group membership * Title::getRestrictions returns a list of groups who can perform a particular action * So getRights should filter out any title-based restriction's actions which require groups that the user is not a member of * Allows sysop access */ public function onUserGetRights(&$user, &$rights, &$title = NULL) { global $wgGroupPermissions, $wgTitle, $wgRequest; if (!is_object($title)) $title = $wgTitle; $groups = $user->getEffectiveGroups();
# If current title is valid and we're not a sysop, process permissions if (is_object($title) && !in_array('sysop', $groups)) {
# Get list of groups required to read this title (empty list means no read restriction) $ns = $title->getNamespace(); if ($ns == NS_SPECIAL) {
# Ugly hack to prevent specialpage operations on unreadable pages list($name, $par) = explode('/', $title->getDBkey(), 2); if ($par) $t = Title::newFromText($par); elseif ($wgRequest->getVal('target')) $t = Title::newFromText($wgRequest->getVal('target')); elseif ($wgRequest->getVal('oldtitle')) $t = Title::newFromText($wgRequest->getVal('oldtitle')); if (is_object($t)) $restrictions = $t->getRestrictions('read'); } else $restrictions = $title->getRestrictions('read');
# If there are read restrictions in place, then check if we're a member of any groups required for read access if (count($restrictions) && count(array_intersect($restrictions, $groups)) < 1) foreach ($rights as $i => $right) if ($right == 'read' || $right == 'move') unset($rights[$i]); elseif ($this->default_read) $wgGroupPermissions['*']['read'] = $this->default_read; } return true; }
/** * Patches SQL queries to ensure that the old_id field is present in all requests for the old_text field so permissions can be validated */ static function patchSQL($match) { if (!preg_match("/old_text/", $match[0])) return $match[0]; $fields = str_replace(" ", "", $match[0]); return ($fields == "*" || preg_match("/old_id/", $fields)) ? $fields : "$fields,old_id"; }
/** * Validate the passed database row and replace any invalid content * - called from DatabaseFetchHook whenever a row contains old_text * - old_id is guaranteed to exist due to patchSQL method */ static function validateRow(&$row) { global $wgUser, $wgSimpleSecurity; $wgSimpleSecurity->enabled = false;
$groups = $wgUser->getEffectiveGroups(); if (in_array('sysop', $groups)) return;
# Obtain a title object from the old_id $dbr =& wfGetDB(DB_SLAVE); $tbl = $dbr->tableName('revision'); $rev = $dbr->selectRow($tbl, 'rev_page', "rev_text_id = {$row->old_id}", __METHOD__); $title = Title::newFromID($rev->rev_page);
# Validate title if (is_object($title) && !in_array('sysop', $groups)) { $restrictions = $title->getRestrictions('read'); if (count($restrictions) && count(array_intersect($restrictions, $groups)) < 1) $row->old_text = wfMsg('badaccess-read'); }
$wgSimpleSecurity->enabled = true; }
/** * Needed in some versions to prevent Special:Version from breaking */ public function __toString() { return __CLASS__; } }
/**
* Hooks into Database::query and Database::fetchObject via the LoadBalancer class * - this is a global because PHP doesn't like nested class definitions */
function wfAddDatabaseHooks() { global $wgLoadBalancer, $wgDBtype;
# This ensures that $wgLoadBalancer is not a stub object when we subclass it # todo: this should be able to work in the case of it being a stub object wfGetDB();
# Create a replica of the Database class # - query method is overriden to ensure that old_id field is returned for all queries which read old_text field # - fetchObject method is overridden to validate row content based on old_id # - the changes to this class are only active for SELECT statements and while not processing security directives $type = ucfirst($wgDBtype); eval("class Database{$type}2 extends Database{$type}".' { public function query($sql, $fname = "", $tempIgnore = false) { global $wgSimpleSecurity; $count = false; if ($wgSimpleSecurity->enabled) $patched = preg_replace_callback("/(?<=SELECT ).+?(?= FROM)/", "SimpleSecurity::patchSQL", $sql, 1, $count); return parent::query($count ? $patched : $sql, $fname, $tempIgnore); } function fetchObject(&$res) { global $wgSimpleSecurity; $row = parent::fetchObject($res); if ($wgSimpleSecurity->enabled && isset($row->old_text)) SimpleSecurity::validateRow($row); return $row; } }');
# Create a replica of the LoadBalancer class which uses the new Database subclass for its connection objects class LoadBalancer2 extends LoadBalancer { function reallyOpenConnection(&$server) { $server['type'] .= '2'; $db =& parent::reallyOpenConnection($server); return $db; } }
# Replace the $wgLoadBalancer object with an identical instance of the new LoadBalancer2 class $wgLoadBalancer->closeAll(); # Close any open connections as they will be of the original Database class $oldLoadBalancer = $wgLoadBalancer; $wgLoadBalancer = new LoadBalancer2($oldLoadBalancer->mServers); foreach (array_keys(get_class_vars('LoadBalancer')) as $k) $wgLoadBalancer->$k = $oldLoadBalancer->$k; }
/**
* Called from $wgExtensionFunctions array when initialising extensions */
function wfSetupSimpleSecurity() { global $wgSimpleSecurity, $wgLanguageCode, $wgMessageCache, $wgSecurityUseDBHook;
# Hooks into Database::query and Database::fetchObject via the LoadBalancer class if ($wgSecurityUseDBHook) wfAddDatabaseHooks();
# Instantiate the SimpleSecurity singleton now that the environment is prepared $wgSimpleSecurity = new SimpleSecurity();
# Add the messages used by the specialpage if ($wgLanguageCode == 'en') { $wgMessageCache->addMessages(array( 'security' => "Security log", 'securitylogpage' => "Security log", 'securitylogpagetext' => "This is a log of actions blocked by the SimpleSecurity extension.", 'securitylogentry' => "", 'securityinfotoggle' => "This article exhibits security restrictions. Click this icon for more detail.", 'badaccess-read' => "!!Permission denied!!", 'badaccess-special' => "You do not have permission to access or perform special page actions on $1" )); } }
/**
* Register magic words */
function wfSimpleSecurityLanguageGetMagic(&$magicWords, $langCode = 0) { global $wgSecurityMagicIf, $wgSecurityMagicGroup; $magicWords[$wgSecurityMagicIf] = array($langCode, $wgSecurityMagicIf); $magicWords[$wgSecurityMagicGroup] = array($langCode, $wgSecurityMagicGroup); return true; }



