User:Fcarpani/PageSecurity Modified Version

I have this version working on MediaWiki 1.9.1

<?php
// PageSecurity MediaWiki extension.
// Restricts access to pages according to security definitions.

// Copyright (C) 2007, Benner Sistemas.
//
// 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

$pageSecurityVersion = '1.1.2';

#----------------------------------------------------------------------------
#    Extension initialization
#----------------------------------------------------------------------------

global $wgExtensionCredits;
$wgExtensionCredits['parserhook'][] = array(
    'name'=>'PageSecurity',
    'version'=>$pageSecurityVersion,
    'author'=>'Fernando Correia modified by Fernando Carpani',
    'url'=>'http://www.mediawiki.org/wiki/Extension:PageSecurity',
    'description' => 'Restricts access to pages according to security definitions. Now Allows a user if he belongs to allowed group using a configuration variable.'
    );
$wgExtensionFunctions[] = "fnPageSecurityExtension";
$wgHooks['ArticleSave'][] = 'fnPageSecuritySaveHook';
$wgHooks['userCan'][] = 'fnPageSecurityUserCanHook';
$wgHooks['PermissionRequired'][] = 'fnPageSecurityPermissionRequiredHook';
$wgHooks['SkinFooterLinks'][] = 'fnPageSecuritySkinFooterLinksHook';
$wgHooks['BeforePageDisplay'][] = 'fnPageSecurityBeforePageDisplayHook';

// Registers the extension with the WikiText parser.
function fnPageSecurityExtension() {
    global $wgParser;
    $wgParser->setHook("security-definition", "fnSecurityDefinitionTag");
}

#----------------------------------------------------------------------------
#    Event handlers
#----------------------------------------------------------------------------

// Processes the <security-definition> tag.
function fnSecurityDefinitionTag($input, $argv, &$parser) {
    if (!isset($argv["name"])) return SecurityDefinition::htmlError("Name parameter not informed for security-definition tag.");
    $name = $argv["name"];
    $security_definition = new SecurityDefinition();
    if (empty($parser->mRevisionId)) {  // preview mode: show from memory
        $result = $security_definition->loadFromPreview($input, $argv);
        if (!empty($result)) return $result;
    }
    else {  // normal mode: show effective settings from database
        $security_definition->mName = $name;
        $security_definition->loadFromDatabase();
    }
    return $security_definition->getHtmlDescription();
}

// Handles the save article event.
// Has two functions: double-check write permission and save security definitions.
function fnPageSecuritySaveHook(&$article, &$user, &$text, &$summary, &$minoredit, &$watchthis, &$sectionanchor, &$flags) {
    // Make sure the user can save the article.
    // This is not strictly required but it adds another layer of security.
    if (!SecurityManager::userCan($article->getTitle(), $user, "write")) {
        return "Access denied for saving this article.";
    }
    // Parse the security definition and save it to the database.
    $pattern = '@<security-definition(.*?)>(.*?)</security-definition>@is';
    $offset = 0;
    while (preg_match($pattern, $text, $matches, PREG_OFFSET_CAPTURE, $offset)) {
        if (!$user->isAllowed('protect')) {
            return "Only sysops can save security definitions.";
        }
        $definition = $matches[0][0];
        $offset = $matches[0][1] + strlen($definition);
        $security_definition = new SecurityDefinition();
        $result = $security_definition->parseXml($definition);
        if (!empty($result)) return $result;
        $security_definition->saveToDatabase();
    }
    return true;
}

// Handles the userCan event.
// $result = false if the $user is not allowed to execute $action on $title.
function fnPageSecurityUserCanHook($title, $user, $action, $result) {
    if (!SecurityManager::userCan($title, $user, $action)) $result = false;
}

// Handles the PermissionRequired event.
// Redirects to the error page.
function fnPageSecurityPermissionRequiredHook () {
    return SecurityManager::permissionRequired();
}

// Handles the SkinFooterLinks event.
// Adds a security definition notice to the page footer.
function fnPageSecuritySkinFooterLinksHook ($template, $footerlinks) {
    SecurityManager::addTemplateNotice($template, $footerlinks);
}

