<?php
/**
* Mediawiki-extension for page-protection. This extension allows protecting
* single areas by spanning them with <protect>-tags. The tag has the following
* parameters:
* users: Comma-separated list of usernames who are allowed to read and edit
* the area.
* groups: Comma-separated lsit of groupnames who are allowed to read and edit
* the area.
* The text between the opening and closing 'protect'-tags will be hidden for
* users not in the specified lists. Members of 'sysop' always have access.
*
* Example:
* <protect users="admin1,admin2" groups="sysop">
* Database-Settings
* ;Server
* : localhost
* ...
* </protect>
*
* With this extension, it is possible to mark single sections of pages as
* protected and thus leaving the other sections editable to all users.
* Access to sections containing <protect>-tags is only permitted for users
* having access to all protected areas in the specified section / article.
*
* TODO Editing sections within protected areas is possible by supplying
* section=X to URL! So we need to check permissions of protect-tags
* surrounding the current section.
*
* TODO Searching for protected text is possible.
*
* @author Copyright (C) 2006 Fabian Schmitt (fs@u4m.de)
* @version 0.7
*
* 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.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* http://www.gnu.org/copyleft/gpl.html
*
*/
define("PROTECT_TAG", "protect");
define("PROTECT_EDIT_TAG", "protect-edit");
define("VAR_USERS", "{{{USERS}}}");
define("VAR_GROUPS", "{{{GROUPS}}}");
define('MSG_NO_ACCESS',
" This area is [[Page Protection|protected]].
You are not allowed to read or edit this area.<br/>
Allowed users: ".VAR_USERS.
" <br/>Allowed groups: ".VAR_GROUPS);
define('MSG_NO_EDIT_ACCESS',
" This page is [[Page Protection|protected]].
You are not allowed to edit this page.<br/>
Allowed users: ".VAR_USERS.
" <br/>Allowed groups: ".VAR_GROUPS);
$wgExtensionFunctions[] = "wfPageProtection";
$wgExtensionCredits['parserhook'][] = array(
'name' => 'ProtectPage',
'author' => 'Fabian Schmitt (http://meta.wikimedia.org/wiki/User:Sirius_gd)',
'version' => '0.7',
'url' => 'http://meta.wikimedia.org/wiki/PageProtection'
);
/**
* Extension-function. Registers parser, hook, messages.
*/
function wfPageProtection() {
global $wgParser;
$msgs = array (
'ProtectedSite' => MSG_NO_ACCESS,
'ProtectedEditSite' => MSG_NO_EDIT_ACCESS
);
global $wgMessageCache;
$wgMessageCache->addMessages($msgs);
// set a hook on our parser to protect content that is protected
// both from viewing and editing
$wgParser->setHook( PROTECT_TAG, "protectPage" );
$wgParser->setHook( PROTECT_EDIT_TAG, "protectEditPage" );
global $wgHooks;
$wgHooks['AlternateEdit'][] = 'protectedEdit';
}
/**
* Splits the users that are allowed to acces the protected area from a
* parameter.
* @param params The parameters as given to the hook-function
* @return Array with user-names
*/
function getAllowedUsers($params) {
if (isset($params["users"])) {
return explode(",", $params["users"]);
} else {
return array();
}
}
/**
* Splits the groups that are allowed to acces the protected area from a
* parameter. If the group sysop is not supplied as parameter, it will be
* added automatically.
* @param params The parameters as given to the hook-function
* @return Array with group-names including sysop.
*/
function getAllowedGroups($params) {
if (isset($params["groups"])) {
$groups = explode(",", $params["groups"]);
}
// sysops always have access
if (!isset($groups) || !in_array("sysop", $groups )) {
$groups[] = "sysop";
}
return $groups;
}
/**
* Checks if a given user has access to a protected area.
* @param user User-object to check for.
* @param users Array containing allowed user-names.
* @param groups Array containing allowed group-names.
* @return true, if user is in list of users or in one of the allowed groups,
* false otherwise.
*/
function hasAccess(&$user, &$users, &$groups) {
// Check Group access
foreach($groups as $group) {
if(in_array($group, $user->mGroups)){
return true;
}
}
if (isset($users) && in_array($user->getName(), $users)) {
return true;
}
return false;
}
/**
* Reads the error-message 'ProtectedSite' from the message-cache and replaces
* all {{{USERS}}} by a comma-separated list of allowed users and all
* {{{GROUPS}}} by a comma-separated lsit of allowed groups.
* @param users List of allowed users.
* @param groups list of allowed groups
* @param parseWiki If true, the text will be parsed before returning.
* @return Error-message.
*/
function getErrorMessage(&$users, &$groups, $parseWiki = true, $edit_protected = false) {
global $wgOut;
if ($edit_protected) {
$msg = wfMsg("ProtectedEditSite");
} else {
$msg = wfMsg("ProtectedSite");
}
$usersString = "";
foreach($users as $user) {
if ($user == "") {
continue;
}
if ($usersString != "") {
$usersString = $usersString.", ";
}
// make link to user-page
$u = User::newFromName($user);
$title = Title::makeTitle(NS_USER,$user);
$userPage = "[[".$title->getNsText().":".$user."|".$user."]]";
$usersString = $usersString.$userPage;
}
$groupsString = "";
foreach($groups as $group) {
if ($groupsString != "") {
$groupsString = $groupsString.", ";
}
$groupsString = $groupsString.$group;
}
$msg = str_replace(VAR_USERS, $usersString, $msg);
$msg = str_replace(VAR_GROUPS, $groupsString, $msg);
if ($parseWiki) {
return $wgOut->parse($msg);
} else {
return $msg;
}
}
/**
* Callback function for the hook to the protect-tag.
* @param text Text to be protected
* @param params Parameters supplied to the tag
* @param parser Global parser-object
* @return If current use is allowed to read the page, $text will be returned.
* Otherwise, an error-page will be returned.
*/
function protectPage( $text, $params, &$parser) {
global $wgUser;
global $wgOut;
$parser->disableCache();
$users = getAllowedUsers($params);
$groups = getAllowedGroups($params);
if (hasAccess($wgUser, $users, $groups)) {
return $wgOut->parse($text);
}
return getErrorMessage($users, $groups);
}
/**
* Callback function for the hook to the protect-edit tag.
* @param text Text to be protected
* @param params Parameters supplied to the tag
* @param parser Global parser-object
* @return If current use is allowed to read the page, $text will be returned.
* Otherwise, an error-page will be returned.
*/
function protectEditPage( $text, $params, &$parser) {
global $wgOut;
$parser->disableCache();
// we don't protect viewing, so show it here
return $wgOut->parse($text);
}
/**
* Reads currently displayed or edited section from request.
* @return Current section or 0 if no section is beeing edited.
*/
function getSection()
{
global $wgRequest;
require_once("includes/WebRequest.php");
// section when editing
$section = $wgRequest->getVal('section');
if (!$section) {
// for preview and finish editing, section is in wpSection
$section = $wgRequest->getVal('wpSection');
if (!$section) {
$section = 0;
}
}
return $section - 1;
}
/**
* Intersects two array and returns the result. If one of the given arrays
* is empty, the other one will be returned.
* @param a1 First array
* @param a2 Second array
* @return Intersection
*/
function intersect($a1, $a2) {
if (count($a1) == 0) {
return $a2;
}
if (count($a2) == 0) {
return $a1;
}
return array_intersect($a1, $a2);
}
/**
* Cancels the editing and prints error message.
* @param users List of allowed users
* @param groups List of allowed groups
*/
function stopEditing($users, $groups, $edit_protected=false) {
global $wgOut;
$wgOut->setPageTitle( "Page is protected" );
$wgOut->setRobotpolicy( 'noindex,nofollow' );
$wgOut->setArticleRelated( false );
$wgOut->addWikiText( getErrorMessage($users, $groups, false, $edit_protected) );
$wgOut->returnToMain( false );
}
/**
* Callback-function for hook to 'AlternateEdit'. Checks if the current user
* is allowed to edit the current article / section and if so returns the
* current editpage-object.
* TODO make the error-page nicer.
* @param editpage EditPage-object
* @return editpage if user is allowed to edit the section, null otherwise.
*/
function protectedEdit($editpage) {
global $wgUser;
global $wgRequest;
global $_REQUEST;
global $_GET;
$section = getSection();
if ($section != -1) {
$old_section = $section + 1;
}
// wipe out the section request for now
if (is_array($_REQUEST) && isset($_REQUEST['section'])) {
unset($_REQUEST['section']);
} elseif (is_array($_GET) && isset($_GET['section'])) {
unset($_GET['section']);
}
// Now get the full page content and see if we've got a protected
// bit anywhere in it
// Get text to be edited.. because we've cleared $_REQUEST and
// $_GET of 'section', Article.php#getContent() will give us the
// full text of the page
$text = $editpage->mArticle->getContent();
$edit_protected = false;
// find protected tags
$content = '';
$tags = '';
$params = array();
// we check first for edit protection with the PROTECT_EDIT_TAG
Parser::extractTagsAndParams(PROTECT_EDIT_TAG, $text, $content, $tags, $params );
if (count($tags) == 0) {
// No edit protection sections.. are there any view/edit
// protection sections?
$content = '';
$tags = '';
$params = array();
Parser::extractTagsAndParams(PROTECT_TAG, $text, $content, $tags, $params );
if (count($tags) == 0) {
if (isset($old_section)) {
$_REQUEST['section'] = $old_section; // reset our section
}
return $editpage; // no protected text was found so continue editing
}
} else {
$edit_protected = true;
}
// get intersections of allowed users and groups for all
// protected areas in text-block.
$users = array();
$groups = array();
foreach ($params as $param) {
$users = intersect($users, getAllowedUsers($param));
$groups = intersect($groups, getAllowedGroups($param));
}
if (hasAccess($wgUser, $users, $groups)) {
if (isset($old_section)) {
$_REQUEST['section'] = $old_section; // reset our section
}
return $editpage;
}
stopEditing($users, $groups, $edit_protected);
}