LotGDocker/lotgd-web/lotgd/lib/modules.php

1460 lines
52 KiB
PHP
Raw Permalink Normal View History

2020-08-17 19:16:42 -04:00
<?php
// translator ready
// addnews ready
// mail ready
require_once("lib/arraytourl.php");
$injected_modules = array(1=>array(),0=>array());
function injectmodule($modulename,$force=false){
global $mostrecentmodule,$injected_modules;
//try to circumvent the array_key_exists() problem we've been having.
if ($force) $force = 1; else $force = 0;
//early escape if we already called injectmodule this hit with the
//same args.
if (isset($injected_modules[$force][$modulename])) {
$mostrecentmodule=$modulename;
return $injected_modules[$force][$modulename];
}
$modulename = modulename_sanitize($modulename);
$modulefilename = "modules/{$modulename}.php";
if (file_exists($modulefilename)){
tlschema("module-{$modulename}");
$sql = "SELECT active,filemoddate,infokeys,version FROM " . db_prefix("modules") . " WHERE modulename='$modulename'";
$result = db_query_cached($sql, "inject-$modulename", 3600);
if (!$force) {
//our chance to abort if this module isn't currently installed
//or doesn't meet the prerequisites.
if (db_num_rows($result)==0) {
tlschema();
output_notl("`n`3Module `#%s`3 is not installed, but was attempted to be injected.`n",$modulename);
$injected_modules[$force][$modulename]=false;
return false;
}
$row = db_fetch_assoc($result);
if ($row['active']){ } else {
tlschema();
output("`n`3Module `#%s`3 is not active, but was attempted to be injected.`n",$modulename);
$injected_modules[$force][$modulename]=false;
return false;
}
}
require_once($modulefilename);
$mostrecentmodule = $modulename;
$info = "";
if (!$force){
//avoid calling the function if we're forcing the module
$fname = $modulename."_getmoduleinfo";
$info = $fname();
if (!isset($info['requires'])) $info['requires'] = array();
if (!is_array($info['requires'])) $info['requires'] = array();
if (!isset($info['download'])) $info['download']="";
if (!isset($info['description'])) $info['description']="";
if (!module_check_requirements($info['requires'])) {
$injected_modules[$force][$modulename]=false;
tlschema();
output("`n`3Module `#%s`3 does not meet its prerequisites.`n",$modulename);
return false;
}
}
//check to see if the module needs to be upgraded.
if (db_num_rows($result)>0){
if (!isset($row)) $row = db_fetch_assoc($result);
$filemoddate = date("Y-m-d H:i:s",filemtime($modulefilename));
if ($row['filemoddate']!=$filemoddate || $row['infokeys']=="" ||
$row['infokeys'][0] != '|' || $row['version']==''){
//The file has recently been modified, lock tables and
//check again (knowing we're the only one who can do this
//at one shot)
$sql = "LOCK TABLES " . db_prefix("modules") . " WRITE";
db_query($sql);
//check again after the table has been locked.
$sql = "SELECT filemoddate FROM " . db_prefix("modules") . " WHERE modulename='$modulename'";
$result = db_query($sql);
$row = db_fetch_assoc($result);
if ($row['filemoddate']!=$filemoddate ||
!isset($row['infokeys']) || $row['infokeys']=="" || $row['infokeys'][0] != '|' ||
$row['version']==''){
//the file mod time is still different from that
//recorded in the database, time to update the database
//and upgrade the module.
debug("The module $modulename was found to have updated, upgrading the module now.");
if (!is_array($info)){
//we might have gotten this info above, if not,
//we need it now.
$fname = $modulename."_getmoduleinfo";
$info = $fname();
if (!isset($info['download']))
$info['download']="";
if (!isset($info['version']))
$info['version']="0.0";
if (!isset($info['description']))
$info['description'] = '';
}
//Everyone else will block at the initial lock tables,
//we'll update, and on their second check, they'll fail.
//Only we will update the table.
$keys = "|".join(array_keys($info), "|")."|";
$sql = "UPDATE ". db_prefix("modules") . " SET moduleauthor='".addslashes($info['author'])."', category='".addslashes($info['category'])."', formalname='".addslashes($info['name'])."', description='".addslashes($info['description'])."', filemoddate='$filemoddate', infokeys='$keys',version='".addslashes($info['version'])."',download='".addslashes($info['download'])."' WHERE modulename='$modulename'";
db_query($sql);
debug($sql);
$sql = "UNLOCK TABLES";
db_query($sql);
// Remove any old hooks (install will reset them)
module_wipehooks();
$fname = $modulename."_install";
if ($fname() === false) {
return false;
}
invalidatedatacache("inject-$modulename");
}else{
$sql = "UNLOCK TABLES";
db_query($sql);
}
}
}
tlschema();
$injected_modules[$force][$modulename]=true;
return true;
}else{
output("`n`\$Module `^%s`\$ was not found in the modules directory.`n",$modulename);
$injected_modules[$force][$modulename]=false;
return false;
}
}
/*
* Returns the status of a module as a bitfield
*
* @param string $modulename The module name
* @param string $version The version to check for (false for don't care)
* @return int The status codes for the module
*/
function module_status($modulename, $version=false) {
global $injected_modules;
$modulename = modulename_sanitize($modulename);
$modulefilename = "modules/$modulename.php";
$status = MODULE_NO_INFO;
if (file_exists($modulefilename)) {
$sql = "SELECT active,filemoddate,infokeys,version FROM " . db_prefix("modules") . " WHERE modulename='$modulename'";
$result = db_query_cached($sql, "inject-$modulename", 3600);
if (db_num_rows($result) > 0) {
// The module is installed
$status = MODULE_INSTALLED;
$row = db_fetch_assoc($result);
if ($row['active']) {
// Module is here and active
$status |= MODULE_ACTIVE;
// In this case, the module could have been force injected or
// not. We still want to mark it either way.
if (array_key_exists($modulename, $injected_modules[0]) &&
$injected_modules[0][$modulename])
$status |= MODULE_INJECTED;
if (array_key_exists($modulename, $injected_modules[1]) &&
$injected_modules[1][$modulename])
$status |= MODULE_INJECTED;
} else {
// Force-injected modules can be injected but not active.
if (array_key_exists($modulename, $injected_modules[1]) &&
$injected_modules[1][$modulename])
$status |= MODULE_INJECTED;
}
// Check the version number
if ($version===false) {
$status |= MODULE_VERSION_OK;
} else {
if (module_compare_versions($row['version'], $version) < 0) {
$status |= MODULE_VERSION_TOO_LOW;
} else {
$status |= MODULE_VERSION_OK;
}
}
} else {
// The module isn't installed
$status = MODULE_NOT_INSTALLED;
}
} else {
// The module file doesn't exist.
$status = MODULE_FILE_NOT_PRESENT;
}
return $status;
}
/**
* Determines if a module is activated
*
* @param string $modulename The module name
* @return bool If the module is active or not
*/
function is_module_active($modulename){
return (module_status($modulename) & MODULE_ACTIVE);
}
/**
* Determines if a module is installed
*
* @param string $modulename The module name
* @param string $version The version to check for
* @return bool If the module is installed
*/
function is_module_installed($modulename,$version=false){
// Status will say the version is okay if we don't care about the
// version or if the version is actually correct
return (module_status($modulename, $version) &
(MODULE_INSTALLED|MODULE_VERSION_OK));
}
/**
* Checks if the module requirements are satisfied. Should a module require
* other modules to be installed and active, then optionally makes them so
*
* @param array $reqs Requirements of a module from _getmoduleinfo()
* @return bool If successful or not
*/
function module_check_requirements($reqs, $forceinject=false){
// Since we can inject here, we need to save off the module we're on
global $mostrecentmodule;
$oldmodule = $mostrecentmodule;
$result = true;
if (!is_array($reqs)) return false;
// Check the requirements.
reset($reqs);
while (list($key,$val)=each($reqs)){
$info = explode("|",$val);
if (!is_module_installed($key,$info[0])) {
return false;
}
// This is actually cheap since we cache the result
$status = module_status($key);
// If it's not injected and we should force it, do so.
if (!($status & MODULE_INJECTED) && $forceinject) {
$result = $result && injectmodule($key);
}
}
$mostrecentmodule = $oldmodule;
return $result;
}
/**
* Blocks a module from being able to hook for the rest of the page hit.
* Please note, any hooks already executed by the blocked module will
* not be undone, so this function is pretty flaky all around.
*
* The only way to use this safely would be to block/unblock modules from
* the everyhit hook and make sure to shortcircuit for any page other than
* the one you care about.
*
* @param mixed $modulename The name of the module you wish to block or true if you want to block all modules.
* @return void
*/
$block_all_modules = false;
$blocked_modules = array();
function blockmodule($modulename) {
global $blocked_modules, $block_all_modules, $currenthook;
if ($modulename === true) {
$block_all_modules = true;
return;
}
$blocked_modules[$modulename]=1;
}
/**
* Unblocks a module from being able to hook for the rest of the page hit.
* Please note, any hooks already blocked for the module being unblocked
* have been lost, so this function is pretty flaky all around.
*
* The only way to use this safely would be to block/unblock modules from
* the everyhit hook and make sure to shortcircuit for any page other than
* the one you care about.
*
* @param mixed $modulename The name of the module you wish to unblock or true if you want to unblock all modules.
* @return void
*/
$unblocked_modules = array();
function unblockmodule($modulename) {
global $unblocked_modules, $block_all_modules;
if ($modulename === true) {
$block_all_modules = false;
return;
}
$unblocked_modules[$modulename]=1;
}
$module_preload = array();
/**
* Preloads data for multiple modules in one shot rather than
* having to make SQL calls for each hook, when many of the hooks
* are found on every page.
* @param array $hooknames Names of hooks whose attached modules should be preloaded.
* @return bool Success
*/
function mass_module_prepare($hooknames){
sort($hooknames);
$Pmodules = db_prefix("modules");
$Pmodule_hooks = db_prefix("module_hooks");
$Pmodule_settings = db_prefix("module_settings");
$Pmodule_userprefs = db_prefix("module_userprefs");
global $modulehook_queries;
global $module_preload;
global $module_settings;
global $module_prefs;
global $session;
//collect the modules who attach to these hooks.
$sql =
"SELECT
$Pmodule_hooks.modulename,
$Pmodule_hooks.location,
$Pmodule_hooks.function,
$Pmodule_hooks.whenactive
FROM
$Pmodule_hooks
INNER JOIN
$Pmodules
ON $Pmodules.modulename = $Pmodule_hooks.modulename
WHERE
active = 1
AND location IN ('".join("', '",$hooknames)."')
ORDER BY
$Pmodule_hooks.location,
$Pmodule_hooks.priority,
$Pmodule_hooks.modulename";
$result = db_query_cached($sql,"moduleprepare-".md5(join($hooknames)));
$modulenames = array();
while ($row = db_fetch_assoc($result)){
$modulenames[$row['modulename']] = $row['modulename'];
if (!isset($module_preload[$row['location']])) {
$module_preload[$row['location']] = array();
$modulehook_queries[$row['location']] = array();
}
//a little black magic trickery: formatting entries in
//$modulehook_queries the same way that db_query_cached
//returns query results.
array_push($modulehook_queries[$row['location']],$row);
$module_preload[$row['location']][$row['modulename']] = $row['function'];
}
//SQL IN() syntax for the modules involved here.
$modulelist = "'".join("', '",$modulenames)."'";
//Load the settings for the modules on these hooks.
$sql =
"SELECT
modulename,
setting,
value
FROM
$Pmodule_settings
WHERE
modulename IN ($modulelist)";
$result = db_query($sql);
while ($row = db_fetch_assoc($result)){
$module_settings[$row['modulename']][$row['setting']] = $row['value'];
}
//Load the current user's prefs for the modules on these hooks.
$sql =
"SELECT
modulename,
setting,
userid,
value
FROM
$Pmodule_userprefs
WHERE
modulename IN ($modulelist)
AND userid = ".(int)$session['user']['acctid'];
$result = db_query($sql);
while ($row = db_fetch_assoc($result)){
$module_prefs[$row['userid']][$row['modulename']][$row['setting']] = $row['value'];
}
return true;
}
/**
* An event that should be triggered
*
* @param string $hookname The name of the event to raise
* @param array $args Arguments that should be passed to the event handler
* @param bool $allowinactive Allow inactive modules
* @param bool $only Only this module?
* @return array The args modified by the event handlers
*/
$currenthook = "";
function modulehook($hookname, $args=false, $allowinactive=false, $only=false){
global $navsection, $mostrecentmodule;
global $blocked_modules, $block_all_modules, $unblocked_modules;
global $output, $session, $modulehook_queries;
global $currenthook;
$lasthook = $currenthook;
$currenthook = $hookname;
static $hookcomment = array();
if ($args===false) $args = array();
$active = "";
if (!$allowinactive) $active = " ". db_prefix("modules") .".active=1 AND";
if (!is_array($args)){
$where = $mostrecentmodule;
if (!$where) {
global $SCRIPT_NAME;
$where = $SCRIPT_NAME;
}
debug("Args parameter to modulehook $hookname from $where is not an array.");
}
if ($session['user']['superuser'] & SU_DEBUG_OUTPUT && !isset($hookcomment[$hookname])){
rawoutput("<!--Module Hook: $hookname; allow inactive: ".($allowinactive?"true":"false")."; only this module: ".($only!==false?$only:"any module"));
if (!is_array($args)) {
$arg = $args . " (NOT AN ARRAY!)";
rawoutput(" arg: $arg");
} else {
reset($args);
while (list($key,$val)=each($args)){
$arg = $key." = ";
if (is_array($val)){
$arg.="array(".count($val).")";
}elseif (is_object($val)){
$arg.="object(".get_class($val).")";
}else{
$arg.=htmlentities(substr($val,0,25), ENT_COMPAT, getsetting("charset", "ISO-8859-1"));
}
rawoutput(" arg: $arg");
}
}
rawoutput(" -->");
$hookcomment[$hookname]=true;
}
if (isset($modulehook_queries[$hookname]) //This data was pre fetched in mass_module_prepare
&& $allowinactive == false //We only ever prefetch for active modules, if we're doing inactive, do the regular query.
){
$result = $modulehook_queries[$hookname];
}else{
$sql =
"SELECT
" . db_prefix("module_hooks") . ".modulename,
" . db_prefix("module_hooks") . ".location,
" . db_prefix("module_hooks") . ".function,
" . db_prefix("module_hooks") . ".whenactive
FROM
" . db_prefix("module_hooks") . "
INNER JOIN
" . db_prefix("modules") . "
ON " . db_prefix("modules") . ".modulename = " . db_prefix("module_hooks") . ".modulename
WHERE
$active
" . db_prefix("module_hooks") . ".location='$hookname'
ORDER BY
" . db_prefix("module_hooks") . ".priority,
" . db_prefix("module_hooks") . ".modulename";
$result = db_query_cached($sql,"hook-".$hookname);
}
// $args is an array passed by value and we take the output and pass it
// back through
// Try at least and fix up a bogus arg so it doesn't cause additional
// problems later.
if (!is_array($args)) {
$args = array('bogus_args'=>$args);
}
// Save off the mostrecent module since having that change can change
// behaviour especially if a module calls modulehooks itself or calls
// library functions which cause them to be called.
$mod = $mostrecentmodule;
while ($row = db_fetch_assoc($result)){
// If we are only running hooks for a specific module, skip all
// others.
if ($only !== false && $row['modulename']!=$only) continue;
// Skip any module invocations which should be blocked.
if (!array_key_exists($row['modulename'],$blocked_modules)){
$blocked_modules[$row['modulename']] = false;
}
if (!array_key_exists($row['modulename'],$unblocked_modules)){
$unblocked_modules[$row['modulename']] = false;
}
if (($block_all_modules || $blocked_modules[$row['modulename']]) &&
!$unblocked_modules[$row['modulename']]) {
continue;
}
if (injectmodule($row['modulename'], $allowinactive)) {
$oldnavsection = $navsection;
tlschema("module-{$row['modulename']}");
// Pass the args into the function and reassign them to the
// result of the function.
// Note: each module gets the previous module's modified return
// value if more than one hook here.
// Order of operations could become an issue, modules are called
// in alphabetical order by their module name (not display name).
// Test the condition code
if (!array_key_exists('whenactive',$row)) $row['whenactive'] = '';
$cond = trim($row['whenactive']);
if ($cond == "" || module_condition($cond) == true) {
// call the module's hook code
$outputbeforehook = $output;
$output="";
/*******************************************************/
$starttime = getmicrotime();
/*******************************************************/
if (function_exists($row['function'])) {
$res = $row['function']($hookname, $args);
} else {
trigger_error("Unknown function {$row['function']} for hoookname $hookname in module {$row['module']}.", E_USER_WARNING);
}
/*******************************************************/
$endtime = getmicrotime();
if (($endtime - $starttime >= 1.00 && ($session['user']['superuser'] & SU_DEBUG_OUTPUT))){
debug("Slow Hook (".round($endtime-$starttime,2)."s): $hookname - {$row['modulename']}`n");
}
/*******************************************************/
$outputafterhook = $output;
$output=$outputbeforehook;
// test to see if we had any output and if the module allows
// us to collapse it
$testout = trim(sanitize_html($outputafterhook));
if (!is_array($res)) {
trigger_error("<b>{$row['function']}</b> did not return an array in the module <b>{$row['modulename']}</b> for hook <b>$hookname</b>.",E_USER_WARNING);
$res = $args;
}
if ($testout >"" &&
$hookname!="collapse{" &&
$hookname!="}collapse" &&
$hookname!="collapse-nav{" &&
$hookname!="}collapse-nav" &&
!array_key_exists('nocollapse',$res)) {
//restore the original output's reference
modulehook("collapse{",
array("name"=>'a-'.$row['modulename']));
$output .= $outputafterhook;
modulehook("}collapse");
} else {
$output .= $outputafterhook;
}
// Clear the collapse flag
unset($res['nocollapse']);
//handle return arguments.
if (is_array($res)) $args = $res;
}
//revert the translation namespace
tlschema();
//revert nav section after we're done here.
$navsection = $oldnavsection;
}
}
$mostrecentmodule=$mod;
$currenthook = $lasthook;
// And hand them back so they can be used.
return $args;
}
$module_settings = array();
function get_all_module_settings($module=false){
//returns an associative array of all the settings for the given module
global $module_settings,$mostrecentmodule;
if ($module === false) $module = $mostrecentmodule;
load_module_settings($module);
return $module_settings[$module];
}
function get_module_setting($name,$module=false){
global $module_settings,$mostrecentmodule;
if ($module === false) $module = $mostrecentmodule;
load_module_settings($module);
if (isset($module_settings[$module][$name])) {
return $module_settings[$module][$name];
}else{
$info = get_module_info($module);
if (isset($info['settings'][$name])){
if (is_array($info['settings'][$name])) {
$v = $info['settings'][$name][0];
$x = explode("|", $v);
} else {
$x = explode("|",$info['settings'][$name]);
}
if (isset($x[1])){
return $x[1];
}
}
return NULL;
}
}
function set_module_setting($name,$value,$module=false){
if ($name == "showFormTabIndex") return true;
global $module_settings,$mostrecentmodule;
if ($module === false) $module = $mostrecentmodule;
load_module_settings($module);
if (isset($module_settings[$module][$name])){
$sql = "UPDATE " . db_prefix("module_settings") . " SET value='".addslashes($value)."' WHERE modulename='$module' AND setting='".addslashes($name)."'";
db_query($sql);
}else{
$sql = "INSERT INTO " . db_prefix("module_settings") . " (modulename,setting,value) VALUES ('$module','".addslashes($name)."','".addslashes($value)."')";
db_query($sql);
}
invalidatedatacache("modulesettings-$module");
$module_settings[$module][$name] = $value;
}
function increment_module_setting($name, $value=1, $module=false){
global $module_settings,$mostrecentmodule;
$value = (float)$value;
if ($module === false) $module = $mostrecentmodule;
load_module_settings($module);
if (isset($module_settings[$module][$name])){
$sql = "UPDATE " . db_prefix("module_settings") . " SET value=value+$value WHERE modulename='$module' AND setting='".addslashes($name)."'";
db_query($sql);
}else{
$sql = "INSERT INTO " . db_prefix("module_settings") . " (modulename,setting,value) VALUES ('$module','".addslashes($name)."','".addslashes($value)."')";
db_query($sql);
}
invalidatedatacache("modulesettings-$module");
$module_settings[$module][$name] += $value;
}
function clear_module_settings($module=false){
global $module_settings,$mostrecentmodule;
if ($module === false) $module = $mostrecentmodule;
if (isset($module_settings[$module])){
debug("Deleted module settings cache for $module.");
unset($module_settings[$module]);
invalidatedatacache("modulesettings-$module");
}
}
function load_module_settings($module){
global $module_settings;
if (!isset($module_settings[$module])){
$module_settings[$module] = array();
$sql = "SELECT * FROM " . db_prefix("module_settings") . " WHERE modulename='$module'";
$result = db_query_cached($sql,"modulesettings-$module");
while ($row = db_fetch_assoc($result)){
$module_settings[$module][$row['setting']] = $row['value'];
}//end while
}//end if
}//end function
function module_delete_objprefs($objtype, $objid)
{
$sql = "DELETE FROM " . db_prefix("module_objprefs") . " WHERE objtype='$objtype' AND objid='$objid'";
db_query($sql);
massinvalidate("objpref-$objtype-$objid");
}
function get_module_objpref($type, $objid, $name, $module=false){
global $mostrecentmodule;
if ($module === false) $module = $mostrecentmodule;
$sql = "SELECT value FROM ".db_prefix("module_objprefs")." WHERE modulename='$module' AND objtype='$type' AND setting='".addslashes($name)."' AND objid='$objid' ";
$result = db_query_cached($sql, "objpref-$type-$objid-$name-$module", 86400);
if (db_num_rows($result)>0){
$row = db_fetch_assoc($result);
return $row['value'];
}
//we couldn't find this elsewhere, load the default value if it exists.
$info = get_module_info($module);
if (isset($info['prefs-'.$type][$name])){
if (is_array($info['prefs-'.$type][$name])) {
$v = $info['prefs-'.$type][$name][0];
$x = explode("|", $v);
} else {
$x = explode("|",$info['prefs-'.$type][$name]);
}
if (isset($x[1])){
set_module_objpref($type,$objid,$name,$x[1],$module);
return $x[1];
}
}
return NULL;
}
function set_module_objpref($objtype,$objid,$name,$value,$module=false){
global $mostrecentmodule;
if ($module === false) $module = $mostrecentmodule;
// Delete the old version and insert the new
$sql = "REPLACE INTO " . db_prefix("module_objprefs") . "(modulename,objtype,setting,objid,value) VALUES ('$module', '$objtype', '$name', '$objid', '".addslashes($value)."')";
db_query($sql);
invalidatedatacache("objpref-$objtype-$objid-$name-$module");
}
function increment_module_objpref($objtype,$objid,$name,$value=1,$module=false) {
global $mostrecentmodule;
$value = (float)$value;
if ($module === false) $module = $mostrecentmodule;
$sql = "UPDATE " . db_prefix("module_objprefs") . " SET value=value+$value WHERE modulename='$module' AND setting='".addslashes($name)."' AND objtype='".addslashes($objtype)."' AND objid=$objid;";
$result= db_query($sql);
if (db_affected_rows($result)==0){
//if the update did not do anything, insert the row
$sql = "INSERT INTO " . db_prefix("module_objprefs") . "(modulename,objtype,setting,objid,value) VALUES ('$module', '$objtype', '$name', '$objid', '".addslashes($value)."')";
db_query($sql);
}
invalidatedatacache("objpref-$objtype-$objid-$name-$module");
}
function module_delete_userprefs($user){
$sql = "DELETE FROM " . db_prefix("module_userprefs") . " WHERE userid='$user'";
db_query($sql);
}
$module_prefs=array();
function get_all_module_prefs($module=false,$user=false){
global $module_prefs,$mostrecentmodule,$session;
if ($module === false) $module = $mostrecentmodule;
if ($user === false) $user = $session['user']['acctid'];
load_module_prefs($module,$user);
return $module_prefs[$user][$module];
}
function get_module_pref($name,$module=false,$user=false){
global $module_prefs,$mostrecentmodule,$session;
if ($module === false) $module = $mostrecentmodule;
if ($user===false) {
if(isset($session['user']['loggedin']) && $session['user']['loggedin']) $user = $session['user']['acctid'];
else $user = 0;
}
if (isset($module_prefs[$user][$module][$name])) {
return $module_prefs[$user][$module][$name];
}
//load here, not before
load_module_prefs($module,$user);
//check if *now* it's loaded
if (isset($module_prefs[$user][$module][$name])) {
return $module_prefs[$user][$module][$name];
}
if (!is_module_active($module)) return NULL;
//we couldn't find this elsewhere, load the default value if it exists.
$info = get_module_info($module);
if (isset($info['prefs'][$name])){
if (is_array($info['prefs'][$name])) {
$v = $info['prefs'][$name][0];
$x = explode("|", $v);
} else {
$x = explode("|",$info['prefs'][$name]);
}
if (isset($x[1])){
set_module_pref($name,$x[1],$module,$user);
return $x[1];
}
}
return NULL;
}
function set_module_pref($name,$value,$module=false,$user=false){
global $module_prefs,$mostrecentmodule,$session;
if ($module === false) $module = $mostrecentmodule;
if ($user === false) $uid=$session['user']['acctid'];
else $uid = $user;
load_module_prefs($module, $uid);
//don't write to the DB if the user isn't logged in.
if (!$session['user']['loggedin'] && !$user) {
// We do need to save to the loaded copy here however
$module_prefs[$uid][$module][$name] = $value;
return;
}
if (isset($module_prefs[$uid][$module][$name])){
$sql = "UPDATE " . db_prefix("module_userprefs") . " SET value='".addslashes($value)."' WHERE modulename='$module' AND setting='$name' AND userid='$uid'";
db_query($sql);
}else{
$sql = "INSERT INTO " . db_prefix("module_userprefs"). " (modulename,setting,userid,value) VALUES ('$module','$name','$uid','".addslashes($value)."')";
db_query($sql);
}
$module_prefs[$uid][$module][$name] = $value;
}
function increment_module_pref($name,$value=1,$module=false,$user=false){
global $module_prefs,$mostrecentmodule,$session;
$value = (float)$value;
if ($module === false) $module = $mostrecentmodule;
if ($user === false) $uid=$session['user']['acctid'];
else $uid = $user;
load_module_prefs($module, $uid);
//don't write to the DB if the user isn't logged in.
if (!$session['user']['loggedin'] && !$user) {
// We do need to save to the loaded copy here however
$module_prefs[$uid][$module][$name] += $value;
return;
}
if (isset($module_prefs[$uid][$module][$name])){
$sql = "UPDATE " . db_prefix("module_userprefs") . " SET value=value+$value WHERE modulename='$module' AND setting='$name' AND userid='$uid'";
db_query($sql);
$module_prefs[$uid][$module][$name] += $value;
}else{
$sql = "INSERT INTO " . db_prefix("module_userprefs"). " (modulename,setting,userid,value) VALUES ('$module','$name','$uid','".addslashes($value)."')";
db_query($sql);
$module_prefs[$uid][$module][$name] = $value;
}
}
function clear_module_pref($name,$module=false,$user=false){
global $module_prefs,$mostrecentmodule,$session;
if ($module === false) $module = $mostrecentmodule;
if ($user === false) $uid=$session['user']['acctid'];
else $uid = $user;
load_module_prefs($module, $uid);
//don't write to the DB if the user isn't logged in.
if (!$session['user']['loggedin'] && !$user) {
// We do need to trash the loaded copy here however
unset($module_prefs[$uid][$module][$name]);
return;
}
if (isset($module_prefs[$uid][$module][$name])){
$sql = "DELETE FROM " . db_prefix("module_userprefs") . " WHERE modulename='$module' AND setting='$name' AND userid='$uid'";
db_query($sql);
}
unset($module_prefs[$uid][$module][$name]);
}
function load_module_prefs($module, $user=false){
global $module_prefs,$session;
if ($user===false) $user = $session['user']['acctid'];
if (!isset($module_prefs[$user])) $module_prefs[$user] = array();
if (!isset($module_prefs[$user][$module])){
$module_prefs[$user][$module] = array();
$sql = "SELECT setting,value FROM " . db_prefix("module_userprefs") . " WHERE modulename='$module' AND userid='$user'";
$result = db_query($sql);
while ($row = db_fetch_assoc($result)){
$module_prefs[$user][$module][$row['setting']] = $row['value'];
}//end while
}//end if
}//end function
function get_module_info($shortname){
global $mostrecentmodule;
$moduleinfo = array();
// Save off the mostrecent module.
$mod = $mostrecentmodule;
if(injectmodule($shortname,true)) {
$fname = $shortname."_getmoduleinfo";
if (function_exists($fname)){
tlschema("module-$shortname");
$moduleinfo = $fname();
tlschema();
// Don't pick up this text unless we need it.
if (!isset($moduleinfo['name']) ||
!isset($moduleinfo['category']) ||
!isset($moduleinfo['author']) ||
!isset($moduleinfo['version'])) {
$ns = translate_inline("Not specified","common");
}
if (!isset($moduleinfo['name']))
$moduleinfo['name']="$ns ($shortname)";
if (!isset($moduleinfo['category']))
$moduleinfo['category']="$ns ($shortname)";
if (!isset($moduleinfo['author']))
$moduleinfo['author']="$ns ($shortname)";
if (!isset($moduleinfo['version']))
$moduleinfo['version']="0.0";
if (!isset($moduleinfo['download']))
$moduleinfo['download'] = "";
if (!isset($moduleinfo['description']))
$moduleinfo['description'] = "";
}
if (!is_array($moduleinfo) || count($moduleinfo)<2){
$mf = translate_inline("Missing function","common");
$moduleinfo = array(
"name"=>"$mf ({$shortname}_getmoduleinfo)",
"version"=>"0.0",
"author"=>"$mf ({$shortname}_getmoduleinfo)",
"category"=>"$mf ({$shortname}_getmoduleinfo)",
"download"=>"",
);
}
} else {
// This module couldn't be injected at all.
return array();
}
$mostrecentmodule = $mod;
if (!isset($moduleinfo['requires']))
$moduleinfo['requires'] = array();
return $moduleinfo;
}
function module_wipehooks() {
global $mostrecentmodule;
//lock the module hooks table.
$sql = "LOCK TABLES ".db_prefix("module_hooks")." WRITE";
db_query($sql);
//invalidate data caches for module hooks associated with this module.
$sql = "SELECT location FROM ".db_prefix("module_hooks")." WHERE modulename='$mostrecentmodule'";
$result = db_query($sql);
while ($row = db_fetch_assoc($result)){
invalidatedatacache("hook-".$row['location']);
}
invalidatedatacache("moduleprepare");
debug("Removing all hooks for $mostrecentmodule");
$sql = "DELETE FROM " . db_prefix("module_hooks"). " WHERE modulename='$mostrecentmodule'";
db_query($sql);
//unlock the module hooks table.
$sql = "UNLOCK TABLES";
db_query($sql);
$sql = "DELETE FROM " . db_prefix("module_event_hooks") . " WHERE modulename='$mostrecentmodule'";
db_query($sql);
}
function module_addeventhook($type, $chance){
global $mostrecentmodule;
debug("Adding an event hook on $type events for $mostrecentmodule");
$sql = "DELETE FROM " . db_prefix("module_event_hooks") . " WHERE modulename='$mostrecentmodule' AND event_type='$type'";
db_query($sql);
$sql = "INSERT INTO " . db_prefix("module_event_hooks") . " (event_type,modulename,event_chance) VALUES ('$type', '$mostrecentmodule','".addslashes($chance)."')";
db_query($sql);
invalidatedatacache("event-".$type);
}
function module_drophook($hookname,$functioncall=false){
global $mostrecentmodule;
if ($functioncall===false)
$functioncall=$mostrecentmodule."_dohook";
$sql = "DELETE FROM " . db_prefix("module_hooks") . " WHERE modulename='$mostrecentmodule' AND location='".addslashes($hookname)."' AND function='".addslashes($functioncall)."'";
db_query($sql);
invalidatedatacache("hook-".$hookname);
invalidatedatacache("moduleprepare");
}
/**
* Called by modules to register themselves for a game module hook point, with default priority.
* Modules with identical priorities will execute alphabetically. Modules can only have one hook on a given hook name,
* even if they call this function multiple times, unless they specify different values for the functioncall argument.
*
* @param string $hookname The hook to receive a notification for
* @param string $functioncall The function that should be called, if not specified, use {modulename}_dohook() as the function
* @param string $whenactive An expression that should be evaluated before triggering the event, if not specified, none.
*/
function module_addhook($hookname,$functioncall=false,$whenactive=false){
module_addhook_priority($hookname,50,$functioncall,$whenactive);
}
/**
* Called by modules to register themselves for a game module hook point, with a given priority -- lower numbers execute first.
* Modules with identical priorities will execute alphabetically. Modules can only have one hook on a given hook name,
* even if they call this function multiple times, unless they specify different values for the functioncall argument.
*
* @param string $hookname The hook to receive a notification for
* @param integer $priority The priority for this hooking -- lower numbers execute first. < 50 means earlier-than-normal execution, > 50 means later than normal execution. Priority only affects execution order compared to other events registered on the same hook, all events on a given hook will execute before the game resumes execution.
* @param string $functioncall The function that should be called, if not specified, use {modulename}_dohook() as the function
* @param string $whenactive An expression that should be evaluated before triggering the event, if not specified, none.
*/
function module_addhook_priority($hookname,$priority=50,$functioncall=false,$whenactive=false){
global $mostrecentmodule;
module_drophook($hookname,$functioncall);
if ($functioncall===false) $functioncall=$mostrecentmodule."_dohook";
if ($whenactive === false) $whenactive = '';
debug("Adding a hook at $hookname for $mostrecentmodule to $functioncall which is active on condition '$whenactive'");
//we want to do a replace in case there's any garbage left in this table which might block new clean data from going in.
//normally that won't be the case, and so this doesn't have any performance implications.
$sql = "REPLACE INTO " . db_prefix("module_hooks") . " (modulename,location,function,whenactive,priority) VALUES ('$mostrecentmodule','".addslashes($hookname)."','".addslashes($functioncall)."','".addslashes($whenactive)."','".addslashes($priority)."')";
db_query($sql);
invalidatedatacache("hook-".$hookname);
invalidatedatacache("moduleprepare");
}
function module_sem_acquire(){
//DANGER DANGER WILL ROBINSON
//use of this function can be EXTREMELY DANGEROUS
//If there is ANY WAY you can avoid using it, I strongly recommend you
//do so. That said, I recognize that at times you need to acquire a
//semaphore so I'll provide a function to accomplish it.
//PLEASE make sure you call module_sem_release() AS SOON AS YOU CAN.
//Since Semaphore support in PHP is a compile time option that is off
//by default, I'll rely on MySQL's semaphore on table lock. Note this
//is NOT as efficient as the PHP semaphore because it blocks other
//things too.
//If someone is feeling industrious, a smart function that uses the PHP
//semaphore when available, and otherwise call the MySQL LOCK TABLES
//code would be sincerely appreciated.
$sql = "LOCK TABLES " . db_prefix("module_settings") . " WRITE";
db_query($sql);
}
function module_sem_release(){
//please see warnings in module_sem_acquire()
$sql = "UNLOCK TABLES";
db_query($sql);
}
function module_collect_events($type, $allowinactive=false)
{
global $session, $playermount;
global $blocked_modules, $block_all_modules, $unblocked_modules;
$active = "";
$events = array();
if (!$allowinactive) $active = " active=1 AND";
$sql = "SELECT " . db_prefix("module_event_hooks") . ".* FROM " . db_prefix("module_event_hooks") . " INNER JOIN " . db_prefix("modules") . " ON ". db_prefix("modules") . ".modulename = " . db_prefix("module_event_hooks") . ".modulename WHERE $active event_type='$type' ORDER BY RAND(".e_rand().")";
$result = db_query_cached($sql,"event-".$type);
while ($row = db_fetch_assoc($result)){
// The event_chance bit needs to return a value, but it can do that
// in any way it wants, and can have if/then or other logical
// structures, so we cannot just force the 'return' syntax unlike
// with buffs.
ob_start();
$chance = eval($row['event_chance'].";");
$err = ob_get_contents();
ob_end_clean();
if ($err > ""){
debug(array("error"=>$err,"Eval code"=>$row['event_chance']));
}
if ($chance < 0) $chance = 0;
if ($chance > 100) $chance = 100;
if (($block_all_modules || array_key_exists($row['modulename'],$blocked_modules) && $blocked_modules[$row['modulename']]) &&
(!array_key_exists($row['modulename'],$unblocked_modules) || !$unblocked_modules[$row['modulename']])) {
$chance = 0;
}
$events[] = array('modulename'=>$row['modulename'],
'rawchance' => $chance);
}
// Now, normalize all of the event chances
$sum = 0;
reset($events);
foreach($events as $event) {
$sum += $event['rawchance'];
}
reset($events);
foreach($events as $index=>$event) {
if ($sum == 0) {
$events[$index]['normchance'] = 0;
} else {
$events[$index]['normchance'] =
round($event['rawchance']/$sum*100,3);
// If an event requests 1% chance, don't give them more!
if ($events[$index]['normchance'] > $event['rawchance'])
$events[$index]['normchance'] = $event['rawchance'];
}
}
return modulehook("collect-events", $events);
}
function module_events($eventtype, $basechance, $baseLink = false) {
if ($baseLink === false){
global $PHP_SELF;
$baseLink = substr($PHP_SELF,strrpos($PHP_SELF,"/")+1)."?";
}else{
//debug("Base link was specified as $baseLink");
//debug(debug_backtrace());
}
if (e_rand(1, 100) <= $basechance) {
global $PHP_SELF;
$events = module_collect_events($eventtype);
$chance = r_rand(1, 100);
reset($events);
$sum = 0;
foreach($events as $event) {
if ($event['rawchance'] == 0) {
continue;
}
if ($chance > $sum && $chance <= $sum + $event['normchance']) {
$_POST['i_am_a_hack'] = 'true';
tlschema("events");
output("`^`c`bSomething Special!`c`b`0");
tlschema();
$op = httpget('op');
httpset('op', "");
module_do_event($eventtype, $event['modulename'], false, $baseLink);
httpset('op', $op);
return 1;
}
$sum += $event['normchance'];
}
}
return 0;
}
function module_do_event($type, $module, $allowinactive=false, $baseLink=false)
{
global $navsection;
if ($baseLink === false){
global $PHP_SELF;
$baseLink = substr($PHP_SELF,strrpos($PHP_SELF,"/")+1)."?";
}else{
//debug("Base link was specified as $baseLink");
//debug(debug_backtrace());
}
// Save off the mostrecent module since having that change can change
// behaviour especially if a module calls modulehooks itself or calls
// library functions which cause them to be called.
if (!isset($mostrecentmodule)) $mostrecentmodule = "";
$mod = $mostrecentmodule;
$_POST['i_am_a_hack'] = 'true';
if(injectmodule($module, $allowinactive)) {
$oldnavsection = $navsection;
tlschema("module-$module");
$fname = $module."_runevent";
$fname($type,$baseLink);
tlschema();
//hook into the running event, but only in *this* running event, not in all
modulehook("runevent_$module", array("type"=>$type, "baselink"=>$baseLink, "get"=>httpallget(), "post"=>httpallpost()));
//revert nav section after we're done here.
$navsection = $oldnavsection;
}
$mostrecentmodule=$mod;
}
function event_sort($a, $b)
{
return strcmp($a['modulename'], $b['modulename']);
}
function module_display_events($eventtype, $forcescript=false) {
global $PHP_SELF, $session;
if (!($session['user']['superuser'] & SU_DEVELOPER)) return;
if ($forcescript === false)
$script = substr($PHP_SELF,strrpos($PHP_SELF,"/")+1);
else
$script = $forcescript;
$events = module_collect_events($eventtype,true);
if (!is_array($events) || count($events) == 0) return;
usort($events, "event_sort");
tlschema("events");
output("`n`nSpecial event triggers:`n");
$name = translate_inline("Name");
$rchance = translate_inline("Raw Chance");
$nchance = translate_inline("Normalized Chance");
rawoutput("<table cellspacing='1' cellpadding='2' border='0' bgcolor='#999999'>");
rawoutput("<tr class='trhead'>");
rawoutput("<td>$name</td><td>$rchance</td><td>nchance</td>");
rawoutput("</tr>");
$i = 0;
foreach($events as $event) {
// Each event is an associative array of 'modulename',
// 'rawchance' and 'normchance'
rawoutput("<tr class='" . ($i%2==0?"trdark":"trlight")."'>");
$i++;
if ($event['modulename']) {
$link = "module-{$event['modulename']}";
$name = $event['modulename'];
}
$rlink = "$script?eventhandler=$link";
$rlink = str_replace("?&","?",$rlink);
$first = strpos($rlink, "?");
$rl1 = substr($rlink, 0, $first+1);
$rl2 = substr($rlink, $first+1);
$rl2 = str_replace("?", "&", $rl2);
$rlink = $rl1 . $rl2;
rawoutput("<td><a href='$rlink'>$name</a></td>");
addnav("", "$rlink");
rawoutput("<td>{$event['rawchance']}</td>");
rawoutput("<td>{$event['normchance']}</td>");
rawoutput("</tr>");
}
rawoutput("</table>");
}
function module_editor_navs($like, $linkprefix)
{
$sql = "SELECT formalname,modulename,active,category FROM " . db_prefix("modules") . " WHERE infokeys LIKE '%|$like|%' ORDER BY category,formalname";
$result = db_query($sql);
$curcat = "";
while($row = db_fetch_assoc($result)) {
if ($curcat != $row['category']) {
$curcat = $row['category'];
addnav(array("%s Modules",$curcat));
}
//I really think we should give keyboard shortcuts even if they're
//susceptible to change (which only happens here when the admin changes
//modules around). This annoys me every single time I come in to this page.
addnav_notl(($row['active'] ? "" : "`)") . $row['formalname']."`0",
$linkprefix . $row['modulename']);
}
}
function module_objpref_edit($type, $module, $id)
{
$info = get_module_info($module);
if (count($info['prefs-'.$type]) > 0) {
$data = array();
$msettings = array();
while(list($key, $val) = each($info['prefs-'.$type])) {
if (is_array($val)) {
$v = $val[0];
$x = explode("|", $v);
$val[0] = $x[0];
$x[0] = $val;
} else {
$x = explode("|", $val);
}
$msettings[$key]=$x[0];
// Set up default
if (isset($x[1])) $data[$key]=$x[1];
}
$sql = "SELECT setting, value FROM " . db_prefix("module_objprefs") . " WHERE modulename='$module' AND objtype='$type' AND objid='$id'";
$result = db_query($sql);
while($row = db_fetch_assoc($result)) {
$data[$row['setting']] = $row['value'];
}
tlschema("module-$module");
showform($msettings, $data);
tlschema();
}
}
function module_compare_versions($a,$b){
//this function returns -1 when $a < $b, 1 when $a > $b, and 0 when $a == $b
//insert alternate version detection and comparison algorithms here.
//default case, typecast as float
$a = (float)$a;
$b = (float)$b;
return ($a < $b ? -1 : ($a > $b ? 1 : 0) );
}
function activate_module($module){
if (!is_module_installed($module)){
if (!install_module($module)){
return false;
}
}
$sql = "UPDATE " . db_prefix("modules") . " SET active=1 WHERE modulename='$module'";
db_query($sql);
invalidatedatacache("inject-$module");
massinvalidate("moduleprepare");
if (db_affected_rows() <= 0){
return false;
}else{
return true;
}
}
function deactivate_module($module){
if (!is_module_installed($module)){
if (!install_module($module)){
return false;
}else{
//modules that weren't installed go to deactivated state by default in install_module
return true;
}
}
$sql = "UPDATE " . db_prefix("modules") . " SET active=0 WHERE modulename='$module'";
db_query($sql);
invalidatedatacache("inject-$module");
massinvalidate("moduleprepare");
if (db_affected_rows() <= 0){
return false;
}else{
return true;
}
}
function uninstall_module($module){
if (injectmodule($module,true)) {
$fname = $module."_uninstall";
output("Running module uninstall script`n");
tlschema("module-{$module}");
$fname();
tlschema();
output("Deleting module entry`n");
$sql = "DELETE FROM " . db_prefix("modules") .
" WHERE modulename='$module'";
db_query($sql);
output("Deleting module hooks`n");
module_wipehooks();
output("Deleting module settings`n");
$sql = "DELETE FROM " . db_prefix("module_settings") .
" WHERE modulename='$module'";
db_query($sql);
invalidatedatacache("modulesettings-$module");
output("Deleting module user prefs`n");
$sql = "DELETE FROM " . db_prefix("module_userprefs") .
" WHERE modulename='$module'";
db_query($sql);
output("Deleting module object prefs`n");
$sql = "DELETE FROM " . db_prefix("module_objprefs") .
" WHERE modulename='$module'";
db_query($sql);
invalidatedatacache("inject-$module");
massinvalidate("moduleprepare");
return true;
} else {
return false;
}
}
function install_module($module, $force=true){
global $mostrecentmodule, $session;
$name = $session['user']['name'];
if (!$name) $name = '`@System`0';
require_once("lib/sanitize.php");
if (modulename_sanitize($module)!=$module){
output("Error, module file names can only contain alpha numeric characters and underscores before the trailing .php`n`nGood module names include 'testmodule.php', 'joesmodule2.php', while bad module names include, 'test.module.php' or 'joes module.php'`n");
return false;
}else{
// If we are forcing an install, then whack the old version.
if ($force) {
$sql = "DELETE FROM " . db_prefix("modules") . " WHERE modulename='$module'";
db_query($sql);
}
// We want to do the inject so that it auto-upgrades any installed
// version correctly.
if (injectmodule($module,true)) {
// If we're not forcing and this is already installed, we are done
if (!$force && is_module_installed($module))
return true;
$info = get_module_info($module);
//check installation requirements
if (!module_check_requirements($info['requires'])){
output("`\$Module could not installed -- it did not meet its prerequisites.`n");
return false;
}else{
$keys = "|".join(array_keys($info), "|")."|";
$sql = "INSERT INTO " . db_prefix("modules") . " (modulename,formalname,moduleauthor,active,filename,installdate,installedby,category,infokeys,version,download,description) VALUES ('$mostrecentmodule','".addslashes($info['name'])."','".addslashes($info['author'])."',0,'{$mostrecentmodule}.php','".date("Y-m-d H:i:s")."','".addslashes($name)."','".addslashes($info['category'])."','$keys','".addslashes($info['version'])."','".addslashes($info['download'])."', '".addslashes($info['description'])."')";
db_query($sql);
$fname = $mostrecentmodule."_install";
if (isset($info['settings']) && count($info['settings']) > 0) {
foreach($info['settings'] as $key=>$val){
if (is_array($val)) {
$x = explode("|", $val[0]);
} else {
$x = explode("|",$val);
}
if (isset($x[1])){
set_module_setting($key,$x[1]);
debug("Setting $key to default {$x[1]}");
}
}
}
if ($fname() === false) {
return false;
}
output("`^Module installed. It is not yet active.`n");
invalidatedatacache("inject-$mostrecentmodule");
massinvalidate("moduleprepare");
return true;
}
} else {
output("`\$Module could not be injected.");
output("Module not installed.");
output("This is probably due to the module file having a parse error or not existing in the filesystem.`n");
return false;
}
}
}
/**
* Evaluates a PHP Expression
*
* @param string $condition The PHP condition to evaluate
* @return bool The result of the evaluated expression
*/
function module_condition($condition) {
global $session;
$result = eval($condition);
return (bool)$result;
}
function get_module_install_status(){
// Collect the names of all installed modules.
$seenmodules = array();
$seencats = array();
$sql = "SELECT modulename,category FROM " . db_prefix("modules");
$result = @db_query($sql);
if ($result !== false){
while ($row = db_fetch_assoc($result)) {
$seenmodules[$row['modulename'].".php"] = true;
if (!array_key_exists($row['category'], $seencats))
$seencats[$row['category']] = 1;
else
$seencats[$row['category']]++;
}
}
$uninstmodules = array();
if ($handle = opendir("modules")){
$ucount=0;
while (false !== ($file = readdir($handle))){
if ($file[0] == ".") continue;
if (preg_match("/\\.php$/", $file) && !isset($seenmodules[$file])){
$ucount++;
$uninstmodules[] = substr($file, 0, strlen($file)-4);
}
}
}
closedir($handle);
sort($uninstmodules);
return array('installedcategories'=>$seencats,'installedmodules'=>$seenmodules,'uninstalledmodules'=>$uninstmodules, 'uninstcount'=>$ucount);
}
function get_racename($thisuser=true) {
if ($thisuser === true) {
global $session;
return translate_inline($session['user']['race'],"race");
} else {
return translate_inline($thisuser,"race");
}
}
function module_delete_oldvalues($table,$key) {
require_once 'lib/gamelog.php';
$total = 0;
$res = db_query("SELECT modulename FROM ".db_prefix('modules')." WHERE infokeys LIKE '%|$key|%'");
while ($row = db_fetch_assoc($res)) {
$mod = $row['modulename'];
require_once "modules/{$mod}.php";
$func = $mod."_getmoduleinfo";
$info = $func();
$keys = array_filter(array_keys($info[$key]), "module_pref_filter");
$keys = array_map("addslashes", $keys);
$keys = implode("','", $keys);
if ($keys) db_query("DELETE FROM ".db_prefix($table)." WHERE modulename='$mod' AND setting NOT IN ('$keys')");
$total += db_affected_rows();
}
gamelog("Cleaned up $total old values in $table that don't exist anymore", 'maintenance');
}
function module_pref_filter($a){
return !is_numeric($a);
}
?>