// Handles the BeforePageDisplay event.
// Changes the page logo according to the page security definition.
function fnPageSecurityBeforePageDisplayHook(&$out) {
    SecurityManager::setLogo();
}

#----------------------------------------------------------------------------
#    SecurityManager class
#----------------------------------------------------------------------------

// Executes page security operations.
class SecurityManager {
    static $userCan = array();  // Used for cache and loop control
    static $securityDefinitions = array();  // Security definition cache
    static $readPermissionError = false;  // true if a read permission error ocurred

    // Returns true if the action is allowed on the page; false if it is not allowed.
    private static function actionAllowed($title, $user, $action, $page_security_definition) {
      global $wgPageSecurityAllowGroupAccept;
        // check parameters
        if (empty($page_security_definition)) return true;  // no security definition
        if (empty($action)) return true;  // no action
        if (!in_array($action, array('read', 'write'))) return true;  // unknown actions won't be checked
        // add meta tags to output
        $meta_tag_name = "Page security definition (" . $title->getPrefixedDBkey() . ")";
        SecurityManager::addMetaTag($meta_tag_name, $page_security_definition->mName);
        // get groups the user belongs to
        $user_groups = $user->getEffectiveGroups();
        // check for sysop access
        global $wgPageSecurityAllowSysop;
        if (!empty($wgPageSecurityAllowSysop)) {  // Are sysops always allowed?
            if (in_array("sysop", $user_groups)) {
                return true;  // sysop access override granted
            }
        }
	// *** Can check allow first and deny last.... dirty but efective way.
	if (empty($wgPageSecurityAllowGroupAccept)||(!$wgPageSecurityAllowGroupAccept)){
	  wfDebug("====>Chequeando primero los grupos PROHIBIDOS\n");
	  
	  // check denied user groups
	  if (!empty($page_security_definition->mPermissions[$action]["deny"])) {
            $denied_groups = $page_security_definition->mPermissions[$action]["deny"];
	  }
	  if (!empty($denied_groups)) {
            foreach ($denied_groups as $group) {
	      if (in_array($group, $user_groups)) {
		return false;  // user is in denied group
	      }
            }
	  }
	  // check allowed user groups
	  if (!empty($page_security_definition->mPermissions[$action]["allow"])) {
            $allowed_groups = $page_security_definition->mPermissions[$action]["allow"];
	  }
	  if (!empty($allowed_groups)) {
            foreach ($allowed_groups as $group) {
	      if (in_array($group, $user_groups)) {
		return true;  // user is in allowed group
	      }
            }
	  }
	} else {
	 # wfDebug("====>Chequeando primero los grupos permitidos PERMITIDOS\n");
	  // check allowed user groups
	  if (!empty($page_security_definition->mPermissions[$action]["allow"])) {
            $allowed_groups = $page_security_definition->mPermissions[$action]["allow"];
	  }
	  if (!empty($allowed_groups)) {
            foreach ($allowed_groups as $group) {
	#      wfDebug(sprintf("=======>Grupo %s Permitido\n",$group));	  
	      if (in_array($group, $user_groups)) {
		return true;  // user is in allowed group
	      }
            }
	  }
	  // check denied user groups
	  if (!empty($page_security_definition->mPermissions[$action]["deny"])) {
            $denied_groups = $page_security_definition->mPermissions[$action]["deny"];
	  }
	  if (!empty($denied_groups)) {
#	    wfDebug(sprintf("=======>Grupo %s Prohibido\n",$group));
            foreach ($denied_groups as $group) {
	      if (in_array($group, $user_groups)) {
		return false;  // user is in denied group
	      }
            }
	  }

	  }
        // access not granted
        return false;
    }

    // Adds a new meta tag to the output HTML page.
    // Will not add duplicate tags.
    private static function addMetaTag($name, $value) {
                global $wgOut;
        if (!empty($wgOut->mMetatags)) {
            foreach ($wgOut->mMetatags as $tag) {
                if ($tag[0] == $name) {
                    return;  // already exists
                }
            }
        }
        $wgOut->addMeta($name, $value);
    }

