From e3d2c46f04b2d510e60a3266f2b3bc2e4550eb85 Mon Sep 17 00:00:00 2001 From: Kjartan Mannes <kjartan@2.no-reply.drupal.org> Date: Tue, 5 Mar 2002 20:15:17 +0000 Subject: [PATCH] - applied search patch. - added who is online block. - made weblog module more configurable. - users may now delete their own accounts (Feature #8) - users may now request a password using email address *or* username. formerly required both items to match an account which was onerous. - the link to request a new password is now presented whenever a user fails login. - there is now a confirmation message after submitting edits to your user information. - error messages in user.module may now be stylized by themes. - <hook>_form has a $param setting you can fill with form parameters. - improved wording for a few config settings. - fixed various non-coding standard things. --- includes/common.inc | 81 ++++--- modules/comment.module | 58 ++++- modules/comment/comment.module | 58 ++++- modules/node.module | 54 ++++- modules/node/node.module | 54 ++++- modules/search.module | 401 ++++++++++++++++++++++++++++++--- modules/search/search.module | 401 ++++++++++++++++++++++++++++++--- modules/system.module | 10 +- modules/system/system.module | 10 +- modules/user.module | 91 ++++++-- modules/user/user.module | 91 ++++++-- modules/weblogs.module | 16 +- update.php | 7 +- 13 files changed, 1155 insertions(+), 177 deletions(-) diff --git a/includes/common.inc b/includes/common.inc index a7575110e216..847a0839c3de 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -144,22 +144,36 @@ function variable_del($name) { unset($conf[$name]); } -/* -** Format a single result entry of a search query: -*/ - +/** + * Format a single result entry of a search query: + * + * @param $item a single search result as returned by <module>_search of type + * array("count" => ..., "link" => ..., "title" => ..., + * "user" => ..., "date" => ..., "keywords" => ...) + * @param $type module type of this item + */ function search_item($item, $type) { - $output .= " <b><u><a href=\"". $item["link"] ."\">". $item["title"] ."</a></u></b><br />"; + $output .= " <b>". $item["count"] . " <u><a href=\"". $item["link"] . "\">". $item["title"] ."</a></u></b><br />"; $output .= " <small>$type ". ($item["user"] ? " - ". $item["user"] : "") ."". ($item["date"] ? " - ". format_date($item["date"], "small") : "") ."</small>"; $output .= "<br /><br />"; return $output; } -/* -** Render a generic search form: -*/ - +/** + * Render a generic search form. + * + * "Generic" means "universal usable" - that is, usable not only from + * module.php?mod=search, but also as a simple seach box (without + * "Restrict search to", help text, etc) from theme's header etc. + * This means: provide options to only conditionally render certain + * parts of this form. + * + * @param $action Form action. Defaults to module.php?mod=search. + * @param $query Query string. Defaults to global $keys. + * @param $options != 0: Render additional form fields/text + * ("Restrict search to", help text, etc). + */ function search_form($action = 0, $query = 0, $options = 0) { global $keys; @@ -171,8 +185,8 @@ function search_form($action = 0, $query = 0, $options = 0) { $query = $keys; } - $output .= " <input type=\"text\" size=\"50\" value=\"". check_form($keys) ."\" name=\"keys\">"; - $output .= " <input type=\"submit\" value=\"". t("Search") ."\">\n"; + $output .= " <br /><input type=\"text\" size=\"50\" value=\"". check_form($keys) ."\" name=\"keys\" />"; + $output .= " <input type=\"submit\" value=\"". t("Search") ."\" />\n"; if ($options != 0) { $output .= "<br />"; @@ -180,18 +194,21 @@ function search_form($action = 0, $query = 0, $options = 0) { foreach (module_list() as $name) { if (module_hook($name, "search")) { - $output .= " <input type=\"checkbox\" name=\"edit[type][$name]\" ". ($edit["type"][$name] ? " checked=\"checked\"" : "") ."/> ". t($name); + $output .= " <input type=\"checkbox\" name=\"edit[type][$name]\" ". ($edit["type"][$name] ? " checked=\"checked\"" : "") ." /> ". t($name); } } + + // TODO: (link to) search hints } + $form .= "<br />"; + return form($output, "post", $action); } /* -** Collect the search results: -*/ - + * Collect the search results: + */ function search_data() { global $keys, $edit; @@ -200,12 +217,18 @@ function search_data() { if ($keys) { foreach (module_list() as $name) { if (module_hook($name, "search") && (!$edit["type"] || $edit["type"][$name]) && ($result = module_invoke($name, "search", check_query($keys)))) { + if ($name == "node" || $name == "comment") { + $output .= "<b>Matching ". $name ."s ranked in order of relevance</b><br />"; + } + else { + $output .= "<b>Matching ". $name ."s</b><br />"; + } foreach ($result as $entry) { $output .= search_item($entry, $name); } } } - if(!$output) { + if (!$output) { $output .= t("Your search yielded no results."); } } @@ -213,10 +236,16 @@ function search_data() { return $output; } -/* -** Display the search form and the resulting data: -*/ - +/** + * Display the search form and the resulting data. + * + * @param $type If set, search only nodes of this type. + * Otherwise, search all types. + * @param $action Form action. Defaults to module.php?mod=search. + * @param $query Query string. Defaults to global $keys. + * @param $options != 0: Render additional form fields/text + * ("Restrict search to", help text, etc). + */ function search_type($type = 0, $action = 0, $query = 0, $options = 0) { global $edit; @@ -304,7 +333,9 @@ function check_query($text) { function filter($text) { foreach (module_list() as $name) { - if (module_hook($name, "filter")) $text = module_invoke($name, "filter", $text); + if (module_hook($name, "filter")) { + $text = module_invoke($name, "filter", $text); + } } return $text; @@ -327,10 +358,6 @@ function check_file($filename) { } } -function file_encode($filename) { - $edit[filedata] = base64_encode(fread($fd, filesize($edit[upload_file]))); -} - function format_info($body, $block) { return "<table><tr><td><table align=\"right\" border=\"1\" width=\"180\"><tr><td>$block</td></tr></table>$body</td></tr></table>\n"; } @@ -552,7 +579,9 @@ function field_get($string, $name) { function field_set($string, $name, $value) { $rval = ereg_replace(",$name=[^,]+", "", ",$string"); - if ($value) $rval .= ($rval == "," ? "" : ",") ."$name=$value"; + if ($value) { + $rval .= ($rval == "," ? "" : ",") ."$name=$value"; + } return substr($rval, 1); } diff --git a/modules/comment.module b/modules/comment.module index 0aa4679893ab..2349906118cc 100644 --- a/modules/comment.module +++ b/modules/comment.module @@ -191,6 +191,15 @@ function comment_preview($edit) { comment_view($comment, t("reply to this comment")); $theme->box(t("Reply"), comment_form($edit)); + + if ($edit["pid"]) { + $comment = db_fetch_object(db_query("SELECT c.*, u.uid, u.name FROM comments c LEFT JOIN users u ON c.uid = u.uid WHERE c.cid = '$edit[pid]'")); + comment_view($comment, t("reply to this comment")); + } + else { + node_view(node_load(array("nid" => $edit["nid"]))); + $edit["pid"] = 0; + } } function comment_post($edit) { @@ -531,10 +540,32 @@ function comment_render($nid, $cid) { function comment_search($keys) { global $PHP_SELF; - $result = db_query("SELECT c.*, u.name FROM comments c LEFT JOIN users u ON c.uid = u.uid WHERE c.subject LIKE '%$keys%' OR c.comment LIKE '%$keys%' ORDER BY c.timestamp DESC LIMIT 20"); - while ($comment = db_fetch_object($result)) { - $find[$i++] = array("title" => check_output($comment->subject), "link" => (strstr($PHP_SELF, "admin.php") ? "admin.php?mod=comment&op=edit&id=$comment->cid" : "node.php?id=$comment->nid&cid=$comment->cid"), "user" => $comment->name, "date" => $comment->timestamp); - } + + // Return the results of performing a search using the indexed search + // for this particular type of node. + // + // Pass an array to the "do_search" function which dictates what it + // will search through, and what it will search for + // + // "keys"'s value is the keywords entered by the user + // + // "type"'s value is used to identify the node type in the search + // index. + // + // "select"'s value is used to relate the data from the specific nodes + // table to the data that the search_index table has in it, and the the + // do_search functino will rank it. + // + // The select must always provide the following fields - lno, title, + // created, uid, name, count + // + // The select statement may optionally provide "nid", which is a secondary + // identifier which is currently used byt the comment module. + // + $find = do_search(array("keys" => $keys, + "type" => "comment", + "select" => "select s.lno as lno, c.nid as nid, c.subject as title, c.timestamp as created, u.uid as uid, u.name as name, s.count as count FROM search_index s, comments c LEFT JOIN users u ON c.uid = u.uid WHERE s.lno = c.cid AND s.type = 'comment' AND s.word like '%'")); + return $find; } @@ -715,4 +746,23 @@ function comment_admin() { } } +function comment_update_index() { + + // Return an array of values to dictate how to update the search index + // for this particular type of node. + // + // "last_update"'s value is used with variable_set to set the + // last time this node type (comment) had an index update run. + // + // "node_type"'s value is used to identify the node type in the search + // index (commentt in this case). + // + // "select"'s value is used to select the node id and text fields from + // the table we are indexing. In this case, we also check against the + // last run date for the comments update. + return array("last_update" => "comment_cron_last", + "node_type" => "comment", + "select" => "SELECT c.cid as lno, c.subject as text1, c.comment as text2 FROM comments c WHERE timestamp > ". variable_get("comment_cron_last", 1)); +} + ?> diff --git a/modules/comment/comment.module b/modules/comment/comment.module index 0aa4679893ab..2349906118cc 100644 --- a/modules/comment/comment.module +++ b/modules/comment/comment.module @@ -191,6 +191,15 @@ function comment_preview($edit) { comment_view($comment, t("reply to this comment")); $theme->box(t("Reply"), comment_form($edit)); + + if ($edit["pid"]) { + $comment = db_fetch_object(db_query("SELECT c.*, u.uid, u.name FROM comments c LEFT JOIN users u ON c.uid = u.uid WHERE c.cid = '$edit[pid]'")); + comment_view($comment, t("reply to this comment")); + } + else { + node_view(node_load(array("nid" => $edit["nid"]))); + $edit["pid"] = 0; + } } function comment_post($edit) { @@ -531,10 +540,32 @@ function comment_render($nid, $cid) { function comment_search($keys) { global $PHP_SELF; - $result = db_query("SELECT c.*, u.name FROM comments c LEFT JOIN users u ON c.uid = u.uid WHERE c.subject LIKE '%$keys%' OR c.comment LIKE '%$keys%' ORDER BY c.timestamp DESC LIMIT 20"); - while ($comment = db_fetch_object($result)) { - $find[$i++] = array("title" => check_output($comment->subject), "link" => (strstr($PHP_SELF, "admin.php") ? "admin.php?mod=comment&op=edit&id=$comment->cid" : "node.php?id=$comment->nid&cid=$comment->cid"), "user" => $comment->name, "date" => $comment->timestamp); - } + + // Return the results of performing a search using the indexed search + // for this particular type of node. + // + // Pass an array to the "do_search" function which dictates what it + // will search through, and what it will search for + // + // "keys"'s value is the keywords entered by the user + // + // "type"'s value is used to identify the node type in the search + // index. + // + // "select"'s value is used to relate the data from the specific nodes + // table to the data that the search_index table has in it, and the the + // do_search functino will rank it. + // + // The select must always provide the following fields - lno, title, + // created, uid, name, count + // + // The select statement may optionally provide "nid", which is a secondary + // identifier which is currently used byt the comment module. + // + $find = do_search(array("keys" => $keys, + "type" => "comment", + "select" => "select s.lno as lno, c.nid as nid, c.subject as title, c.timestamp as created, u.uid as uid, u.name as name, s.count as count FROM search_index s, comments c LEFT JOIN users u ON c.uid = u.uid WHERE s.lno = c.cid AND s.type = 'comment' AND s.word like '%'")); + return $find; } @@ -715,4 +746,23 @@ function comment_admin() { } } +function comment_update_index() { + + // Return an array of values to dictate how to update the search index + // for this particular type of node. + // + // "last_update"'s value is used with variable_set to set the + // last time this node type (comment) had an index update run. + // + // "node_type"'s value is used to identify the node type in the search + // index (commentt in this case). + // + // "select"'s value is used to select the node id and text fields from + // the table we are indexing. In this case, we also check against the + // last run date for the comments update. + return array("last_update" => "comment_cron_last", + "node_type" => "comment", + "select" => "SELECT c.cid as lno, c.subject as text1, c.comment as text2 FROM comments c WHERE timestamp > ". variable_get("comment_cron_last", 1)); +} + ?> diff --git a/modules/node.module b/modules/node.module index 2b49a090de8b..6dab1419d853 100644 --- a/modules/node.module +++ b/modules/node.module @@ -48,7 +48,7 @@ function node_teaser($body) { function node_invoke($node, $name, $arg = 0) { if (is_array($node)) { - $function = $node[type] ."_$name"; + $function = $node["type"] ."_$name"; } else if (is_object($node)) { $function = $node->type ."_$name"; @@ -261,11 +261,28 @@ function node_perm() { function node_search($keys) { global $PHP_SELF; - $result = db_query("SELECT n.nid, n.title, n.created, u.uid, u.name FROM node n LEFT JOIN users u ON n.uid = u.uid WHERE n.status = 1 AND (n.title LIKE '%$keys%' OR n.teaser LIKE '%$keys%' OR n.body LIKE '%$keys%') ORDER BY n.created DESC LIMIT 20"); - while ($node = db_fetch_object($result)) { - $find[$i++] = array("title" => check_output($node->title), "link" => (strstr($PHP_SELF, "admin.php") ? "admin.php?mod=node&type=node&op=edit&id=$node->nid" : "node.php?id=$node->nid"), "user" => $node->name, "date" => $node->created); - } - + // Return the results of performing a search using the indexed search + // for this particular type of node. + // + // Pass an array to the "do_search" function which dictates what it + // will search through, and what it will search for + // + // "keys"'s value is the keywords entered by the user + // + // "type"'s value is used to identify the node type in the search + // index. + // + // "select"'s value is used to relate the data from the specific nodes + // table to the data that the search_index table has in it, and the the + // do_search functino will rank it. + // + // The select must always provide the following fields - lno, title, + // created, uid, name, count + // + $find = do_search(array("keys" => $keys, + "type" => "node", + "select" => "select s.lno as lno, n.title as title, n.created as created, u.uid as uid, u.name as name, s.count as count FROM search_index s, node n LEFT JOIN users u ON n.uid = u.uid WHERE s.lno = n.nid AND s.type = 'node' AND s.word like '%' AND n.status = 1")); + return $find; } @@ -316,7 +333,7 @@ function node_filter_line($text) { ** Replace '<br>', '<br />', '<p>' and '<p />' by '\n': */ - $text = eregi_replace("<br>", "\n", $text); + $text = eregi_replace("<br />", "\n", $text); $text = eregi_replace("<br />", "\n", $text); $text = eregi_replace("<p>", "\n", $text); $text = eregi_replace("<p />", "\n", $text); @@ -772,7 +789,7 @@ function node_form($edit) { $function = $edit->type ."_form"; if (function_exists($function)) { - $form .= $function($edit, $help, $error); + $form .= $function($edit, $help, $error, $param); } /* @@ -857,7 +874,7 @@ function node_form($edit) { $output .= " </tr>"; $output .= "</table>"; - return form($output); + return form($output, ($param["method"] ? $param["method"] : "post"), $param["action"], $param["options"]); } function node_add($type) { @@ -1176,4 +1193,23 @@ function node_page() { $theme->footer(); } +function node_update_index() { + + // Return an array of values to dictate how to update the search index + // for this particular type of node. + // + // "last_update"'s value is used with variable_set to set the + // last time this node type had an index update run. + // + // "node_type"'s value is used to identify the node type in the search + // index. + // + // "select"'s value is used to select the node id and text fields from + // the table we are indexing. In this case, we also check against the + // last run date for the nodes update. + return array("last_update" => "node_cron_last", + "node_type" => "node", + "select" => "SELECT n.nid as lno, n.title as text1, n.body as text2 FROM node n WHERE n.status = 1 AND moderate = 0 and (created > " . variable_get("node_cron_last", 1) . " or changed > " . variable_get("node_cron_last", 1) . ")"); +} + ?> diff --git a/modules/node/node.module b/modules/node/node.module index 2b49a090de8b..6dab1419d853 100644 --- a/modules/node/node.module +++ b/modules/node/node.module @@ -48,7 +48,7 @@ function node_teaser($body) { function node_invoke($node, $name, $arg = 0) { if (is_array($node)) { - $function = $node[type] ."_$name"; + $function = $node["type"] ."_$name"; } else if (is_object($node)) { $function = $node->type ."_$name"; @@ -261,11 +261,28 @@ function node_perm() { function node_search($keys) { global $PHP_SELF; - $result = db_query("SELECT n.nid, n.title, n.created, u.uid, u.name FROM node n LEFT JOIN users u ON n.uid = u.uid WHERE n.status = 1 AND (n.title LIKE '%$keys%' OR n.teaser LIKE '%$keys%' OR n.body LIKE '%$keys%') ORDER BY n.created DESC LIMIT 20"); - while ($node = db_fetch_object($result)) { - $find[$i++] = array("title" => check_output($node->title), "link" => (strstr($PHP_SELF, "admin.php") ? "admin.php?mod=node&type=node&op=edit&id=$node->nid" : "node.php?id=$node->nid"), "user" => $node->name, "date" => $node->created); - } - + // Return the results of performing a search using the indexed search + // for this particular type of node. + // + // Pass an array to the "do_search" function which dictates what it + // will search through, and what it will search for + // + // "keys"'s value is the keywords entered by the user + // + // "type"'s value is used to identify the node type in the search + // index. + // + // "select"'s value is used to relate the data from the specific nodes + // table to the data that the search_index table has in it, and the the + // do_search functino will rank it. + // + // The select must always provide the following fields - lno, title, + // created, uid, name, count + // + $find = do_search(array("keys" => $keys, + "type" => "node", + "select" => "select s.lno as lno, n.title as title, n.created as created, u.uid as uid, u.name as name, s.count as count FROM search_index s, node n LEFT JOIN users u ON n.uid = u.uid WHERE s.lno = n.nid AND s.type = 'node' AND s.word like '%' AND n.status = 1")); + return $find; } @@ -316,7 +333,7 @@ function node_filter_line($text) { ** Replace '<br>', '<br />', '<p>' and '<p />' by '\n': */ - $text = eregi_replace("<br>", "\n", $text); + $text = eregi_replace("<br />", "\n", $text); $text = eregi_replace("<br />", "\n", $text); $text = eregi_replace("<p>", "\n", $text); $text = eregi_replace("<p />", "\n", $text); @@ -772,7 +789,7 @@ function node_form($edit) { $function = $edit->type ."_form"; if (function_exists($function)) { - $form .= $function($edit, $help, $error); + $form .= $function($edit, $help, $error, $param); } /* @@ -857,7 +874,7 @@ function node_form($edit) { $output .= " </tr>"; $output .= "</table>"; - return form($output); + return form($output, ($param["method"] ? $param["method"] : "post"), $param["action"], $param["options"]); } function node_add($type) { @@ -1176,4 +1193,23 @@ function node_page() { $theme->footer(); } +function node_update_index() { + + // Return an array of values to dictate how to update the search index + // for this particular type of node. + // + // "last_update"'s value is used with variable_set to set the + // last time this node type had an index update run. + // + // "node_type"'s value is used to identify the node type in the search + // index. + // + // "select"'s value is used to select the node id and text fields from + // the table we are indexing. In this case, we also check against the + // last run date for the nodes update. + return array("last_update" => "node_cron_last", + "node_type" => "node", + "select" => "SELECT n.nid as lno, n.title as text1, n.body as text2 FROM node n WHERE n.status = 1 AND moderate = 0 and (created > " . variable_get("node_cron_last", 1) . " or changed > " . variable_get("node_cron_last", 1) . ")"); +} + ?> diff --git a/modules/search.module b/modules/search.module index ac7f719a0648..ccf188f7b018 100644 --- a/modules/search.module +++ b/modules/search.module @@ -1,37 +1,362 @@ <?php // $Id$ +function search_help() { + $output = "<b>". t("Search hints") ."</b>"; + $output .= "<p>". t("The search allows you to search for words in the website's content. You can specify multiple words, and they will all be searched for, and the page that provides the highest hit count returned.") ."</p>"; + $output .= "<p>". t("As this website provides multiple content types, the results are grouped by content type as well. If you only wish to search through certain types of content, you can modify the behaviour of this search using the 'Restrict search to' checkboxes below.") ."</p>"; + $output .= "<p>". t("To specify that a word is <b>required</b> in the pages that are returned, place a '+' in front of it like this '+walk'.") ."</p>"; + $output .= "<p>". t("You can also use wildcards, so 'walk*' will match 'walk', 'walking', 'walker', 'walkable' and 'walkability'... Alright you got me, I made the last ones up.") ."</p>"; + $output .= "<p>". t("Searches are not case sensitive, regardless of how you type them all letters will be searched for in lower case") ."</p>"; + $output .= "<b>". t("Words excluded from the search") ."</b>"; + $output .= "<p>". t("Some words which commonly occur are filtered out by the searching process, these are commonly called 'noisewords'. Examples are 'a, at, and, are, as, ask', and the list goes on. Words shorter than ". variable_get("minimum_word_size", 2) ." letters are also filtered from the search index."); + $output .= "<p>". t("These words will never be matched when specified, even if they appear in the node you are searching for."); + return $output; +} + +/** + * Return an array of valid search access permissions + */ function search_perm() { - return array("search content"); + return array("search content", "administer search"); } +/** + * Return an array of links to be displayed + * + * @param $type The type of page requesting the link + * + */ function search_link($type) { if ($type == "page" && user_access("search content")) { $links[] = "<a href=\"module.php?mod=search\" title=\"". t("Search for older content.") ."\">". t("search") ."</a>"; } + if ($type == "admin" && user_access("administer search")) { + $links[] = "<a href=\"admin.php?mod=search\">". t("search") ."</a>"; + } + return $links ? $links : array(); } -/* -function search_item($item, $type) { - $output .= "<p>"; - $output .= " <b><u><a href=\"". $item["link"] ."\">". $item["title"] ."</a></u></b><br />"; - $output .= " <small>$type ". ($item["user"] ? " - ". $item["user"] : "") ."". ($item["date"] ? " - ". format_date($item["date"], "small") : "") ."</small>"; - $output .= "</p>"; +/** + * search engine administration actions + * + */ +function search_admin() { + global $op, $id, $edit; + + // Only allow people with sufficient access. + if (user_access("administer search")) { + switch ($op) { + case "Submit": + print status(search_save($edit)); + break; + case "reindex": + search_invalidate(); + print t("index invalidated") ."<br />\n"; + search_cron(); + print t("index recreated") ."<br /><hr />\n"; + break; + default: + } + print search_display(array("noisewords" => variable_get("noisewords", ""), "minimum_word_size" => variable_get("minimum_word_size", 2), "help_pos" => variable_get("help_pos", 1), "remove_short" => variable_get("remove_short", "0"))); + } + return; +} + +/** + * perform a regularly run action across all modules that have the + * <module>_update_index function in them. + * + */ +function search_cron() { + foreach (module_list() as $module) { + $module_array = module_invoke($module, "update_index"); + if ($module_array) { + update_index($module_array); + } + $module_array = null; + } + return; +} + +/** + * Perform a search on a word(s) + * + * Search function called by each node that supports the indexed search + * + * @param $search_array an array as returned from <module>_search + * of type array("keys" => ..., + * "type" => ..., "select" => ...) + * see node_search in node.module for an + * explanation of array items + */ +function do_search($search_array) { + global $PHP_SELF; + + $keys = strtolower($search_array["keys"]); + $type = $search_array["type"]; + $select = $search_array["select"]; + + // Replace wildcards with mysql wildcards + $keys = str_replace("*", "%", $keys); + + // Split the words entered into an array + $words = explode(" ", $keys); + + foreach ($words as $word) { + + // If the word is too short, and we've got it set to skip them, + // loop + if (strlen($word) < variable_get("remove_short", 0)) { + continue; + } + // If the word is preceeded by a "+", then this word is required, and + // pages that match other words, but not this one will be removed + if (substr($word, 0, 1) == "+") { + $word = substr($word, 1); + $required = 1; + $reqcount++; + $remove_rest = 1; + } + else { + $required = 0; + } + + // Put the next search word into the query and do the query + $query = preg_replace("'\%'", $word, $select); + $result = db_query($query); + + // If we got any results + if (db_num_rows($result) != 0) { + $found = 1; + + // Create an in memory array of the results, + while ($row = db_fetch_array($result)) { + $lno = $row["lno"]; + $nid = $row["nid"]; + $title = $row["title"]; + $created = $row["created"]; + $uid = $row["uid"]; + $name = $row["name"]; + $count = $row["count"]; + + // If the just fetched row is not already in the table + if ($results[$lno]["lno"] != $lno) { + $results[$lno]["count"] = $count; + + $results[$lno]["lno"] = $lno; + $results[$lno]["nid"] = $nid; + $results[$lno]["title"] = $title; + $results[$lno]["created"] = $created; + $results[$lno]["uid"] = $uid; + $results[$lno]["name"] = $name; + + // If this is a required word, set it to "valid" + if ($required == 1) { + $results[$lno]["valid"] = 1; + } + } + else { + // Different word, but existing "lno", increase the count of + // matches against this "lno" by the number of times this + // word appears in the text + $results[$lno]["count"] = $results[$lno]["count"] + $count; + + // Another match on the a required word, increase valid + if ($required == 1) { + $results[$lno]["valid"]++; + } + } + } + } + } + + if ($found) { + // Black magic here to sort the results + array_multisort($results, SORT_DESC); + + // OK, time to output the results. + foreach ($results as $key => $value) { + $lno = $value["lno"]; + $nid = $value["nid"]; + $title = $value["title"]; + $created = $value["created"]; + $uid = $value["uid"]; + $name = $value["name"]; + $count = $value["count"]; + if ($remove_rest) { + if ($value["valid"] != $reqcount) { + continue; + } + } + switch ($type) { + case "node": + $find[$i++] = array("count" => $count, "title" => check_output($title), "link" => (strstr($PHP_SELF, "admin.php") ? "admin.php?mod=node&type=node&op=edit&id=$lno" : "node.php?id=$lno"), "user" => $name, "date" => $created, "keywords" => implode("|", $words)); + break; + case "comment": + $find[$i++] = array("count" => $count, "title" => check_output($title), "link" => (strstr($PHP_SELF, "admin.php") ? "admin.php?mod=comment&op=edit&id=$lno" : "node.php?id=$nid&cid=$lno"), "user" => $name, "date" => $created, "keywords" => implode("|", $words)); + break; + } + } + } + + return $find; +} + +/** + * Update the search_index table + * + * @param $search_array an array as returned from <module>_update_index + * of type array("last_update" => ..., + * "node_type" => ..., "select" => ...) + * see node_update_index in node.module for an + * explanation of array items + */ +function update_index($search_array) { + $last_update = variable_get($search_array["last_update"], 1); + $node_type = $search_array["node_type"]; + $select = $search_array["select"]; + $minimum_word_size = variable_get("minimum_word_size", 2); + + //watchdog("user", "$last_update<br />$node_type<br />$select"); + + $result = db_query($select); + + if (db_num_rows($result)) { + // Wohoo, found some, look through the nodes we just selected + while ($node = db_fetch_array ($result)) { + + // Trash any existing entries in the search index for this node, + // in case its a modified node. + db_query("DELETE from search_index where lno = '". $node["lno"] ."' and type = '". $node_type ."'"); + + // Build the wordlist, teaser not included, as it then gives a + // false count of the number of hist, and doesn't show up + // when clicking on a node from the search interface anyway. + $wordlist = $node["text1"] . $node["text2"]; + + // Strip heaps of stuff out of it + $wordlist = preg_replace("'<[\/\!]*?[^<>]*?>'si", "", $wordlist); + + // Remove all numbers + $wordlist = preg_replace("'[0-9]'", "", $wordlist); + + // Remove punctuation and stuff + $wordlist = preg_replace("'(!|%|,|:|;|\(|\)|\&|\"|\'|\.|-|\/|\?|\\\)'", + "", + $wordlist); + + // Strip out (now mangled) http and tags. + $wordlist = preg_replace("'http\w+'", "", $wordlist); + $wordlist = preg_replace("'www\w+'", "", $wordlist); + + // Remove all newlines of any type + $wordlist = preg_replace("'([\r\n]|[\r]|[\n])'", " ", $wordlist); + + // Lower case the whole thing. + $wordlist = strtolower($wordlist); + + // Remove "noisewords" + $noise = explode("|", $noisewords); + foreach ($noise as $word) { + $wordlist = preg_replace("' $word '", " ", $wordlist); + } + + // Remove whitespace + $wordlist = preg_replace("'[\s]+'", " ", $wordlist); + + // Make it an array + $eachword = explode(" ", $wordlist); + + // walk through the array, giving a "weight" to each word, based on + // the number of times it appears in a page. + foreach ($eachword as $word) { + if (strlen($word) > $minimum_word_size) { + if ($newwords[$word]) { + $newwords[$word]++; + } + else { + $newwords[$word] = 1; + } + } + } + + // Walk through the weighted words array, inserting them into + // the search index + foreach ($newwords as $key => $value) { + $inputword = ("INSERT INTO search_index VALUES('$key', ". $node["lno"] .", '$node_type', $value)"); + mysql_query($inputword); + } + + // Zap the weighted words array, so we dont add multiples. + $newwords = array (); + } + } + + // update the last time this process was run. + variable_set($search_array["last_update"], time()); + + return true; +} + +/** + * Display the current search parameters for the administrator to be able + * to modify + * + * @param $edit An array of fields as setup via calling form_textfield, + * form_textarea etc + */ +function search_display($edit) { + $form = form_textfield(t("Minimum word size to index"), "minimum_word_size", $edit["minimum_word_size"], 10, 10); + $form .= form_textfield(t("Minimum word length to try and search for"), "remove_short", $edit["remove_short"], 10, 10); + $form .= form_textarea(t("Noisewords"), "noisewords", $edit["noisewords"], 70, 10); + $form .= form_select(t("Help text position"), "help_pos", $edit["help_pos"], array("1" => t("Above search form"), "2" => t("Below search form"), "3" => t("Link from above search form"), "4" => t("Link from below search form"))); + $form .= form_submit("Submit"); + + $links[] = "<a href=\"admin.php?mod=search&op=reindex\">reindex all</a>"; + + $output = "<small>". implode(" · ", $links) ."</small><hr />"; + + $output .= form($form); return $output; } -*/ -function search_page() { +function search_invalidate() { + foreach (module_list() as $module) { + $module_array = module_invoke($module, "update_index"); + if ($module_array) { + variable_set($module_array["last_update"], 1); + } + $module_array = null; + } + return; +} + +/** + * Save the values entered by the administrator for the search module + * + * @param $edit An array of fields as setup via calling form_textfield, + * form_textarea etc + */ +function search_save($edit) { + variable_set("minimum_word_size", $edit["minimum_word_size"]); + variable_set("noisewords", $edit["noisewords"]); + variable_set("help_pos", $edit["help_pos"]); + variable_set("remove_short", $edit["remove_short"]); +} + +function search_view() { global $theme, $edit, $type, $keys; if (user_access("search content")) { - + /* ** Verify the user input: */ + // TODO: is this necessary or is it / should it be done in search_{form|data}? $type = check_input($type); $keys = check_input($keys); @@ -40,38 +365,33 @@ function search_page() { ** Construct the search form: */ - $form .= " <input size=\"50\" value=\"". check_form($keys) ."\" name=\"keys\" type=\"text\" />"; - $form .= " <input type=\"submit\" value=\"". t("Search") ."\" /><br />"; - $form .= t("Restrict search to") .": "; - - foreach (module_list() as $name) { - if (module_hook($name, "user") || module_hook($name, "search")) { - $form .= " <input type=\"checkbox\" name=\"edit[type][$name]\"". ($edit["type"][$name] ? " checked=\"checked\"" : "") ." /> ". t($name); - } - } - - $form = form($form); + $form = search_form(NULL, NULL, TRUE); /* ** Collect the search results: */ - $array = array(); - - if ($keys) { - foreach (module_list() as $name) { - if ((!$edit["type"] || $edit["type"][$name]) && ($result = module_invoke($name, "search", $keys))) { - foreach ($result as $entry) { - $output .= search_item($entry, $name); - } - } - } - } + $output = search_data(); /* ** Display form and search results: */ + $help_link = "<a href=\"module.php?mod=search&op=help\">search help</a>"; + switch (variable_get("help_pos", 1)) { + case "1": + $form = search_help(). $form; + break; + case "2": + $form .= search_help(); + break; + case "3": + $form = $help_link. $form; + break; + case "4": + $form .= $help_link; + } + $theme->header(); if ($form) { @@ -88,12 +408,27 @@ function search_page() { } $theme->footer(); - } + } else { $theme->header(); $theme->box(t("Access denied"), message_access()); $theme->footer(); } + +} + +function search_page() { + global $theme, $op; + + switch ($op) { + case "help": + $theme->header(); + $theme->box(t("Search Help"), search_help()); + $theme->footer(); + break; + default: + search_view(); + } } ?> diff --git a/modules/search/search.module b/modules/search/search.module index ac7f719a0648..ccf188f7b018 100644 --- a/modules/search/search.module +++ b/modules/search/search.module @@ -1,37 +1,362 @@ <?php // $Id$ +function search_help() { + $output = "<b>". t("Search hints") ."</b>"; + $output .= "<p>". t("The search allows you to search for words in the website's content. You can specify multiple words, and they will all be searched for, and the page that provides the highest hit count returned.") ."</p>"; + $output .= "<p>". t("As this website provides multiple content types, the results are grouped by content type as well. If you only wish to search through certain types of content, you can modify the behaviour of this search using the 'Restrict search to' checkboxes below.") ."</p>"; + $output .= "<p>". t("To specify that a word is <b>required</b> in the pages that are returned, place a '+' in front of it like this '+walk'.") ."</p>"; + $output .= "<p>". t("You can also use wildcards, so 'walk*' will match 'walk', 'walking', 'walker', 'walkable' and 'walkability'... Alright you got me, I made the last ones up.") ."</p>"; + $output .= "<p>". t("Searches are not case sensitive, regardless of how you type them all letters will be searched for in lower case") ."</p>"; + $output .= "<b>". t("Words excluded from the search") ."</b>"; + $output .= "<p>". t("Some words which commonly occur are filtered out by the searching process, these are commonly called 'noisewords'. Examples are 'a, at, and, are, as, ask', and the list goes on. Words shorter than ". variable_get("minimum_word_size", 2) ." letters are also filtered from the search index."); + $output .= "<p>". t("These words will never be matched when specified, even if they appear in the node you are searching for."); + return $output; +} + +/** + * Return an array of valid search access permissions + */ function search_perm() { - return array("search content"); + return array("search content", "administer search"); } +/** + * Return an array of links to be displayed + * + * @param $type The type of page requesting the link + * + */ function search_link($type) { if ($type == "page" && user_access("search content")) { $links[] = "<a href=\"module.php?mod=search\" title=\"". t("Search for older content.") ."\">". t("search") ."</a>"; } + if ($type == "admin" && user_access("administer search")) { + $links[] = "<a href=\"admin.php?mod=search\">". t("search") ."</a>"; + } + return $links ? $links : array(); } -/* -function search_item($item, $type) { - $output .= "<p>"; - $output .= " <b><u><a href=\"". $item["link"] ."\">". $item["title"] ."</a></u></b><br />"; - $output .= " <small>$type ". ($item["user"] ? " - ". $item["user"] : "") ."". ($item["date"] ? " - ". format_date($item["date"], "small") : "") ."</small>"; - $output .= "</p>"; +/** + * search engine administration actions + * + */ +function search_admin() { + global $op, $id, $edit; + + // Only allow people with sufficient access. + if (user_access("administer search")) { + switch ($op) { + case "Submit": + print status(search_save($edit)); + break; + case "reindex": + search_invalidate(); + print t("index invalidated") ."<br />\n"; + search_cron(); + print t("index recreated") ."<br /><hr />\n"; + break; + default: + } + print search_display(array("noisewords" => variable_get("noisewords", ""), "minimum_word_size" => variable_get("minimum_word_size", 2), "help_pos" => variable_get("help_pos", 1), "remove_short" => variable_get("remove_short", "0"))); + } + return; +} + +/** + * perform a regularly run action across all modules that have the + * <module>_update_index function in them. + * + */ +function search_cron() { + foreach (module_list() as $module) { + $module_array = module_invoke($module, "update_index"); + if ($module_array) { + update_index($module_array); + } + $module_array = null; + } + return; +} + +/** + * Perform a search on a word(s) + * + * Search function called by each node that supports the indexed search + * + * @param $search_array an array as returned from <module>_search + * of type array("keys" => ..., + * "type" => ..., "select" => ...) + * see node_search in node.module for an + * explanation of array items + */ +function do_search($search_array) { + global $PHP_SELF; + + $keys = strtolower($search_array["keys"]); + $type = $search_array["type"]; + $select = $search_array["select"]; + + // Replace wildcards with mysql wildcards + $keys = str_replace("*", "%", $keys); + + // Split the words entered into an array + $words = explode(" ", $keys); + + foreach ($words as $word) { + + // If the word is too short, and we've got it set to skip them, + // loop + if (strlen($word) < variable_get("remove_short", 0)) { + continue; + } + // If the word is preceeded by a "+", then this word is required, and + // pages that match other words, but not this one will be removed + if (substr($word, 0, 1) == "+") { + $word = substr($word, 1); + $required = 1; + $reqcount++; + $remove_rest = 1; + } + else { + $required = 0; + } + + // Put the next search word into the query and do the query + $query = preg_replace("'\%'", $word, $select); + $result = db_query($query); + + // If we got any results + if (db_num_rows($result) != 0) { + $found = 1; + + // Create an in memory array of the results, + while ($row = db_fetch_array($result)) { + $lno = $row["lno"]; + $nid = $row["nid"]; + $title = $row["title"]; + $created = $row["created"]; + $uid = $row["uid"]; + $name = $row["name"]; + $count = $row["count"]; + + // If the just fetched row is not already in the table + if ($results[$lno]["lno"] != $lno) { + $results[$lno]["count"] = $count; + + $results[$lno]["lno"] = $lno; + $results[$lno]["nid"] = $nid; + $results[$lno]["title"] = $title; + $results[$lno]["created"] = $created; + $results[$lno]["uid"] = $uid; + $results[$lno]["name"] = $name; + + // If this is a required word, set it to "valid" + if ($required == 1) { + $results[$lno]["valid"] = 1; + } + } + else { + // Different word, but existing "lno", increase the count of + // matches against this "lno" by the number of times this + // word appears in the text + $results[$lno]["count"] = $results[$lno]["count"] + $count; + + // Another match on the a required word, increase valid + if ($required == 1) { + $results[$lno]["valid"]++; + } + } + } + } + } + + if ($found) { + // Black magic here to sort the results + array_multisort($results, SORT_DESC); + + // OK, time to output the results. + foreach ($results as $key => $value) { + $lno = $value["lno"]; + $nid = $value["nid"]; + $title = $value["title"]; + $created = $value["created"]; + $uid = $value["uid"]; + $name = $value["name"]; + $count = $value["count"]; + if ($remove_rest) { + if ($value["valid"] != $reqcount) { + continue; + } + } + switch ($type) { + case "node": + $find[$i++] = array("count" => $count, "title" => check_output($title), "link" => (strstr($PHP_SELF, "admin.php") ? "admin.php?mod=node&type=node&op=edit&id=$lno" : "node.php?id=$lno"), "user" => $name, "date" => $created, "keywords" => implode("|", $words)); + break; + case "comment": + $find[$i++] = array("count" => $count, "title" => check_output($title), "link" => (strstr($PHP_SELF, "admin.php") ? "admin.php?mod=comment&op=edit&id=$lno" : "node.php?id=$nid&cid=$lno"), "user" => $name, "date" => $created, "keywords" => implode("|", $words)); + break; + } + } + } + + return $find; +} + +/** + * Update the search_index table + * + * @param $search_array an array as returned from <module>_update_index + * of type array("last_update" => ..., + * "node_type" => ..., "select" => ...) + * see node_update_index in node.module for an + * explanation of array items + */ +function update_index($search_array) { + $last_update = variable_get($search_array["last_update"], 1); + $node_type = $search_array["node_type"]; + $select = $search_array["select"]; + $minimum_word_size = variable_get("minimum_word_size", 2); + + //watchdog("user", "$last_update<br />$node_type<br />$select"); + + $result = db_query($select); + + if (db_num_rows($result)) { + // Wohoo, found some, look through the nodes we just selected + while ($node = db_fetch_array ($result)) { + + // Trash any existing entries in the search index for this node, + // in case its a modified node. + db_query("DELETE from search_index where lno = '". $node["lno"] ."' and type = '". $node_type ."'"); + + // Build the wordlist, teaser not included, as it then gives a + // false count of the number of hist, and doesn't show up + // when clicking on a node from the search interface anyway. + $wordlist = $node["text1"] . $node["text2"]; + + // Strip heaps of stuff out of it + $wordlist = preg_replace("'<[\/\!]*?[^<>]*?>'si", "", $wordlist); + + // Remove all numbers + $wordlist = preg_replace("'[0-9]'", "", $wordlist); + + // Remove punctuation and stuff + $wordlist = preg_replace("'(!|%|,|:|;|\(|\)|\&|\"|\'|\.|-|\/|\?|\\\)'", + "", + $wordlist); + + // Strip out (now mangled) http and tags. + $wordlist = preg_replace("'http\w+'", "", $wordlist); + $wordlist = preg_replace("'www\w+'", "", $wordlist); + + // Remove all newlines of any type + $wordlist = preg_replace("'([\r\n]|[\r]|[\n])'", " ", $wordlist); + + // Lower case the whole thing. + $wordlist = strtolower($wordlist); + + // Remove "noisewords" + $noise = explode("|", $noisewords); + foreach ($noise as $word) { + $wordlist = preg_replace("' $word '", " ", $wordlist); + } + + // Remove whitespace + $wordlist = preg_replace("'[\s]+'", " ", $wordlist); + + // Make it an array + $eachword = explode(" ", $wordlist); + + // walk through the array, giving a "weight" to each word, based on + // the number of times it appears in a page. + foreach ($eachword as $word) { + if (strlen($word) > $minimum_word_size) { + if ($newwords[$word]) { + $newwords[$word]++; + } + else { + $newwords[$word] = 1; + } + } + } + + // Walk through the weighted words array, inserting them into + // the search index + foreach ($newwords as $key => $value) { + $inputword = ("INSERT INTO search_index VALUES('$key', ". $node["lno"] .", '$node_type', $value)"); + mysql_query($inputword); + } + + // Zap the weighted words array, so we dont add multiples. + $newwords = array (); + } + } + + // update the last time this process was run. + variable_set($search_array["last_update"], time()); + + return true; +} + +/** + * Display the current search parameters for the administrator to be able + * to modify + * + * @param $edit An array of fields as setup via calling form_textfield, + * form_textarea etc + */ +function search_display($edit) { + $form = form_textfield(t("Minimum word size to index"), "minimum_word_size", $edit["minimum_word_size"], 10, 10); + $form .= form_textfield(t("Minimum word length to try and search for"), "remove_short", $edit["remove_short"], 10, 10); + $form .= form_textarea(t("Noisewords"), "noisewords", $edit["noisewords"], 70, 10); + $form .= form_select(t("Help text position"), "help_pos", $edit["help_pos"], array("1" => t("Above search form"), "2" => t("Below search form"), "3" => t("Link from above search form"), "4" => t("Link from below search form"))); + $form .= form_submit("Submit"); + + $links[] = "<a href=\"admin.php?mod=search&op=reindex\">reindex all</a>"; + + $output = "<small>". implode(" · ", $links) ."</small><hr />"; + + $output .= form($form); return $output; } -*/ -function search_page() { +function search_invalidate() { + foreach (module_list() as $module) { + $module_array = module_invoke($module, "update_index"); + if ($module_array) { + variable_set($module_array["last_update"], 1); + } + $module_array = null; + } + return; +} + +/** + * Save the values entered by the administrator for the search module + * + * @param $edit An array of fields as setup via calling form_textfield, + * form_textarea etc + */ +function search_save($edit) { + variable_set("minimum_word_size", $edit["minimum_word_size"]); + variable_set("noisewords", $edit["noisewords"]); + variable_set("help_pos", $edit["help_pos"]); + variable_set("remove_short", $edit["remove_short"]); +} + +function search_view() { global $theme, $edit, $type, $keys; if (user_access("search content")) { - + /* ** Verify the user input: */ + // TODO: is this necessary or is it / should it be done in search_{form|data}? $type = check_input($type); $keys = check_input($keys); @@ -40,38 +365,33 @@ function search_page() { ** Construct the search form: */ - $form .= " <input size=\"50\" value=\"". check_form($keys) ."\" name=\"keys\" type=\"text\" />"; - $form .= " <input type=\"submit\" value=\"". t("Search") ."\" /><br />"; - $form .= t("Restrict search to") .": "; - - foreach (module_list() as $name) { - if (module_hook($name, "user") || module_hook($name, "search")) { - $form .= " <input type=\"checkbox\" name=\"edit[type][$name]\"". ($edit["type"][$name] ? " checked=\"checked\"" : "") ." /> ". t($name); - } - } - - $form = form($form); + $form = search_form(NULL, NULL, TRUE); /* ** Collect the search results: */ - $array = array(); - - if ($keys) { - foreach (module_list() as $name) { - if ((!$edit["type"] || $edit["type"][$name]) && ($result = module_invoke($name, "search", $keys))) { - foreach ($result as $entry) { - $output .= search_item($entry, $name); - } - } - } - } + $output = search_data(); /* ** Display form and search results: */ + $help_link = "<a href=\"module.php?mod=search&op=help\">search help</a>"; + switch (variable_get("help_pos", 1)) { + case "1": + $form = search_help(). $form; + break; + case "2": + $form .= search_help(); + break; + case "3": + $form = $help_link. $form; + break; + case "4": + $form .= $help_link; + } + $theme->header(); if ($form) { @@ -88,12 +408,27 @@ function search_page() { } $theme->footer(); - } + } else { $theme->header(); $theme->box(t("Access denied"), message_access()); $theme->footer(); } + +} + +function search_page() { + global $theme, $op; + + switch ($op) { + case "help": + $theme->header(); + $theme->box(t("Search Help"), search_help()); + $theme->footer(); + break; + default: + search_view(); + } } ?> diff --git a/modules/system.module b/modules/system.module index c4a2bfb317bc..c8cdc99aecfd 100644 --- a/modules/system.module +++ b/modules/system.module @@ -40,14 +40,14 @@ function system_view_options() { // general settings: $output .= "<h3>General settings</h3>\n"; $output .= form_textfield("Name", "site_name", variable_get("site_name", "drupal"), 55, 55, "The name of this website."); - $output .= form_textfield("E-mail address", "site_mail", variable_get("site_mail", ini_get("sendmail_from")), 55, 128, "A valid e-mail address for this website, used by the auto-mailer to create new user accounts."); - $output .= form_textfield("Slogan", "site_slogan", variable_get("site_slogan", ""), 55, 128, "The slogan of this website"); - $output .= form_textarea("Mission", "site_mission", variable_get("site_mission", ""), 55, 5, "Your site's mission statement or focus."); + $output .= form_textfield("E-mail address", "site_mail", variable_get("site_mail", ini_get("sendmail_from")), 55, 128, "A valid e-mail address for this website, used by the auto-mailer during registration, new password requests, notifications, etc."); + $output .= form_textfield("Slogan", "site_slogan", variable_get("site_slogan", ""), 55, 128, "The slogan of this website. Some themes display a slogan when available."); + $output .= form_textarea("Mission", "site_mission", variable_get("site_mission", ""), 55, 5, "Your site's mission statement or focus. When enabled, this mission is listed at Drupal.org"); $output .= form_textarea("Footer message", "site_footer", variable_get("site_footer", ""), 55, 5, "This text will be displayed at the bottom of each page. Useful for adding a copyright notice to your pages."); $output .= form_textfield("Anonymous user", "anonymous", variable_get("anonymous", "Anonymous"), 55, 55, "The name used to indicate anonymous users."); foreach (module_list() as $name) { if (module_hook($name, "page")) $pages[$name] = $name; } - $output .= form_select("Default front page", "site_frontpage", variable_get("site_frontpage", "node"), $pages, "The default front page."); - $output .= form_textfield("Extra front page settings", "site_frontpage_extra", variable_get("site_frontpage_extra", ""), 55, 128, "Lets you define additonal variables for the main page in PHP."); + $output .= form_select("Default front page", "site_frontpage", variable_get("site_frontpage", "node"), $pages, "The home page displays content from this module (usually <b>node</b>)."); + $output .= form_textarea("Extra front page PHP", "site_frontpage_extra", variable_get("site_frontpage_extra", ""), 55, 5, "Insert arbitrary PHP into the home page. This PHP executes via <b>eval()</b> after the page header but before the main page content."); $output .= "<hr />\n"; // caching: diff --git a/modules/system/system.module b/modules/system/system.module index c4a2bfb317bc..c8cdc99aecfd 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -40,14 +40,14 @@ function system_view_options() { // general settings: $output .= "<h3>General settings</h3>\n"; $output .= form_textfield("Name", "site_name", variable_get("site_name", "drupal"), 55, 55, "The name of this website."); - $output .= form_textfield("E-mail address", "site_mail", variable_get("site_mail", ini_get("sendmail_from")), 55, 128, "A valid e-mail address for this website, used by the auto-mailer to create new user accounts."); - $output .= form_textfield("Slogan", "site_slogan", variable_get("site_slogan", ""), 55, 128, "The slogan of this website"); - $output .= form_textarea("Mission", "site_mission", variable_get("site_mission", ""), 55, 5, "Your site's mission statement or focus."); + $output .= form_textfield("E-mail address", "site_mail", variable_get("site_mail", ini_get("sendmail_from")), 55, 128, "A valid e-mail address for this website, used by the auto-mailer during registration, new password requests, notifications, etc."); + $output .= form_textfield("Slogan", "site_slogan", variable_get("site_slogan", ""), 55, 128, "The slogan of this website. Some themes display a slogan when available."); + $output .= form_textarea("Mission", "site_mission", variable_get("site_mission", ""), 55, 5, "Your site's mission statement or focus. When enabled, this mission is listed at Drupal.org"); $output .= form_textarea("Footer message", "site_footer", variable_get("site_footer", ""), 55, 5, "This text will be displayed at the bottom of each page. Useful for adding a copyright notice to your pages."); $output .= form_textfield("Anonymous user", "anonymous", variable_get("anonymous", "Anonymous"), 55, 55, "The name used to indicate anonymous users."); foreach (module_list() as $name) { if (module_hook($name, "page")) $pages[$name] = $name; } - $output .= form_select("Default front page", "site_frontpage", variable_get("site_frontpage", "node"), $pages, "The default front page."); - $output .= form_textfield("Extra front page settings", "site_frontpage_extra", variable_get("site_frontpage_extra", ""), 55, 128, "Lets you define additonal variables for the main page in PHP."); + $output .= form_select("Default front page", "site_frontpage", variable_get("site_frontpage", "node"), $pages, "The home page displays content from this module (usually <b>node</b>)."); + $output .= form_textarea("Extra front page PHP", "site_frontpage_extra", variable_get("site_frontpage_extra", ""), 55, 5, "Insert arbitrary PHP into the home page. This PHP executes via <b>eval()</b> after the page header but before the main page content."); $output .= "<hr />\n"; // caching: diff --git a/modules/user.module b/modules/user.module index 5fb552a2ef80..5bde04273bf9 100644 --- a/modules/user.module +++ b/modules/user.module @@ -24,7 +24,7 @@ function sess_read($key) { function sess_write($key, $value) { global $HTTP_SERVER_VARS; - db_query("UPDATE users SET hostname = '". check_input($HTTP_SERVER_VARS[REMOTE_ADDR]) ."', session = '". check_query($value) ."', timestamp = '". time() ."' WHERE sid = '$key'"); + db_query("UPDATE users SET hostname = '". check_input($HTTP_SERVER_VARS["REMOTE_ADDR"]) ."', session = '". check_query($value) ."', timestamp = '". time() ."' WHERE sid = '$key'"); return ''; } @@ -32,7 +32,7 @@ function sess_write($key, $value) { function sess_destroy($key) { global $HTTP_SERVER_VARS; - db_query("UPDATE users SET hostname = '". check_input($HTTP_SERVER_VARS[REMOTE_ADDR]) ."', timestamp = '". time() ."', sid = '' WHERE sid = '$key'"); + db_query("UPDATE users SET hostname = '". check_input($HTTP_SERVER_VARS["REMOTE_ADDR"]) ."', timestamp = '". time() ."', sid = '' WHERE sid = '$key'"); } function sess_gc($lifetime) { @@ -63,7 +63,7 @@ function user_load($array = array()) { foreach ($array as $key => $value) { if ($key == "pass") { - $query .= "u.$key = '" . md5($value) . "' AND "; + $query .= "u.$key = '". md5($value) ."' AND "; } else { $query .= "u.$key = '". addslashes($value) ."' AND "; @@ -482,6 +482,23 @@ function user_block() { $block[0]["info"] = t("User information"); $block[0]["link"] = "module.php?mod=user"; + // Who's online block + $time = 60 * 60; // minutes * seconds + $limit = 0; // List the X most recent people + + $result = db_query("SELECT uid, name FROM users WHERE timestamp > unix_timestamp() - ($time) ORDER BY timestamp DESC LIMIT $limit"); + + if (db_num_rows($result)) { + $output = "<ol>"; + while ($account = db_fetch_object($result)) { + $output .= '<li><a href="module.php?mod=user&op=view&id='. $account->uid .'">'. (strlen($account->name) > 10 ? substr($account->name, 0, 10) . '...' : $account->name) .'</a></li>'; + } + $output .= "</ol>"; + $block[1]["content"] = $output; + } + $block[1]["subject"] = t("Who's online"); + $block[1]["info"] = t("Who's online"); + return $block; } @@ -724,7 +741,7 @@ function user_login($edit = array()) { } else { if (!$error) { - $error = t("Authentication failed."); + $error = sprintf(t("Sorry. Unrecognized username or password. Have you %sforgotten your password%s?"),"<a href=\"/module.php?mod=user&op=password\">","</a>"); } if ($server) { watchdog("user", "failed login for '$name@$server': $error"); @@ -740,7 +757,7 @@ function user_login($edit = array()) { */ if ($error) { - $output .= "<p><span style=\"color: red;\">". check_output($error) ."</span></p>"; + $output .= "<p><span style=\"color: red;\" class=\"error\">". check_output($error) ."</span></p>"; } /* @@ -778,8 +795,15 @@ function user_logout() { function user_pass($edit = array()) { - if ($edit["name"] && $edit["mail"]) { - if ($account = db_fetch_object(db_query("SELECT uid FROM users WHERE name = '". check_input($edit["name"]) ."' AND mail = '". check_input($edit["mail"]) ."'"))) { + if ($edit["name"]) { + $account = db_fetch_object(db_query("SELECT uid FROM users WHERE name = '". check_input($edit["name"]) . "'")); + if (!$account) $error = sprintf(t("Sorry. The username <i>%s</i> is not recognized."), $edit["name"]); + } + else if ($edit["mail"]) { + $account = db_fetch_object(db_query("SELECT uid FROM users WHERE mail = '". check_input($edit["mail"]) ."'")); + if (!$account) $error = sprintf(t("Sorry. The e-mail address <i>%s</i> is not recognized."), $edit["mail"]); + } + if ($account) { $from = variable_get("site_mail", ini_get("sendmail_from")); $pass = user_password(); @@ -801,17 +825,17 @@ function user_pass($edit = array()) { return t("Your password and further instructions have been sent to your e-mail address."); } else { - watchdog("user", "mail password: '". $edit["name"] ."' and <". $edit["mail"] ."> do not match"); - - return t("Could not send password: no match for the specified username and e-mail address."); + + // Display error message if necessary. + if ($error) { + $output .= "<p><span style=\"color: red;\" class=\"error\">". check_output($error) ."</span></p>"; } - } - else { /* ** Display form: */ + $output .= sprintf(t("%sEnter your username %sor%s your email address.%s"), "<p>", "<b><i>", "</i></b>", "</p>"); $output .= form_textfield(t("Username"), "name", $edit["name"], 30, 64); $output .= form_textfield(t("E-mail address"), "mail", $edit["mail"], 30, 64); $output .= form_submit(t("E-mail new password")); @@ -894,7 +918,7 @@ function user_register($edit = array()) { else { if ($error) { - $output .= "<p><span style=\"color: red;\">". check_output($error) ."</span></p>"; + $output .= "<p><span style=\"color: red;\" class=\"error\">". check_output($error) ."</span></p>"; } $output .= form_textfield(t("Username"), "name", $edit["name"], 30, 64, t("Your full name or your prefered username: only letters, numbers and spaces are allowed.")); @@ -910,6 +934,24 @@ function user_register($edit = array()) { } } + +function user_delete() { + global $edit, $user; + + if ($edit["confirm"]) { + watchdog(user,"$user->name deactivated her own account."); + db_query("UPDATE users SET mail = 'deleted', status='0' WHERE uid = '$user->uid'"); + $output .= t("Your account has been deactivated."); + } + else { + $output .= form_item(t("Confirm Deletion"), t("You are about to deactivate your own user account. In addition, your email address will be removed from the database.")); + $output .= form_hidden("confirm", 1); + $output .= form_submit(t("Delete account")); + $output = form($output); + } + return $output; +} + function user_edit($edit = array()) { global $HTTP_HOST, $themes, $user, $languages; @@ -967,17 +1009,13 @@ function user_edit($edit = array()) { $user = user_save($user, array_merge($edit, $data)); - /* - ** Redirect the user to his personal information page: - */ - - drupal_goto("module.php?mod=user&op=view"); + $output .= sprintf(t("Your user information changes have been saved."), "<p><b>", "</b></p>"); } } } if ($error) { - $output .= "<p><span style=\"color: red;\">". check_output($error) ."</span></p>"; + $output .= "<p><span style=\"color: red;\" class=\"error\">". check_output($error) ."</span></p>"; } $output .= form_textfield(t("Username"), "name", $user->name, 30, 55, t("Your full name or your prefered username: only letters, numbers and spaces are allowed.")); @@ -1005,7 +1043,7 @@ function user_edit($edit = array()) { $output .= form_textarea(t("Signature"), "signature", $user->signature, 70, 3, t("Your signature will be publicly displayed at the end of your comments.") ."<br />". t("Allowed HTML tags") .": ". htmlspecialchars(variable_get("allowed_html", ""))); $output .= form_item(t("Password"), "<input type=\"password\" name=\"edit[pass1]\" size=\"12\" maxlength=\"24\" /> <input type=\"password\" name=\"edit[pass2]\" size=\"12\" maxlength=\"24\" />", t("Enter your new password twice if you want to change your current password or leave it blank if you are happy with your current password.")); $output .= form_submit(t("Save user information")); - + $output = form($output); } @@ -1015,6 +1053,7 @@ function user_edit($edit = array()) { function user_menu() { $links[] = "<a href=\"module.php?mod=user&op=view\">". t("view user information") ."</a>"; $links[] = "<a href=\"module.php?mod=user&op=edit\">". t("edit user information") ."</a>"; + $links[] = "<a href=\"module.php?mod=user&op=delete\">". t("delete account") ."</a>"; return "<div align=\"center\">". implode(" · ", $links) ."</div>"; } @@ -1101,7 +1140,15 @@ function user_page() { $theme->box(t("Log in"), $output); $theme->footer(); break; - case t("Save user information"): + case t("Delete account"): + case t("delete"); + $output = user_delete(); + $theme->header(); + $theme->box(t("User account"), user_menu()); + $theme->box(t("Delete account"), $output); + $theme->footer(); + break; + case t("Save user information"): case "edit": $output = user_edit($edit); $theme->header(); @@ -1193,7 +1240,7 @@ function user_admin_create($edit = array()) { else { if ($error) { - $output .= "<p><span style=\"color: red;\">". check_output($error) ."</span></p>"; + $output .= "<p><span style=\"color: red;\" class=\"error\">". check_output($error) ."</span></p>"; } $output .= form_textfield("Username", "name", $edit["name"], 30, 55); diff --git a/modules/user/user.module b/modules/user/user.module index 5fb552a2ef80..5bde04273bf9 100644 --- a/modules/user/user.module +++ b/modules/user/user.module @@ -24,7 +24,7 @@ function sess_read($key) { function sess_write($key, $value) { global $HTTP_SERVER_VARS; - db_query("UPDATE users SET hostname = '". check_input($HTTP_SERVER_VARS[REMOTE_ADDR]) ."', session = '". check_query($value) ."', timestamp = '". time() ."' WHERE sid = '$key'"); + db_query("UPDATE users SET hostname = '". check_input($HTTP_SERVER_VARS["REMOTE_ADDR"]) ."', session = '". check_query($value) ."', timestamp = '". time() ."' WHERE sid = '$key'"); return ''; } @@ -32,7 +32,7 @@ function sess_write($key, $value) { function sess_destroy($key) { global $HTTP_SERVER_VARS; - db_query("UPDATE users SET hostname = '". check_input($HTTP_SERVER_VARS[REMOTE_ADDR]) ."', timestamp = '". time() ."', sid = '' WHERE sid = '$key'"); + db_query("UPDATE users SET hostname = '". check_input($HTTP_SERVER_VARS["REMOTE_ADDR"]) ."', timestamp = '". time() ."', sid = '' WHERE sid = '$key'"); } function sess_gc($lifetime) { @@ -63,7 +63,7 @@ function user_load($array = array()) { foreach ($array as $key => $value) { if ($key == "pass") { - $query .= "u.$key = '" . md5($value) . "' AND "; + $query .= "u.$key = '". md5($value) ."' AND "; } else { $query .= "u.$key = '". addslashes($value) ."' AND "; @@ -482,6 +482,23 @@ function user_block() { $block[0]["info"] = t("User information"); $block[0]["link"] = "module.php?mod=user"; + // Who's online block + $time = 60 * 60; // minutes * seconds + $limit = 0; // List the X most recent people + + $result = db_query("SELECT uid, name FROM users WHERE timestamp > unix_timestamp() - ($time) ORDER BY timestamp DESC LIMIT $limit"); + + if (db_num_rows($result)) { + $output = "<ol>"; + while ($account = db_fetch_object($result)) { + $output .= '<li><a href="module.php?mod=user&op=view&id='. $account->uid .'">'. (strlen($account->name) > 10 ? substr($account->name, 0, 10) . '...' : $account->name) .'</a></li>'; + } + $output .= "</ol>"; + $block[1]["content"] = $output; + } + $block[1]["subject"] = t("Who's online"); + $block[1]["info"] = t("Who's online"); + return $block; } @@ -724,7 +741,7 @@ function user_login($edit = array()) { } else { if (!$error) { - $error = t("Authentication failed."); + $error = sprintf(t("Sorry. Unrecognized username or password. Have you %sforgotten your password%s?"),"<a href=\"/module.php?mod=user&op=password\">","</a>"); } if ($server) { watchdog("user", "failed login for '$name@$server': $error"); @@ -740,7 +757,7 @@ function user_login($edit = array()) { */ if ($error) { - $output .= "<p><span style=\"color: red;\">". check_output($error) ."</span></p>"; + $output .= "<p><span style=\"color: red;\" class=\"error\">". check_output($error) ."</span></p>"; } /* @@ -778,8 +795,15 @@ function user_logout() { function user_pass($edit = array()) { - if ($edit["name"] && $edit["mail"]) { - if ($account = db_fetch_object(db_query("SELECT uid FROM users WHERE name = '". check_input($edit["name"]) ."' AND mail = '". check_input($edit["mail"]) ."'"))) { + if ($edit["name"]) { + $account = db_fetch_object(db_query("SELECT uid FROM users WHERE name = '". check_input($edit["name"]) . "'")); + if (!$account) $error = sprintf(t("Sorry. The username <i>%s</i> is not recognized."), $edit["name"]); + } + else if ($edit["mail"]) { + $account = db_fetch_object(db_query("SELECT uid FROM users WHERE mail = '". check_input($edit["mail"]) ."'")); + if (!$account) $error = sprintf(t("Sorry. The e-mail address <i>%s</i> is not recognized."), $edit["mail"]); + } + if ($account) { $from = variable_get("site_mail", ini_get("sendmail_from")); $pass = user_password(); @@ -801,17 +825,17 @@ function user_pass($edit = array()) { return t("Your password and further instructions have been sent to your e-mail address."); } else { - watchdog("user", "mail password: '". $edit["name"] ."' and <". $edit["mail"] ."> do not match"); - - return t("Could not send password: no match for the specified username and e-mail address."); + + // Display error message if necessary. + if ($error) { + $output .= "<p><span style=\"color: red;\" class=\"error\">". check_output($error) ."</span></p>"; } - } - else { /* ** Display form: */ + $output .= sprintf(t("%sEnter your username %sor%s your email address.%s"), "<p>", "<b><i>", "</i></b>", "</p>"); $output .= form_textfield(t("Username"), "name", $edit["name"], 30, 64); $output .= form_textfield(t("E-mail address"), "mail", $edit["mail"], 30, 64); $output .= form_submit(t("E-mail new password")); @@ -894,7 +918,7 @@ function user_register($edit = array()) { else { if ($error) { - $output .= "<p><span style=\"color: red;\">". check_output($error) ."</span></p>"; + $output .= "<p><span style=\"color: red;\" class=\"error\">". check_output($error) ."</span></p>"; } $output .= form_textfield(t("Username"), "name", $edit["name"], 30, 64, t("Your full name or your prefered username: only letters, numbers and spaces are allowed.")); @@ -910,6 +934,24 @@ function user_register($edit = array()) { } } + +function user_delete() { + global $edit, $user; + + if ($edit["confirm"]) { + watchdog(user,"$user->name deactivated her own account."); + db_query("UPDATE users SET mail = 'deleted', status='0' WHERE uid = '$user->uid'"); + $output .= t("Your account has been deactivated."); + } + else { + $output .= form_item(t("Confirm Deletion"), t("You are about to deactivate your own user account. In addition, your email address will be removed from the database.")); + $output .= form_hidden("confirm", 1); + $output .= form_submit(t("Delete account")); + $output = form($output); + } + return $output; +} + function user_edit($edit = array()) { global $HTTP_HOST, $themes, $user, $languages; @@ -967,17 +1009,13 @@ function user_edit($edit = array()) { $user = user_save($user, array_merge($edit, $data)); - /* - ** Redirect the user to his personal information page: - */ - - drupal_goto("module.php?mod=user&op=view"); + $output .= sprintf(t("Your user information changes have been saved."), "<p><b>", "</b></p>"); } } } if ($error) { - $output .= "<p><span style=\"color: red;\">". check_output($error) ."</span></p>"; + $output .= "<p><span style=\"color: red;\" class=\"error\">". check_output($error) ."</span></p>"; } $output .= form_textfield(t("Username"), "name", $user->name, 30, 55, t("Your full name or your prefered username: only letters, numbers and spaces are allowed.")); @@ -1005,7 +1043,7 @@ function user_edit($edit = array()) { $output .= form_textarea(t("Signature"), "signature", $user->signature, 70, 3, t("Your signature will be publicly displayed at the end of your comments.") ."<br />". t("Allowed HTML tags") .": ". htmlspecialchars(variable_get("allowed_html", ""))); $output .= form_item(t("Password"), "<input type=\"password\" name=\"edit[pass1]\" size=\"12\" maxlength=\"24\" /> <input type=\"password\" name=\"edit[pass2]\" size=\"12\" maxlength=\"24\" />", t("Enter your new password twice if you want to change your current password or leave it blank if you are happy with your current password.")); $output .= form_submit(t("Save user information")); - + $output = form($output); } @@ -1015,6 +1053,7 @@ function user_edit($edit = array()) { function user_menu() { $links[] = "<a href=\"module.php?mod=user&op=view\">". t("view user information") ."</a>"; $links[] = "<a href=\"module.php?mod=user&op=edit\">". t("edit user information") ."</a>"; + $links[] = "<a href=\"module.php?mod=user&op=delete\">". t("delete account") ."</a>"; return "<div align=\"center\">". implode(" · ", $links) ."</div>"; } @@ -1101,7 +1140,15 @@ function user_page() { $theme->box(t("Log in"), $output); $theme->footer(); break; - case t("Save user information"): + case t("Delete account"): + case t("delete"); + $output = user_delete(); + $theme->header(); + $theme->box(t("User account"), user_menu()); + $theme->box(t("Delete account"), $output); + $theme->footer(); + break; + case t("Save user information"): case "edit": $output = user_edit($edit); $theme->header(); @@ -1193,7 +1240,7 @@ function user_admin_create($edit = array()) { else { if ($error) { - $output .= "<p><span style=\"color: red;\">". check_output($error) ."</span></p>"; + $output .= "<p><span style=\"color: red;\" class=\"error\">". check_output($error) ."</span></p>"; } $output .= form_textfield("Username", "name", $edit["name"], 30, 55); diff --git a/modules/weblogs.module b/modules/weblogs.module index c4f06ea3f51e..3619124bf0b3 100644 --- a/modules/weblogs.module +++ b/modules/weblogs.module @@ -1,6 +1,12 @@ <?php // $Id$ +function weblogs_conf_options() { + $output .= form_select("Weblogs ping", "weblogs_ping", variable_get("weblogs_ping", 0), array("Disabled", "Enabled"), "Enabling Weblogs ping will inform <a href=\"http://www.weblogs.com/\">Weblogs.com</a> when your site updates."); + + return $output; +} + function weblogs_help() { $output .= "<p><a href=\"http://www.weblogs.com/\">Weblogs.com</a> is a website that tracks and displays updates to weblogs and news-oriented websites. To get your Drupal site listed, weblogs.com must be informed about your site's updates. This is the job of the weblog module and when installed, the administrator doesn't have to do anything to participate in the <a href=\"http://www.weblogs.com/\">weblogs.com system</a>. The weblog module automatically notifies weblogs.com when your site is updated. To do so, Drupal implements the <a href=\"http://www.xmlrpc.com/weblogsCom/\">XML-RPC interface of weblogs.com</a>.</p>"; return $output; @@ -8,11 +14,13 @@ function weblogs_help() { function weblogs_cron() { - if (db_num_rows(db_query("SELECT nid FROM node WHERE status = 1 AND moderate = 0 AND (created > '". variable_get("weblogs_cron_last", time()) ."' OR changed > '". variable_get("weblogs_cron_last", time()) ."')"), 1)) { - weblogs_notify(variable_get("site_name", "drupal") ." - ". variable_get("site_slogan", ""), path_uri()); - } + if (variable_get("weblogs_ping", 0) && variable_get("site_name", 0) && variable_get("site_url", 0)) { + if (db_num_rows(db_query("SELECT nid FROM node WHERE status = 1 AND moderate = 0 AND (created > '". variable_get("weblogs_cron_last", time()) ."' OR changed > '". variable_get("weblogs_cron_last", time()) ."')"), 1)) { + weblogs_notify(variable_get("site_name", "") ." - ". variable_get("site_slogan", ""), variable_get("site_url", "")); + } - variable_set("weblogs_cron_last", time()); + variable_set("weblogs_cron_last", time()); + } } function weblogs_notify($name, $url) { diff --git a/update.php b/update.php index 31899188f24e..9a22969c105e 100644 --- a/update.php +++ b/update.php @@ -46,7 +46,8 @@ "2002-01-17" => "update_19", "2002-01-27" => "update_20", "2002-01-30" => "update_21", - "2002-02-19" => "update_22" + "2002-02-19" => "update_22", + "2002-03-05" => "update_23" ); // Update functions @@ -332,6 +333,10 @@ function update_22() { update_sql("ALTER TABLE cache MODIFY data MEDIUMTEXT;"); } +function update_23() { + update_sql("CREATE TABLE search_index (word varchar(50) default NULL, lno int(10) unsigned default NULL, type varchar(16) default NULL, count int(10) unsigned default NULL, KEY lno (lno), KEY word (word);"); +} + /* ** System functions */ -- GitLab