diff --git a/CHANGELOG b/CHANGELOG index 124d4090661afae2ccd51f3c473b9fcbefa8cd95..753132c35e747e4e50a05dcadd50f6e8b452c373 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,8 @@ Drupal x.x.x, xxxx-xx-xx ------------------------ - added support for the MetaWeblog API and MoveableType extensions. +- performance: + * improved module loading when serving cached pages. - theme system: * made all theme functions start with 'theme_'. * made all theme functions return their output. diff --git a/cron.php b/cron.php index a65351ae106cd51605890eff1fb5fa1bea63994a..d7e17082017f1bd4eccfcc039e4603a84a6ab4dd 100644 --- a/cron.php +++ b/cron.php @@ -1,6 +1,7 @@ <?php // $Id$ +include_once "includes/bootstrap.inc"; include_once "includes/common.inc"; /* diff --git a/database/database.mssql b/database/database.mssql index 353f4ecce8fb3025b355fdf0571a30fb5acefc20..d944effa5f3bb65561a8d744360d322b647bd344 100644 --- a/database/database.mssql +++ b/database/database.mssql @@ -299,6 +299,7 @@ CREATE TABLE [dbo].[system] ( [type] [varchar] (255) NOT NULL , [description] [varchar] (255) NOT NULL , [status] [int] NOT NULL + [bootstrap] [int] NOT NULL ) ON [PRIMARY] GO diff --git a/database/database.mysql b/database/database.mysql index 19c31faddc9ed964b4dc73ff45e157a5178244fb..75b6d0ff2d72a6fc0e882b9479dbc34d16b54af8 100644 --- a/database/database.mysql +++ b/database/database.mysql @@ -426,6 +426,7 @@ CREATE TABLE system ( type varchar(255) NOT NULL default '', description varchar(255) NOT NULL default '', status int(2) NOT NULL default '0', + bootstrap int(2) NOT NULL default '0', PRIMARY KEY (filename) ) TYPE=MyISAM; diff --git a/database/database.pgsql b/database/database.pgsql index ec541ccb09168a8351f8c6ce64f23f34efe0a8e0..be0193d3659c6fbabcccc687f0a1541aac174e88 100644 --- a/database/database.pgsql +++ b/database/database.pgsql @@ -422,6 +422,7 @@ CREATE TABLE system ( type varchar(255) NOT NULL default '', description varchar(255) NOT NULL default '', status integer NOT NULL default '0', + bootstrap integer NOT NULL default '0', PRIMARY KEY (filename) ); diff --git a/error.php b/error.php index 0433593d595bf7847af97a292876370e5f575332..072f7b1265cba0df75fa19500af38b93dbc7a98d 100644 --- a/error.php +++ b/error.php @@ -1,6 +1,7 @@ <?php // $Id$ +include_once "includes/bootstrap.inc"; include_once "includes/common.inc"; $errors = array( diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc new file mode 100644 index 0000000000000000000000000000000000000000..c13f1b8b7d23c63a32b7ed084614ebc8309e8e6d --- /dev/null +++ b/includes/bootstrap.inc @@ -0,0 +1,226 @@ +<?php + +function conf_init() { + + /* + ** Try finding a matching configuration file by stripping the website's + ** URI from left to right. If no configuration file is found, return a + ** default value 'conf'. + */ + + $uri = $_SERVER["PHP_SELF"]; + + $file = strtolower(strtr($_SERVER["HTTP_HOST"] . substr($uri, 0, strrpos($uri, "/")), "/:", "..")); + + while (strlen($file) > 4) { + if (file_exists("includes/$file.php")) { + return $file; + } + else { + $file = substr($file, strpos($file, ".") + 1); + } + } + + return "conf"; +} + +function variable_init($conf = array()) { + $result = db_query("SELECT * FROM {variable} "); + while ($variable = db_fetch_object($result)) { + if (!isset($conf[$variable->name])) { + $conf[$variable->name] = unserialize($variable->value); + } + } + + return $conf; +} + +function variable_get($name, $default) { + global $conf; + + return isset($conf[$name]) ? $conf[$name] : $default; +} + +function variable_set($name, $value) { + global $conf; + + db_query("DELETE FROM {variable} WHERE name = '%s'", $name); + db_query("INSERT INTO {variable} (name, value) VALUES ('%s', '%s')", $name, serialize($value)); + + $conf[$name] = $value; +} + +function variable_del($name) { + global $conf; + + db_query("DELETE FROM {variable} WHERE name = '%s'", $name); + + unset($conf[$name]); +} + +function cache_get($key) { + $cache = db_fetch_object(db_query("SELECT data, created FROM {cache} WHERE cid = '%s'", $key)); + return $cache->data ? $cache : 0; +} + +function cache_set($cid, $data, $expire = 0) { + db_query("UPDATE {cache} SET data = '%s', created = %d, expire = %d WHERE cid = '%s'", $data, time(), $expire, $cid); + if (!db_affected_rows()) { + db_query("INSERT INTO {cache} (cid, data, created, expire) VALUES('%s', '%s', %d, %d)", $cid, $data, time(), $expire); + } +} + +function cache_clear_all($cid = NULL) { + if (empty($cid)) { + db_query("DELETE FROM {cache} WHERE expire <> 0"); + } + else { + db_query("DELETE FROM {cache} WHERE cid = '%s'", $cid); + } +} + +function page_set_cache() { + global $user; + + if (!$user->uid && $_SERVER["REQUEST_METHOD"] == "GET") { + if ($data = ob_get_contents()) { + cache_set(request_uri(), $data, 1); + } + } +} + +function page_get_cache() { + global $user; + + $cache = NULL; + + if (!$user->uid && $_SERVER["REQUEST_METHOD"] == "GET") { + $cache = cache_get(request_uri()); + + if (empty($cache)) { + ob_start(); + } + } + + return $cache; +} + +function drupal_page_header() { + + if (variable_get("dev_timer", 0)) { + timer_start(); + } + + if (variable_get("cache", 0)) { + if ($cache = page_get_cache()) { + + // Set default values: + $date = gmdate("D, d M Y H:i:s", $cache->created) ." GMT"; + $etag = '"'. md5($date) .'"'; + + // Check http headers: + $modified_since = isset($_SERVER["HTTP_IF_MODIFIED_SINCE"]) ? $_SERVER["HTTP_IF_MODIFIED_SINCE"] == $date : NULL; + $none_match = isset($_SERVER["HTTP_IF_NONE_MATCH"]) ? $_SERVER["HTTP_IF_NONE_MATCH"] == $etag : NULL; + + // The type checking here is very important, be careful when changing entries. + if (($modified_since !== NULL || $none_match !== NULL) && $modified_since !== false && $none_match !== false) { + header("HTTP/1.0 304 Not Modified"); + exit(); + } + + // Send appropriate response: + header("Last-Modified: $date"); + header("ETag: $etag"); + print $cache->data; + + /* + ** call all init() and exit() hooks without including all modules + ** only use those hooks for critical operations + */ + foreach (module_list(0, 1) as $module) { + if (is_array($module) && $module['bootstrap']) { + include_once $module['filename']; + foreach (bootstrap_hooks() as $hook) { + module_invoke($module['name'], $hook); + } + } + } + exit(); + } + } +} + +// critical hooks called even when serving a cached page +function bootstrap_hooks() { + return array('init', 'exit'); +} + +function referer_uri() { + + if (isset($_SERVER["HTTP_REFERER"])) { + $uri = $_SERVER["HTTP_REFERER"]; + + return check_url($uri); + } +} + +function arg($index) { + + static $arguments; + + if (empty($arguments)) { + $arguments = explode("/", $_GET["q"]); + } + + return $arguments[$index]; +} + +function check_query($text) { + return addslashes($text); +} + +function check_url($uri) { + $uri = htmlspecialchars($uri, ENT_QUOTES); + + /* + ** We replace ( and ) with their entity equivalents to prevent XSS + ** attacks. + */ + + $uri = strtr($uri, array("(" => "&040;", ")" => "&041;")); + + return $uri; +} + +function request_uri() { + /* + ** Since request_uri() is only available on Apache, we generate + ** equivalent using other environment vars. + */ + + if (isset($_SERVER["REQUEST_URI"])) { + $uri = $_SERVER["REQUEST_URI"]; + } + else { + $uri = $_SERVER["PHP_SELF"] ."?". $_SERVER["QUERY_STRING"]; + } + + return check_url($uri); +} +function timer_start() { + global $timer; + list($usec, $sec) = explode(" ", microtime()); + $timer = (float)$usec + (float)$sec; +} + +unset($conf); +$config = conf_init(); + +include_once "includes/$config.php"; +include_once "includes/database.inc"; +include_once "includes/session.inc"; +include_once "includes/module.inc"; + +// initialize configuration variables, using values from conf.php if available: +$conf = variable_init(isset($conf) ? $conf : array()); +?> diff --git a/includes/common.inc b/includes/common.inc index 5c4835b197b62c814e5c5b2caead47bc302dbf5b..2a67db247411cfacc64d9c5578a4769e62b7b7c0 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -1,30 +1,6 @@ <?php // $Id$ -function conf_init() { - - /* - ** Try finding a matching configuration file by stripping the website's - ** URI from left to right. If no configuration file is found, return a - ** default value 'conf'. - */ - - $uri = $_SERVER["PHP_SELF"]; - - $file = strtolower(strtr($_SERVER["HTTP_HOST"] . substr($uri, 0, strrpos($uri, "/")), "/:", "..")); - - while (strlen($file) > 4) { - if (file_exists("includes/$file.php")) { - return $file; - } - else { - $file = substr($file, strpos($file, ".") + 1); - } - } - - return "conf"; -} - /** * Build the alias/path array */ @@ -100,17 +76,6 @@ function fix_gpc_magic() { $fixed = true; } -function arg($index) { - - static $arguments; - - if (empty($arguments)) { - $arguments = explode("/", $_GET["q"]); - } - - return $arguments[$index]; -} - function array2object($node) { if (is_array($node)) { @@ -139,31 +104,6 @@ function object2array($node) { return $array; } -function referer_uri() { - - if (isset($_SERVER["HTTP_REFERER"])) { - $uri = $_SERVER["HTTP_REFERER"]; - - return check_url($uri); - } -} - -function request_uri() { - /* - ** Since request_uri() is only available on Apache, we generate - ** equivalent using other environment vars. - */ - - if (isset($_SERVER["REQUEST_URI"])) { - $uri = $_SERVER["REQUEST_URI"]; - } - else { - $uri = $_SERVER["PHP_SELF"] ."?". $_SERVER["QUERY_STRING"]; - } - - return check_url($uri); -} - function message_access() { return t("You are not authorized to access this page."); } @@ -210,40 +150,6 @@ function t($string, $args = 0) { } } -function variable_init($conf = array()) { - $result = db_query("SELECT * FROM {variable} "); - while ($variable = db_fetch_object($result)) { - if (!isset($conf[$variable->name])) { - $conf[$variable->name] = unserialize($variable->value); - } - } - - return $conf; -} - -function variable_get($name, $default) { - global $conf; - - return isset($conf[$name]) ? $conf[$name] : $default; -} - -function variable_set($name, $value) { - global $conf; - - db_query("DELETE FROM {variable} WHERE name = '%s'", $name); - db_query("INSERT INTO {variable} (name, value) VALUES ('%s', '%s')", $name, serialize($value)); - - $conf[$name] = $value; -} - -function variable_del($name) { - global $conf; - - db_query("DELETE FROM {variable} WHERE name = '%s'", $name); - - unset($conf[$name]); -} - function drupal_specialchars($input, $quotes = ENT_NOQUOTES) { /* @@ -476,27 +382,10 @@ function valid_input_data($data) { return 1; } -function check_url($uri) { - $uri = htmlspecialchars($uri, ENT_QUOTES); - - /* - ** We replace ( and ) with their entity equivalents to prevent XSS - ** attacks. - */ - - $uri = strtr($uri, array("(" => "&040;", ")" => "&041;")); - - return $uri; -} - function check_form($text) { return drupal_specialchars($text, ENT_QUOTES); } -function check_query($text) { - return addslashes($text); -} - function filter($text) { $modules = module_list(); @@ -672,53 +561,6 @@ function format_size($size) { return t("%size %suffix", array("%size" => $size, "%suffix" => $suffix)); } -function cache_get($key) { - $cache = db_fetch_object(db_query("SELECT data, created FROM {cache} WHERE cid = '%s'", $key)); - return $cache->data ? $cache : 0; -} - -function cache_set($cid, $data, $expire = 0) { - db_query("UPDATE {cache} SET data = '%s', created = %d, expire = %d WHERE cid = '%s'", $data, time(), $expire, $cid); - if (!db_affected_rows()) { - db_query("INSERT INTO {cache} (cid, data, created, expire) VALUES('%s', '%s', %d, %d)", $cid, $data, time(), $expire); - } -} - -function cache_clear_all($cid = NULL) { - if (empty($cid)) { - db_query("DELETE FROM {cache} WHERE expire <> 0"); - } - else { - db_query("DELETE FROM {cache} WHERE cid = '%s'", $cid); - } -} - -function page_set_cache() { - global $user; - - if (!$user->uid && $_SERVER["REQUEST_METHOD"] == "GET") { - if ($data = ob_get_contents()) { - cache_set(request_uri(), $data, 1); - } - } -} - -function page_get_cache() { - global $user; - - $cache = NULL; - - if (!$user->uid && $_SERVER["REQUEST_METHOD"] == "GET") { - $cache = cache_get(request_uri()); - - if (empty($cache)) { - ob_start(); - } - } - - return $cache; -} - function format_interval($timestamp) { $units = array("1 year|%count years" => 31536000, "1 week|%count weeks" => 604800, "1 day|%count days" => 86400, "1 hour|%count hours" => 3600, "1 min|%count min" => 60, "1 sec|%count sec" => 1); foreach ($units as $key=>$value) { @@ -1005,64 +847,6 @@ function link_node($node, $main = 0) { return module_invoke_all("link", "node", $node, $main); } -function timer_start() { - global $timer; - list($usec, $sec) = explode(" ", microtime()); - $timer = (float)$usec + (float)$sec; -} - -function drupal_page_header() { - - if (variable_get("dev_timer", 0)) { - timer_start(); - } - - if (variable_get("cache", 0)) { - if ($cache = page_get_cache()) { - - // Set default values: - $date = gmdate("D, d M Y H:i:s", $cache->created) ." GMT"; - $etag = '"'. md5($date) .'"'; - - // Check http headers: - $modified_since = isset($_SERVER["HTTP_IF_MODIFIED_SINCE"]) ? $_SERVER["HTTP_IF_MODIFIED_SINCE"] == $date : NULL; - $none_match = isset($_SERVER["HTTP_IF_NONE_MATCH"]) ? $_SERVER["HTTP_IF_NONE_MATCH"] == $etag : NULL; - - // The type checking here is very important, be careful when changing entries. - if (($modified_since !== NULL || $none_match !== NULL) && $modified_since !== false && $none_match !== false) { - header("HTTP/1.0 304 Not Modified"); - exit(); - } - - // Send appropriate response: - header("Last-Modified: $date"); - header("ETag: $etag"); - print $cache->data; - - /* - ** A hook for modules where modules may take action at the end of a - ** request good uses include setting a cache, page logging, etc. - */ - - module_invoke_all("exit"); - - exit(); - } - } - - /* - ** Putting the check here avoids SQL query overhead in case we are - ** serving cached pages. The downside, however, is that the init - ** hooks might use unchecked data. - */ - - if (!user_access("bypass input data check")) { - if (!valid_input_data($_REQUEST)) { - die("terminated request because of suspicious input data"); - } - } -} - function drupal_page_footer() { if (variable_get("cache", 0)) { page_set_cache(); @@ -1076,22 +860,12 @@ function drupal_page_footer() { module_invoke_all("exit"); } -unset($conf); - -$config = conf_init(); - -include_once "includes/$config.php"; -include_once "includes/database.inc"; -include_once "includes/module.inc"; include_once "includes/theme.inc"; include_once "includes/pager.inc"; include_once "includes/menu.inc"; include_once "includes/xmlrpc.inc"; include_once "includes/tablesort.inc"; -// initialize configuration variables, using values from conf.php if available: -$conf = variable_init(isset($conf) ? $conf : array()); - // set error handler: set_error_handler("error_handler"); @@ -1116,9 +890,16 @@ function drupal_page_footer() { // initialize installed modules: module_init(); +if (!user_access("bypass input data check")) { + if (!valid_input_data($_REQUEST)) { + die("terminated request because of suspicious input data"); + } +} + // initialize localization system: $locale = locale_init(); // initialize theme: $theme = init_theme(); + ?> diff --git a/includes/module.inc b/includes/module.inc index e80384343e615e43cfd3f0fca2387c06ff8a3c52..787f072f26b479b274f1d567b5721edcdc2881b8 100644 --- a/includes/module.inc +++ b/includes/module.inc @@ -40,8 +40,8 @@ function module_invoke_all($hook, $a1 = NULL, $a2 = NULL, $a3 = NULL, $a4 = NULL return $return; } -// return array of module names (includes lazy module loading): -function module_list($refresh = 0) { +// return array of module names (includes lazy module loading if not in bootstrap mode) +function module_list($refresh = 0, $bootstrap = 0) { static $list; if ($refresh) { @@ -50,16 +50,20 @@ function module_list($refresh = 0) { if (!$list) { $list = array("admin" => "admin", "system" => "system", "user" => "user", "watchdog" => "watchdog"); - $result = db_query("SELECT name, filename FROM {system} WHERE type = 'module' AND status = '1' ORDER BY name"); + $result = db_query("SELECT name, filename, bootstrap FROM {system} WHERE type = 'module' AND status = '1' ORDER BY name"); while ($module = db_fetch_object($result)) { if (file_exists($module->filename)) { - $list[$module->name] = $module->name; - include_once $module->filename; + if ($bootstrap) { + $list[$module->name] = array("name"=> $module->name, "bootstrap" => $module->bootstrap, "filename" => $module->filename); + } + else { + $list[$module->name] = $module->name; + include_once $module->filename; + } } } natcasesort($list); } - return $list; } diff --git a/includes/session.inc b/includes/session.inc new file mode 100644 index 0000000000000000000000000000000000000000..5b1389a2322557ae46dca703e97c12a41571854d --- /dev/null +++ b/includes/session.inc @@ -0,0 +1,66 @@ +<?php + +session_set_save_handler("sess_open", "sess_close", "sess_read", "sess_write", "sess_destroy", "sess_gc"); +session_start(); + +/*** Session functions *****************************************************/ + +function sess_open($save_path, $session_name) { + return 1; +} + +function sess_close() { + return 1; +} + +function sess_read($key) { + global $user; + + $result = db_query_range("SELECT u.*, s.*, r.name AS role FROM {users} u INNER JOIN {role} r ON u.rid = r.rid INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.sid = '". check_query($key) ."' AND u.status < 3", 0, 1); + $user = db_fetch_object($result); + if ($user->data && $data = unserialize($user->data)) { + foreach ($data as $key => $value) { + if (!isset($user->$key)) { + $user->$key = $value; + } + } + } + + return !empty($user->session) ? $user->session : ''; +} + +function sess_write($key, $value) { + global $user; + + db_query("UPDATE {sessions} SET uid = %d, hostname = '%s', session = '%s', timestamp = %d WHERE sid = '$key'", $user->uid, $_SERVER["REMOTE_ADDR"], $value, time()); + + if (!db_affected_rows()) { + db_query("INSERT INTO {sessions} (uid, sid, hostname, session, timestamp) values(%d, '%s', '%s', '%s', %d)", $user->uid, $key, $_SERVER["REMOTE_ADDR"], $value, time()); + } + + return ''; +} + +function sess_destroy($key) { + + db_query("DELETE FROM {sessions} WHERE sid = '$key'"); + +} + +function sess_gc($lifetime) { + + /* + ** Be sure to adjust 'php_value session.gc_maxlifetime' to a large enough + ** value. For example, if you want user sessions to stay in your database + ** for three weeks before deleting them, you need to set gc_maxlifetime + ** to '1814400'. At that value, only after a user doesn't log in after + ** three weeks (1814400 seconds) will his/her session be removed. + */ + db_query("DELETE FROM {sessions} WHERE timestamp < %d", time() - $lifetime); + + return 1; + +} + + +?> \ No newline at end of file diff --git a/index.php b/index.php index f75d50b7ad69e018ef0f1394f9ede7357b07a904..6493bdfd040c14399cbb3064e517dc917608fb4a 100644 --- a/index.php +++ b/index.php @@ -1,6 +1,8 @@ <?php // $Id$ +include_once "includes/bootstrap.inc"; +drupal_page_header(); include_once "includes/common.inc"; drupal_page_header(); diff --git a/misc/drupal.css b/misc/drupal.css index 4c478e5beedc23018f80c4c270b13f00e23c30b7..f40610a9ddd735090d946ccabd98535232c1c627 100644 --- a/misc/drupal.css +++ b/misc/drupal.css @@ -10,7 +10,7 @@ th { border-bottom: 1px solid #ccc; } fieldset { - display: inline; + margin-bottom: 1em; } #tracker table { border-collapse: collapse; diff --git a/modules/system.module b/modules/system.module index c67722ad2f4e6148e09c59331e341ecb2f2e6d2d..67533f1d60cd678087b2a671a4c9d2c9acb40fd6 100644 --- a/modules/system.module +++ b/modules/system.module @@ -57,7 +57,8 @@ function system_link($type) { menu("admin/system", t("configuration"), "system_admin", 3); menu("admin/system/themes", t("themes"), "system_admin", 2); - foreach (list_themes(1) as $theme) { + foreach (list_themes() as $theme) { + // TODO: reenable 'forced refresh' once we move the menu_build() later in the request. it added overhead with no benefit // NOTE: refresh the list because some themes might have been enabled/disabled. include_once "$theme->filename"; $function = $theme->name ."_settings"; @@ -67,7 +68,8 @@ function system_link($type) { } menu("admin/system/modules", t("modules"), "system_admin", 3); - foreach (module_list(1) as $name) { + foreach (module_list() as $name) { + // TODO: reenable 'forced refresh' once we move the menu_build() later in the request. it added overhead with no benefit // NOTE: refresh the list because some modules might have been enabled/disabled. if (module_hook($name, "settings")) { menu("admin/system/modules/$name", t($name), "system_admin"); @@ -103,32 +105,31 @@ function system_view_general() { global $conf; // general settings: - $output .= "<h3>". t("General settings") ."</h3>\n"; - $output .= form_textfield(t("Name"), "site_name", variable_get("site_name", "drupal"), 70, 70, t("The name of this web site.")); - $output .= form_textfield(t("E-mail address"), "site_mail", variable_get("site_mail", ini_get("sendmail_from")), 70, 128, t("A valid e-mail address for this website, used by the auto-mailer during registration, new password requests, notifications, etc.")); - $output .= form_textfield(t("Slogan"), "site_slogan", variable_get("site_slogan", ""), 70, 128, t("The slogan of this website. Some themes display a slogan when available.")); - $output .= form_textarea(t("Mission"), "site_mission", variable_get("site_mission", ""), 70, 5, t("Your site's mission statement or focus.")); - $output .= form_textarea(t("Footer message"), "site_footer", variable_get("site_footer", ""), 70, 5, t("This text will be displayed at the bottom of each page. Useful for adding a copyright notice to your pages.")); - $output .= form_textfield(t("Anonymous user"), "anonymous", variable_get("anonymous", "Anonymous"), 70, 70, t("The name used to indicate anonymous users.")); - $output .= form_textfield(t("Default front page"), "site_frontpage", variable_get("site_frontpage", "node"), 70, 70, t("The home page displays content from this relative URL. If you are not using clean URLs, specify the part after '?q='. If unsure, specify 'node'.")); - $output .= form_radios(t("Clean URLs"), "clean_url", variable_get("clean_url", 0), array(t("Disabled"), t("Enabled")), t("Enable or disable clean URLs. If enabled, you'll need <code>ModRewrite</code> support. See also the <code>.htaccess</code> file in Drupal's top-level directory.")); - $output .= "<hr />\n"; + $group = form_textfield(t("Name"), "site_name", variable_get("site_name", "drupal"), 70, 70, t("The name of this web site.")); + $group .= form_textfield(t("E-mail address"), "site_mail", variable_get("site_mail", ini_get("sendmail_from")), 70, 128, t("A valid e-mail address for this website, used by the auto-mailer during registration, new password requests, notifications, etc.")); + $group .= form_textfield(t("Slogan"), "site_slogan", variable_get("site_slogan", ""), 70, 128, t("The slogan of this website. Some themes display a slogan when available.")); + $group .= form_textarea(t("Mission"), "site_mission", variable_get("site_mission", ""), 70, 5, t("Your site's mission statement or focus.")); + $group .= form_textarea(t("Footer message"), "site_footer", variable_get("site_footer", ""), 70, 5, t("This text will be displayed at the bottom of each page. Useful for adding a copyright notice to your pages.")); + $group .= form_textfield(t("Anonymous user"), "anonymous", variable_get("anonymous", "Anonymous"), 70, 70, t("The name used to indicate anonymous users.")); + $group .= form_textfield(t("Default front page"), "site_frontpage", variable_get("site_frontpage", "node"), 70, 70, t("The home page displays content from this relative URL. If you are not using clean URLs, specify the part after '?q='. If unsure, specify 'node'.")); + $group .= form_radios(t("Clean URLs"), "clean_url", variable_get("clean_url", 0), array(t("Disabled"), t("Enabled")), t("Enable or disable clean URLs. If enabled, you'll need <code>ModRewrite</code> support. See also the <code>.htaccess</code> file in Drupal's top-level directory.")); + + $output = form_group(t("General settings"), $group); // caching: - $output .= "<h3>". t("Cache settings") ."</h3>\n"; - $output .= form_radios(t("Cache support"), "cache", variable_get("cache", 0), array(t("Disabled"), t("Enabled")), t("Enable or disable the caching of rendered pages. When caching is enabled, Drupal will flush the cache when required to make sure updates take effect immediately. Check the %documentation for information on Drupal's cache system.", array("%documentation" => l(t("cache documentation"), "admin/system/help#cache")))); - $output .= "<hr />\n"; + $group = form_radios(t("Cache support"), "cache", variable_get("cache", 0), array(t("Disabled"), t("Enabled")), t("Enable or disable the caching of rendered pages. When caching is enabled, Drupal will flush the cache when required to make sure updates take effect immediately. Check the %documentation for information on Drupal's cache system.", array("%documentation" => l(t("cache documentation"), "admin/system/help#cache")))); + + $output .= form_group(t("Cache settings"), $group); // submission settings: - $output .= "<h3>". t("Submission settings") ."</h3>\n"; $rate = array(-10000 => t("Disabled"), 1 => t("Maximum 1 every second"), 5 => t("Maximum 1 every 5 seconds"), 15 => t("Maximum 1 every 15 seconds"), 30 => t("Maximum 1 every 30 seconds"), 60 => t("Maximum 1 every minute"), 300 => t("Maximum 1 every 5 minutes"), 900 => t("Maximum 1 every 15 minutes"), 1800 => t("Maximum 1 every 30 minutes"), 3600 => t("Maximum 1 every hour"), 21600 => t("Maximum 1 every 6 hours"), 43200 => t("Maximum 1 every 12 hours")); - $output .= form_select(t("Maximum node rate"), "max_node_rate", variable_get("max_node_rate", 900), $rate, t("The maximum submission rate for nodes. Its purpose is to stop potential abuse or denial of service attacks.")); - $output .= form_select(t("Maximum comment rate"), "max_comment_rate", variable_get("max_comment_rate", 120), $rate, t("The maximum submission rate for comments. Its purpose is to stop potential abuse or denial of service attacks.")); - $output .= "<hr />\n"; + $group = form_select(t("Maximum node rate"), "max_node_rate", variable_get("max_node_rate", 900), $rate, t("The maximum submission rate for nodes. Its purpose is to stop potential abuse or denial of service attacks.")); + $group .= form_select(t("Maximum comment rate"), "max_comment_rate", variable_get("max_comment_rate", 120), $rate, t("The maximum submission rate for comments. Its purpose is to stop potential abuse or denial of service attacks.")); + + $output .= form_group(t("Submission settings"), $group); // date settings: - $output .= "<h3>". t("Date format settings") ."</h3>\n"; // date settings: possible date formats $dateshort = array("m/d/Y - H:i", "d/m/Y - H:i", "Y/m/d - H:i", @@ -153,17 +154,12 @@ function system_view_general() { $datelongchoices[$f] = format_date(time(), "custom", $f); } - $output .= form_select(t("Date format (short)"), "date_format_short", variable_get("date_format_short", $dateshort[0]), $dateshortchoices, t("The short format of date display.")); - $output .= form_select(t("Date format (medium)"), "date_format_medium", variable_get("date_format_medium", $datemedium[0]), $datemediumchoices, t("The medium sized date display.")); - $output .= form_select(t("Date format (long)"), "date_format_long", variable_get("date_format_long", $datelong[0]), $datelongchoices, t("Longer date format used for detailed display.")); + $group = form_select(t("Date format (short)"), "date_format_short", variable_get("date_format_short", $dateshort[0]), $dateshortchoices, t("The short format of date display.")); + $group .= form_select(t("Date format (medium)"), "date_format_medium", variable_get("date_format_medium", $datemedium[0]), $datemediumchoices, t("The medium sized date display.")); + $group .= form_select(t("Date format (long)"), "date_format_long", variable_get("date_format_long", $datelong[0]), $datelongchoices, t("Longer date format used for detailed display.")); - return $output; -} + $output .= form_group(t("Date format settings"), $group); -function system_view_module($name) { - if (module_hook($name, "settings")) { - $output .= "<h3><a id=\"$name\">". ucfirst(t("$name")) ." ". t("settings") ."</a></h3>". module_invoke($name, "settings") ."<hr />\n"; - } return $output; } @@ -228,7 +224,7 @@ function system_view($type, $arg = "") { break; case "modules": if ($arg) { - $form = system_view_module($arg); + $form = module_invoke($arg, "settings"); } else { $form = system_listing("module", "modules", $required); @@ -299,6 +295,14 @@ function system_listing($type, $directory, $required = array()) { if ($type == "module") { $info->name = module_invoke($file->name, "help", "admin/system/modules#name") ? module_invoke($file->name, "help", "admin/system/modules#name") : module_invoke($file->name, "system", "name") ? module_invoke($file->name, "system", "name") : $file->name; $info->description = module_invoke($file->name, "help", "admin/system/modules#description") ? module_invoke($file->name, "help", "admin/system/modules#description") : module_invoke($file->name, "system", "description"); + // log the critical hooks implemented by this module + $bootstrap = 0; + foreach (bootstrap_hooks() as $hook) { + if (module_hook($file->name, $hook)) { + $bootstrap = 1; + break; + } + } } elseif ($type == "theme") { $info->name = $file->name; @@ -308,7 +312,7 @@ function system_listing($type, $directory, $required = array()) { // Update the contents of the system table: db_query("DELETE FROM {system} WHERE filename = '%s' AND type = '%s'", $filename, $type); - db_query("INSERT INTO {system} (name, description, type, filename, status) VALUES ('%s', '%s', '%s', '%s', %d)", $info->name, $info->description, $type, $filename, $file->status); + db_query("INSERT INTO {system} (name, description, type, filename, status, bootstrap) VALUES ('%s', '%s', '%s', '%s', %d, %d)", $info->name, $info->description, $type, $filename, $file->status, $bootstrap); $rows[] = array($info->name, $info->description, array("data" => (in_array($filename, $required) ? form_hidden("status][$filename", 1) . t("required") : form_checkbox("", "status][$filename", 1, $file->status)), "align" => "center")); } diff --git a/modules/system/system.module b/modules/system/system.module index c67722ad2f4e6148e09c59331e341ecb2f2e6d2d..67533f1d60cd678087b2a671a4c9d2c9acb40fd6 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -57,7 +57,8 @@ function system_link($type) { menu("admin/system", t("configuration"), "system_admin", 3); menu("admin/system/themes", t("themes"), "system_admin", 2); - foreach (list_themes(1) as $theme) { + foreach (list_themes() as $theme) { + // TODO: reenable 'forced refresh' once we move the menu_build() later in the request. it added overhead with no benefit // NOTE: refresh the list because some themes might have been enabled/disabled. include_once "$theme->filename"; $function = $theme->name ."_settings"; @@ -67,7 +68,8 @@ function system_link($type) { } menu("admin/system/modules", t("modules"), "system_admin", 3); - foreach (module_list(1) as $name) { + foreach (module_list() as $name) { + // TODO: reenable 'forced refresh' once we move the menu_build() later in the request. it added overhead with no benefit // NOTE: refresh the list because some modules might have been enabled/disabled. if (module_hook($name, "settings")) { menu("admin/system/modules/$name", t($name), "system_admin"); @@ -103,32 +105,31 @@ function system_view_general() { global $conf; // general settings: - $output .= "<h3>". t("General settings") ."</h3>\n"; - $output .= form_textfield(t("Name"), "site_name", variable_get("site_name", "drupal"), 70, 70, t("The name of this web site.")); - $output .= form_textfield(t("E-mail address"), "site_mail", variable_get("site_mail", ini_get("sendmail_from")), 70, 128, t("A valid e-mail address for this website, used by the auto-mailer during registration, new password requests, notifications, etc.")); - $output .= form_textfield(t("Slogan"), "site_slogan", variable_get("site_slogan", ""), 70, 128, t("The slogan of this website. Some themes display a slogan when available.")); - $output .= form_textarea(t("Mission"), "site_mission", variable_get("site_mission", ""), 70, 5, t("Your site's mission statement or focus.")); - $output .= form_textarea(t("Footer message"), "site_footer", variable_get("site_footer", ""), 70, 5, t("This text will be displayed at the bottom of each page. Useful for adding a copyright notice to your pages.")); - $output .= form_textfield(t("Anonymous user"), "anonymous", variable_get("anonymous", "Anonymous"), 70, 70, t("The name used to indicate anonymous users.")); - $output .= form_textfield(t("Default front page"), "site_frontpage", variable_get("site_frontpage", "node"), 70, 70, t("The home page displays content from this relative URL. If you are not using clean URLs, specify the part after '?q='. If unsure, specify 'node'.")); - $output .= form_radios(t("Clean URLs"), "clean_url", variable_get("clean_url", 0), array(t("Disabled"), t("Enabled")), t("Enable or disable clean URLs. If enabled, you'll need <code>ModRewrite</code> support. See also the <code>.htaccess</code> file in Drupal's top-level directory.")); - $output .= "<hr />\n"; + $group = form_textfield(t("Name"), "site_name", variable_get("site_name", "drupal"), 70, 70, t("The name of this web site.")); + $group .= form_textfield(t("E-mail address"), "site_mail", variable_get("site_mail", ini_get("sendmail_from")), 70, 128, t("A valid e-mail address for this website, used by the auto-mailer during registration, new password requests, notifications, etc.")); + $group .= form_textfield(t("Slogan"), "site_slogan", variable_get("site_slogan", ""), 70, 128, t("The slogan of this website. Some themes display a slogan when available.")); + $group .= form_textarea(t("Mission"), "site_mission", variable_get("site_mission", ""), 70, 5, t("Your site's mission statement or focus.")); + $group .= form_textarea(t("Footer message"), "site_footer", variable_get("site_footer", ""), 70, 5, t("This text will be displayed at the bottom of each page. Useful for adding a copyright notice to your pages.")); + $group .= form_textfield(t("Anonymous user"), "anonymous", variable_get("anonymous", "Anonymous"), 70, 70, t("The name used to indicate anonymous users.")); + $group .= form_textfield(t("Default front page"), "site_frontpage", variable_get("site_frontpage", "node"), 70, 70, t("The home page displays content from this relative URL. If you are not using clean URLs, specify the part after '?q='. If unsure, specify 'node'.")); + $group .= form_radios(t("Clean URLs"), "clean_url", variable_get("clean_url", 0), array(t("Disabled"), t("Enabled")), t("Enable or disable clean URLs. If enabled, you'll need <code>ModRewrite</code> support. See also the <code>.htaccess</code> file in Drupal's top-level directory.")); + + $output = form_group(t("General settings"), $group); // caching: - $output .= "<h3>". t("Cache settings") ."</h3>\n"; - $output .= form_radios(t("Cache support"), "cache", variable_get("cache", 0), array(t("Disabled"), t("Enabled")), t("Enable or disable the caching of rendered pages. When caching is enabled, Drupal will flush the cache when required to make sure updates take effect immediately. Check the %documentation for information on Drupal's cache system.", array("%documentation" => l(t("cache documentation"), "admin/system/help#cache")))); - $output .= "<hr />\n"; + $group = form_radios(t("Cache support"), "cache", variable_get("cache", 0), array(t("Disabled"), t("Enabled")), t("Enable or disable the caching of rendered pages. When caching is enabled, Drupal will flush the cache when required to make sure updates take effect immediately. Check the %documentation for information on Drupal's cache system.", array("%documentation" => l(t("cache documentation"), "admin/system/help#cache")))); + + $output .= form_group(t("Cache settings"), $group); // submission settings: - $output .= "<h3>". t("Submission settings") ."</h3>\n"; $rate = array(-10000 => t("Disabled"), 1 => t("Maximum 1 every second"), 5 => t("Maximum 1 every 5 seconds"), 15 => t("Maximum 1 every 15 seconds"), 30 => t("Maximum 1 every 30 seconds"), 60 => t("Maximum 1 every minute"), 300 => t("Maximum 1 every 5 minutes"), 900 => t("Maximum 1 every 15 minutes"), 1800 => t("Maximum 1 every 30 minutes"), 3600 => t("Maximum 1 every hour"), 21600 => t("Maximum 1 every 6 hours"), 43200 => t("Maximum 1 every 12 hours")); - $output .= form_select(t("Maximum node rate"), "max_node_rate", variable_get("max_node_rate", 900), $rate, t("The maximum submission rate for nodes. Its purpose is to stop potential abuse or denial of service attacks.")); - $output .= form_select(t("Maximum comment rate"), "max_comment_rate", variable_get("max_comment_rate", 120), $rate, t("The maximum submission rate for comments. Its purpose is to stop potential abuse or denial of service attacks.")); - $output .= "<hr />\n"; + $group = form_select(t("Maximum node rate"), "max_node_rate", variable_get("max_node_rate", 900), $rate, t("The maximum submission rate for nodes. Its purpose is to stop potential abuse or denial of service attacks.")); + $group .= form_select(t("Maximum comment rate"), "max_comment_rate", variable_get("max_comment_rate", 120), $rate, t("The maximum submission rate for comments. Its purpose is to stop potential abuse or denial of service attacks.")); + + $output .= form_group(t("Submission settings"), $group); // date settings: - $output .= "<h3>". t("Date format settings") ."</h3>\n"; // date settings: possible date formats $dateshort = array("m/d/Y - H:i", "d/m/Y - H:i", "Y/m/d - H:i", @@ -153,17 +154,12 @@ function system_view_general() { $datelongchoices[$f] = format_date(time(), "custom", $f); } - $output .= form_select(t("Date format (short)"), "date_format_short", variable_get("date_format_short", $dateshort[0]), $dateshortchoices, t("The short format of date display.")); - $output .= form_select(t("Date format (medium)"), "date_format_medium", variable_get("date_format_medium", $datemedium[0]), $datemediumchoices, t("The medium sized date display.")); - $output .= form_select(t("Date format (long)"), "date_format_long", variable_get("date_format_long", $datelong[0]), $datelongchoices, t("Longer date format used for detailed display.")); + $group = form_select(t("Date format (short)"), "date_format_short", variable_get("date_format_short", $dateshort[0]), $dateshortchoices, t("The short format of date display.")); + $group .= form_select(t("Date format (medium)"), "date_format_medium", variable_get("date_format_medium", $datemedium[0]), $datemediumchoices, t("The medium sized date display.")); + $group .= form_select(t("Date format (long)"), "date_format_long", variable_get("date_format_long", $datelong[0]), $datelongchoices, t("Longer date format used for detailed display.")); - return $output; -} + $output .= form_group(t("Date format settings"), $group); -function system_view_module($name) { - if (module_hook($name, "settings")) { - $output .= "<h3><a id=\"$name\">". ucfirst(t("$name")) ." ". t("settings") ."</a></h3>". module_invoke($name, "settings") ."<hr />\n"; - } return $output; } @@ -228,7 +224,7 @@ function system_view($type, $arg = "") { break; case "modules": if ($arg) { - $form = system_view_module($arg); + $form = module_invoke($arg, "settings"); } else { $form = system_listing("module", "modules", $required); @@ -299,6 +295,14 @@ function system_listing($type, $directory, $required = array()) { if ($type == "module") { $info->name = module_invoke($file->name, "help", "admin/system/modules#name") ? module_invoke($file->name, "help", "admin/system/modules#name") : module_invoke($file->name, "system", "name") ? module_invoke($file->name, "system", "name") : $file->name; $info->description = module_invoke($file->name, "help", "admin/system/modules#description") ? module_invoke($file->name, "help", "admin/system/modules#description") : module_invoke($file->name, "system", "description"); + // log the critical hooks implemented by this module + $bootstrap = 0; + foreach (bootstrap_hooks() as $hook) { + if (module_hook($file->name, $hook)) { + $bootstrap = 1; + break; + } + } } elseif ($type == "theme") { $info->name = $file->name; @@ -308,7 +312,7 @@ function system_listing($type, $directory, $required = array()) { // Update the contents of the system table: db_query("DELETE FROM {system} WHERE filename = '%s' AND type = '%s'", $filename, $type); - db_query("INSERT INTO {system} (name, description, type, filename, status) VALUES ('%s', '%s', '%s', '%s', %d)", $info->name, $info->description, $type, $filename, $file->status); + db_query("INSERT INTO {system} (name, description, type, filename, status, bootstrap) VALUES ('%s', '%s', '%s', '%s', %d, %d)", $info->name, $info->description, $type, $filename, $file->status, $bootstrap); $rows[] = array($info->name, $info->description, array("data" => (in_array($filename, $required) ? form_hidden("status][$filename", 1) . t("required") : form_checkbox("", "status][$filename", 1, $file->status)), "align" => "center")); } diff --git a/modules/user.module b/modules/user.module index 7a218635bcbbe5c793917d3d888be6fb225e5401..6b7909f6d1204a4cb720a7698ecfc8c5782c76c4 100644 --- a/modules/user.module +++ b/modules/user.module @@ -1,68 +1,6 @@ <?php // $Id$ -session_set_save_handler("sess_open", "sess_close", "sess_read", "sess_write", "sess_destroy", "sess_gc"); -session_start(); - -/*** Session functions *****************************************************/ - -function sess_open($save_path, $session_name) { - return 1; -} - -function sess_close() { - return 1; -} - -function sess_read($key) { - global $user; - - $result = db_query_range("SELECT u.*, s.*, r.name AS role FROM {users} u INNER JOIN {role} r ON u.rid = r.rid INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.sid = '". check_query($key) ."' AND u.status < 3", 0, 1); - $user = db_fetch_object($result); - if ($user->data && $data = unserialize($user->data)) { - foreach ($data as $key => $value) { - if (!isset($user->$key)) { - $user->$key = $value; - } - } - } - - return !empty($user->session) ? $user->session : ''; -} - -function sess_write($key, $value) { - global $user; - - db_query("UPDATE {sessions} SET uid = %d, hostname = '%s', session = '%s', timestamp = %d WHERE sid = '$key'", $user->uid, $_SERVER["REMOTE_ADDR"], $value, time()); - - if (!db_affected_rows()) { - db_query("INSERT INTO {sessions} (uid, sid, hostname, session, timestamp) values(%d, '%s', '%s', '%s', %d)", $user->uid, $key, $_SERVER["REMOTE_ADDR"], $value, time()); - } - - return ''; -} - -function sess_destroy($key) { - - db_query("DELETE FROM {sessions} WHERE sid = '$key'"); - -} - -function sess_gc($lifetime) { - - /* - ** Be sure to adjust 'php_value session.gc_maxlifetime' to a large enough - ** value. For example, if you want user sessions to stay in your database - ** for three weeks before deleting them, you need to set gc_maxlifetime - ** to '1814400'. At that value, only after a user doesn't log in after - ** three weeks (1814400 seconds) will his/her session be removed. - */ - db_query("DELETE FROM {sessions} WHERE timestamp < %d", time() - $lifetime); - - return 1; - -} - /*** Common functions ******************************************************/ function user_external_load($authname) { diff --git a/modules/user/user.module b/modules/user/user.module index 7a218635bcbbe5c793917d3d888be6fb225e5401..6b7909f6d1204a4cb720a7698ecfc8c5782c76c4 100644 --- a/modules/user/user.module +++ b/modules/user/user.module @@ -1,68 +1,6 @@ <?php // $Id$ -session_set_save_handler("sess_open", "sess_close", "sess_read", "sess_write", "sess_destroy", "sess_gc"); -session_start(); - -/*** Session functions *****************************************************/ - -function sess_open($save_path, $session_name) { - return 1; -} - -function sess_close() { - return 1; -} - -function sess_read($key) { - global $user; - - $result = db_query_range("SELECT u.*, s.*, r.name AS role FROM {users} u INNER JOIN {role} r ON u.rid = r.rid INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.sid = '". check_query($key) ."' AND u.status < 3", 0, 1); - $user = db_fetch_object($result); - if ($user->data && $data = unserialize($user->data)) { - foreach ($data as $key => $value) { - if (!isset($user->$key)) { - $user->$key = $value; - } - } - } - - return !empty($user->session) ? $user->session : ''; -} - -function sess_write($key, $value) { - global $user; - - db_query("UPDATE {sessions} SET uid = %d, hostname = '%s', session = '%s', timestamp = %d WHERE sid = '$key'", $user->uid, $_SERVER["REMOTE_ADDR"], $value, time()); - - if (!db_affected_rows()) { - db_query("INSERT INTO {sessions} (uid, sid, hostname, session, timestamp) values(%d, '%s', '%s', '%s', %d)", $user->uid, $key, $_SERVER["REMOTE_ADDR"], $value, time()); - } - - return ''; -} - -function sess_destroy($key) { - - db_query("DELETE FROM {sessions} WHERE sid = '$key'"); - -} - -function sess_gc($lifetime) { - - /* - ** Be sure to adjust 'php_value session.gc_maxlifetime' to a large enough - ** value. For example, if you want user sessions to stay in your database - ** for three weeks before deleting them, you need to set gc_maxlifetime - ** to '1814400'. At that value, only after a user doesn't log in after - ** three weeks (1814400 seconds) will his/her session be removed. - */ - db_query("DELETE FROM {sessions} WHERE timestamp < %d", time() - $lifetime); - - return 1; - -} - /*** Common functions ******************************************************/ function user_external_load($authname) { diff --git a/themes/xtemplate/xtemplate.css b/themes/xtemplate/xtemplate.css index 5d3c2cc9adc13fd5fd4c5d639976cd024f11eb9e..36b09efa46712012a94d3083cab842d5eaa3505b 100644 --- a/themes/xtemplate/xtemplate.css +++ b/themes/xtemplate/xtemplate.css @@ -37,6 +37,9 @@ a:hover { color: #39c; text-decoration: underline; } +fieldset { + border: 1px solid #ccc; +} p { margin: 0 0 1em 0; padding: 0; diff --git a/update.php b/update.php index fb44352de6e05f25178ce47b646539c7bf83fa63..acedf0054246412bae0d2fc7f840a528866f3651 100644 --- a/update.php +++ b/update.php @@ -59,7 +59,8 @@ "2003-10-11" => "update_67", "2003-10-20" => "update_68", "2003-10-22" => "update_69", - "2003-10-27" => "update_70" + "2003-10-27" => "update_70", + "2003-11-17" => "update_71" ); function update_32() { @@ -547,6 +548,10 @@ function update_70() { update_sql("ALTER TABLE {variable} CHANGE name name varchar(48) NOT NULL"); } +function update_71() { + update_sql("ALTER TABLE {system} ADD bootstrap int(2)"); +} + /* ** System functions */ @@ -603,8 +608,9 @@ function update_page() { case "Update": // make sure we have updates to run. print update_page_header("Drupal database update"); - print "<b>» <a href=\"index.php\">main page</a></b><br />\n"; - print "<b>» <a href=\"index.php?q=admin\">administration pages</a></b><br />\n"; + $links[] = "<a href=\"index.php\">main page</a>"; + $links[] = "<a href=\"index.php?q=admin\">administration pages</a>"; + print theme("item_list", $links); // NOTE: we can't use l() here because the URL would point to 'update.php?q=admin'. if ($edit["start"] == -1) { print "No updates to perform."; @@ -662,6 +668,7 @@ function update_info() { } if (isset($_GET["op"])) { + include_once "includes/bootstrap.inc"; include_once "includes/common.inc"; // Access check: diff --git a/xmlrpc.php b/xmlrpc.php index 41538a98ba1a0c683806f4f7604edf1777bdabb7..69430d803010164519bdb0cd79d4d8725b3856bd 100644 --- a/xmlrpc.php +++ b/xmlrpc.php @@ -2,6 +2,7 @@ // $Id$ include_once "includes/xmlrpcs.inc"; +include_once "includes/bootstrap.inc"; include_once "includes/common.inc"; $functions = module_invoke_all("xmlrpc");