    // Adds a security definition notice to the template.
    function addTemplateNotice(&$template, &$footerlinks) {
        if (empty($template->data["titleprefixeddbkey"])) return;
        $titleprefixeddbkey = $template->data["titleprefixeddbkey"];
        if (empty(SecurityManager::$securityDefinitions[$titleprefixeddbkey])) return;
        $securityDefinition = SecurityManager::$securityDefinitions[$titleprefixeddbkey];
        if (empty($securityDefinition->mNotice)) return;
        $notice = $securityDefinition->mNotice;
        $footerlinks[] = "SecurityDefinitionNotice";
        $template->data["SecurityDefinitionNotice"] = $notice;
    }

    // Executes the work for the UserCan function.
    private static function doUserCan($title, $user, $action) {
        // get page data
        $content = SecurityManager::getRevisionText($title);
        if (empty($content)) return true;  // nothing to check

        // check permissions
        if (empty(SecurityManager::$securityDefinitions[$title->getPrefixedDBkey()])) {
            SecurityManager::$securityDefinitions[$title->getPrefixedDBkey()] = SecurityManager::getPageSecurityDefinition($title, $content);
        }
        $page_security_definition = SecurityManager::$securityDefinitions[$title->getPrefixedDBkey()];
        if (!empty($page_security_definition)) {  // there is a security definition for this page
            if (!SecurityManager::actionAllowed($title, $user, $action, $page_security_definition)) {
                if ($action == "read") {
                    SecurityManager::$readPermissionError = true;
                }
                return false;
            }
        }

        // protect transclusion
        global $wgPageSecurityOptionalTransclusions;
        if ($action == "read" and !$wgPageSecurityOptionalTransclusions) {
            if (SecurityManager::transclusionNotAllowed($user, $content)) {
                return false;
            }
        }

        // access granted
        return true;
    }

    // Gets the security definition that applies to the page.
    // Only the first matching configuration will be used.
    private static function getPageSecurityDefinition($title, $content) {
        // search for a matching security definition
        global $wgPageSecurity;
        if (empty($title)) return;  // no title
        if (empty($wgPageSecurity)) return;  // no security definition
        foreach ($wgPageSecurity as $expression) {
            if (is_array($expression)) {  // pattern definition
                // get parameters
                if (count($expression) != 3) continue;  // ignore invalid expression
                $security_definition_name = $expression[0];
                $protection_subject = $expression[1];
                $protection_pattern = utf8_encode($expression[2]);
                // get pattern subject text
                if (strcasecmp($protection_subject, "title") == 0)
                    $subject = $title->getPrefixedText();
                else if (strcasecmp($protection_subject, "content") == 0) {
                    if (empty($content)) continue;  // no subject
                    $subject = $content;
                }
                else
                    continue;  // ignore invalid expression
                // test pattern in page
                if (preg_match("&$protection_pattern&is", $subject, $matches, PREG_OFFSET_CAPTURE)) {
                    $page_security_definition_name = $security_definition_name;
                    break;
                }
            } else {  // a default security definition was specified
                $default_definition = $expression;
            }
        }
        // use default security definition if needed
        if (empty($page_security_definition_name)) {  // no expression matches this page
            if (!empty($default_definition)) {
                $page_security_definition_name =  $default_definition;  // use default security definition
            }
        }
        // get security definition object
        if (empty($page_security_definition_name)) return;
        $page_security_definition = new SecurityDefinition();
        $page_security_definition->mName = $page_security_definition_name;
        if (!$page_security_definition->loadFromDatabase()) return;
        return $page_security_definition;
    }

    // Returns the text of the current revision of the title.
    private static function getRevisionText($title) {
        if (empty($title)) return;
        $revision = Revision::newFromTitle($title);
        if (empty($revision)) return;
        $content = $revision->getText();
        if (empty($content)) return;
        return $content;
    }

    // Redirects to the error page specified by the user.
    // The custom error message will be displayed if both these conditions are true:
    // - The access restriction was generated by the PageSecurity extension.
    // - A custom error page was specified.
    // Otherwise, the default error message will be displayed.
    static function permissionRequired() {
        global $wgOut;
        global $wgPageSecurityErrorPage;
        if (!SecurityManager::$readPermissionError) return;
        if (empty($wgPageSecurityErrorPage)) return;
        $title = Title::newFromText($wgPageSecurityErrorPage);
        $redirectURL = $title->getFullURL();
        $wgOut->redirect($redirectURL);
        return false;  // instruct caller to skip processing
    }

    // Sets the logo to the one specified in the page security definition, if there is one.
    // If no logo was specified, nothing is changed.
    static function setLogo() {
        global $wgTitle;
        if (empty($wgTitle)) return;
        $db_key = $wgTitle->getPrefixedDBkey();
        if (empty($db_key)) return;
        if (empty(SecurityManager::$securityDefinitions)) return;
        $page_security_definition = SecurityManager::$securityDefinitions[$db_key];
        if (empty($page_security_definition)) return;
        $logo = $page_security_definition->mLogo;
        if (empty($logo)) return;
        global $wgLogo;
        $wgLogo = $logo;
    }

    // Returns true if the page includes a protected page that the user is not allowed to access.
    private static function transclusionNotAllowed($user, $content) {
        $pattern = '@{{(.+?)(\|.*?)?}}@is';
        $offset = 0;
        while (preg_match($pattern, $content, $matches, PREG_OFFSET_CAPTURE, $offset)) {
            $offset = $matches[0][1] + 1;  // restart search at the second character to prevent attacks
            $transclusion_text = trim($matches[1][0]);
            $transclusion_link = $transclusion_text;
            $transclusion_title = Title::newFromText($transclusion_link, NS_TEMPLATE);
            if (!empty($transclusion_title)) {
                if (!SecurityManager::UserCan($transclusion_title, $user, "read")) return true;  // transclusion not allowed
            }
        }
        return false;  // no forbidden transclusion
    }

    // Checkes whether $user can execute $action on $title.
    // Returns true if the user can do the action; false if the action is not allowed.
    // Safe to use even in case of recursion or circular reference.
    static function userCan($title, $user, $action) {
        $title_key = $title->getPrefixedDBkey();
        if (in_array($action, array('create', 'edit', 'move'))) $action = 'write';  // write permission allows several actions
        if (isset(SecurityManager::$userCan[$title_key][$action])) {  // either already processed or processing
            $stored_result = SecurityManager::$userCan[$title_key][$action];
            if ($stored_result === "processing") return true;  // prevent infinite loop
            return $stored_result;
        }
        SecurityManager::$userCan[$title_key][$action] = "processing";
        $result = SecurityManager::doUserCan($title, $user, $action);
        SecurityManager::$userCan[$title_key][$action] = $result;  // store result to avoid checking the same permission again
        return $result;
    }

}

#----------------------------------------------------------------------------
#    SecurityDefinition class
#----------------------------------------------------------------------------

//  A security definition.
class SecurityDefinition {
    var $mId;
    var $mName;
    var $mBaseId;
    var $mBaseName;
    var $mNotice;
    var $mLogo;
    var $mSecurityDefinitionItems;  // array of SecurityDefinitionItem
    var $mPermissions;  // array of action, permission, group
    
    function SecurityDefinition() {
        // initialize members to avoid uninitialized variable errors
        $this->mId = 0;
        $this->mName = "";
        $this->mBaseId = 0;
        $this->mBaseName = "";
        $this->mNotice = "";
        $this->mLogo = "";
    }

    // Adds an item to the security definition.
    // Prevents duplicated items.
    function addItem($item) {
        if (!$this->duplicatedItem($item)) $this->mSecurityDefinitionItems[] = $item;
    }

    // Creates the permissions collection from the items array.
    function createPermissionsArray() {
        unset($this->mPermissions);
        if (empty($this->mSecurityDefinitionItems)) return;
        foreach ($this->mSecurityDefinitionItems as $item) {
            $this->mPermissions[$item->mAction][$item->mPermission][] = $item->mGroup;
        }
    }

    // Returns true if an item with the same permission is already in the security definition; false if it is not.
    function duplicatedItem($item) {
        if (empty($this->mSecurityDefinitionItems)) return false;
        foreach ($this->mSecurityDefinitionItems as $existingItem) {
            if ($existingItem->mAction == $item->mAction and
                $existingItem->mPermission == $item->mPermission and
                $existingItem->mGroup == $item->mGroup)
                return true;
        }
        return false;
    }

    // Finds an Id that corresponds to a name.
    static function findId($name) {
        $securityDefinition = new SecurityDefinition();
        $securityDefinition->mName = $name;
        if ($securityDefinition->loadFromDatabase())
            return $securityDefinition->mId;
        else
            return 0;
    }

    // Returns an HTML description of the security definition.
    function getHtmlDescription() {
        if (empty($this->mName))
            return SecurityDefinition::htmlError("The security definition was not initialized.");
        $name = $this->mName;
        if (empty($this->mBaseName))
            $inheritance = "";
        else
            $inheritance = " (inherits from " . $this->mBaseName. ")";
        $description = "<p><b>Security definition $name$inheritance:</b></p>\n";
        if (!empty($this->mNotice)) {
            $description .= "<p><i>{$this->mNotice}</i></p>\n";
        }
        if (!empty($this->mLogo)) {
            $description .= "<p>Logo: {$this->mLogo}</p>\n";
        }
        if (empty($this->mPermissions))
            $description .= "<p>(empty)</p>\n";
        else {
            $description .= "<ul>\n";
            foreach ($this->mPermissions as $action => $actions) {
                $allow = "";
                if (!empty($actions["allow"])) {
                    foreach ($actions["allow"] as $group) {
                        if ($group == "*") $group = "all";
                        if (!empty($allow)) $allow .= ", ";
                        $allow .= $group;
                    }
                }
                $except = "";
                if (!empty($actions["deny"])) {
                    foreach ($actions["deny"] as $group) {
                        if ($group == "*") $group = "all";
                        if (!empty($except)) $except .= ", ";
                        $except .= $group;
                    }
                }
                $description .= "  <li>";
                $description .= "<b>$action</b>: ";
                if (empty($allow))
                    $description .= "deny all";
                else {
                    $description .= "allow $allow";
                    if (!empty($except)) $description .= " except $except";
                }
                $description .= ".</li>\n";
            }
            $description .= "</ul>\n";
        }
        return $description;
    }

    // Returns an error message formatted as HTML.
    static function htmlError($input) {
        return "<p><b>Security definition error: $input</b></p>\n";
    }

    // Loads items from a base security definition.
    // Only the base id or the base name must be informed.
    function loadFromBase($baseId = 0, $baseName = "") {
        if (empty($baseId) and empty($baseName)) return SecurityDefinition::htmlError("The base security definition was not specified.");
        $baseSecurityDefinition = new SecurityDefinition();
        $baseSecurityDefinition->mId = $baseId;
        $baseSecurityDefinition->mName = $baseName;
        if (!$baseSecurityDefinition->loadFromDatabase()) return SecurityDefinition::htmlError("Base security definition is empty.");
        $this->mBaseId = $baseSecurityDefinition->mId;
        $this->mBaseName = $baseSecurityDefinition->mName;
        foreach ($baseSecurityDefinition->mSecurityDefinitionItems as $item) {
            $this->addItem($item);
        }
    }

    // Loads the SecurityDefinitionItems from the database.
    // Uses either $mId or $mName as keys.
    // Returns true if the record was loaded; false if not found.
        function loadFromDatabase() {
        // load security definition
        if (!empty($this->mId)) $key = array('security_definition_id' => $this->mId);
        else if (!empty($this->mName)) $key = array('security_definition_name' => $this->mName);
        else return false;  // no key
        unset($this->mSecurityDefinitionItems);  // must unset before possibly loading base security definition
        $dbr =& wfGetDB(DB_SLAVE);
        $res = $dbr->select(
            'security_definitions',
            array(
                'security_definition_id',
                'security_definition_name',
                'base_security_definition_id',
                'security_definition_notice',
                'security_definition_logo',
            ),
            $key,
            __METHOD__
        );
        if ($record = $dbr->fetchObject($res)) {
            $this->mId = intval($record->security_definition_id);
            $this->mName = trim($record->security_definition_name);
            $this->mBaseId = intval($record->base_security_definition_id);
            $this->mNotice = trim($record->security_definition_notice);
            $this->mLogo = trim($record->security_definition_logo);
            if (!empty($this->mBaseId)) {
                $this->loadFromBase($this->mBaseId, "");
            }
        } else {
            return false;
        }

        // load items
        $dbr =& wfGetDB(DB_SLAVE);
        $res = $dbr->select(
            'security_definition_items',
            array(
                'security_definition_item_id',
                'security_definition_id',
                'security_definition_item_action',
                'security_definition_item_permission',
                'security_definition_item_group',
            ),
            array('security_definition_id' => $this->mId),
            __METHOD__,
            array('ORDER BY'  => 'security_definition_item_id')
        );
        while ($record = $dbr->fetchObject($res)) {
            $item = new SecurityDefinitionItem();
            $item->mId = $record->security_definition_item_id;
            $item->mSecurityDefinitionId = $record->security_definition_id;
            $item->mAction = trim($record->security_definition_item_action);
            $item->mPermission = trim($record->security_definition_item_permission);
            $item->mGroup = trim($record->security_definition_item_group);
            $this->addItem($item);
        }
        $this->createPermissionsArray();
        return true;
        }

    // Loads from information available during an edit preview.
    function loadFromPreview($input, $argv) {
        if (isset($argv["name"]))
            $name_parameter = 'name="' . $argv["name"] . '"';
        else
            $name_parameter = "";
        if (isset($argv["notice"]))
            $notice_parameter = 'notice="' . $argv["notice"] . '"';
        else
            $notice_parameter = "";
        if (isset($argv["logo"]))
            $logo_parameter = 'logo="' . $argv["logo"] . '"';
        else
            $logo_parameter = "";
        if (isset($argv["base"]))
            $base_parameter = 'base="' . $argv["base"] . '"';
        else
            $base_parameter = "";
        $tag = "<security_definition $name_parameter $notice_parameter $logo_parameter $base_parameter>$input</security_definition>";
        return $this->parseXml($tag);
    }

    // Creates item definitions from a XML structure.
    function parseXml($tag) {
        $xmlParser = xml_parser_create();
        xml_parse_into_struct($xmlParser, $tag, $vals, $index);
        xml_parser_free($xmlParser);
        if (empty($vals[0])) return SecurityDefinition::htmlError("The security definition could not be parsed.");
        $open_tag = $vals[0];
        if (empty($open_tag["attributes"])) return SecurityDefinition::htmlError("No attribute was specified in security definition.");
        $attributes = $open_tag["attributes"];
        if (empty($attributes["NAME"])) return SecurityDefinition::htmlError("Name was not specified in security definition.");
        $name = $attributes["NAME"];
        $this->mId = SecurityDefinition::findId($name);
        $this->mName = $name;
        if (!empty($attributes["NOTICE"])) {
            $this->mNotice = $attributes["NOTICE"];
        }
        if (!empty($attributes["LOGO"])) {
            $this->mLogo = $attributes["LOGO"];
        }
        unset($this->mSecurityDefinitionItems);  // must unset before possibly loading from base
        if (!empty($attributes["BASE"])) $baseName = $attributes["BASE"];
        if (!empty($baseName)) {
            $result = $this->loadFromBase(0, $baseName);
            if (!empty($result)) return $result;
        }
        foreach ($vals as $value) {
            $permission = trim(strtolower($value["tag"]));
            if ($permission == "allow" || $permission == "deny") {
                if ($value["type"] != "complete") continue;
                if (empty($value["attributes"])) return SecurityDefinition::htmlError("No attribute in $permission tag of then security definition.");
                $attributes = $value["attributes"];
                if (empty($attributes["ACTION"])) return SecurityDefinition::htmlError("No action in $permission tag of then security definition.");
                $action = trim(strtolower($attributes["ACTION"]));
                if (!in_array($action, array('read', 'write'))) return SecurityDefinition::htmlError("Action $action is not recognized. Use 'read' or 'write'.");
                if (empty($value["value"])) return SecurityDefinition::htmlError("No group in $permission tag of then security definition.");
                $groups = split(",", $value["value"]);
                if (empty($groups)) return SecurityDefinition::htmlError("No group in $permission tag of then security definition.");
                foreach ($groups as $group) {
                    $group = trim($group);
                    if (strcasecmp($group, "all") == 0) $group = "*";  // substitute "*" for "all"
                    $item = new SecurityDefinitionItem();
                    $item->mSecurityDefinitionId = $this->mId;
                    $item->mAction = $action;
                    $item->mPermission = $permission;
                    $item->mGroup = $group;
                    $this->addItem($item);
                }
            }
        }
        $this->createPermissionsArray();
    }

    // Saves the SecurityDefinitionItems to the database.
    function saveToDatabase() {
        // delete previous security definition items
        if (!empty($this->mId)) {
            $dbw =& wfGetDB( DB_MASTER );
            $dbw->delete('security_definition_items',
                array('security_definition_id' => $this->mId),
                __METHOD__
            );
        }

        // save security definition
                $dbw =& wfGetDB( DB_MASTER );
        if (empty($this->mId)) {  // insert
                    $seqVal = $dbw->nextSequenceValue('security_definition_id_seq');
                    $dbw->insert('security_definitions',
                            array(
                                    'security_definition_id' => $seqVal,
                                    'security_definition_name' => trim($this->mName),
                                    'base_security_definition_id' => $this->mBaseId,
                    'security_definition_notice' => trim($this->mNotice),
                                    'security_definition_logo' => trim($this->mLogo),
                            ), __METHOD__
                    );
                    $this->mId = $dbw->insertId();
        } else {  // update
                    $dbw->update('security_definitions',
                            array(
                                    'security_definition_name' => trim($this->mName),
                                    'base_security_definition_id' => $this->mBaseId,
                    'security_definition_notice' => trim($this->mNotice),
                                    'security_definition_logo' => trim($this->mLogo),
                            ),
                array('security_definition_id' => $this->mId),
                __METHOD__
                    );
        }

        // save security definition items
        if (empty($this->mSecurityDefinitionItems)) return;
        foreach ($this->mSecurityDefinitionItems as $item) {
            if (empty($item->mSecurityDefinitionId)) $item->mSecurityDefinitionId = $this->mId;  // associate with security definition record ID
            if ($item->mSecurityDefinitionId == $this->mId) {  //  do not save items inherited from base security
                $item->addToDatabase();
            }
        }
    }

}

#----------------------------------------------------------------------------
#    SecurityDefinitionItem class
#----------------------------------------------------------------------------

// An item of a security definition.  
class SecurityDefinitionItem {
    var $mId;
    var $mSecurityDefinitionId;
    var $mAction;
    var $mPermission;
    var $mGroup;
    
    function SecurityDefinitionItem() {
    }
    
    // Inserts the record into the database.
        function addToDatabase() {
                $dbw =& wfGetDB( DB_MASTER );
                $seqVal = $dbw->nextSequenceValue('security_definition_item_id_seq');
                $dbw->insert('security_definition_items',
                        array(
                                'security_definition_item_id' => $seqVal,
                                'security_definition_id' => $this->mSecurityDefinitionId,
                                'security_definition_item_action' => $this->mAction,
                                'security_definition_item_permission' => $this->mPermission,
                                'security_definition_item_group' => $this->mGroup,
                        ), __METHOD__
                );
                $this->mId = $dbw->insertId();
        }

}
?>