Commit 6f08c442 authored by Leigh Stoller's avatar Leigh Stoller

The Emulab Knowledge Base!

Okay, I implemented a primitive Knowledge Base! The current contents are
*all* the existing FAQ entries, which I entered manually. Here are the
details.

* My reason for doing this is that we need something very simple. The wiki
  is too much of a barrier, and its search capabilities are pathetic.

* The search page for the Knowledge Base is:

	https://www.emulab.net/kb-search.php3

  Fairly primitive keyword search. Turns out that mysql 4.0 has a bunch for
  really good text searching functions built in, but we run 3.23 ... so I
  had to roll it myself. So, its a simple keyword (space or comma
  separated) search, no regular expressions.

* Each DB record has a "faq_entry" flag, so creating the current FAQ on the
  fly from the DB is easy. See:

	https://www.emulab.net/kb-faq.php3

* In reddot mode, you can add new KB entries:

	https://www.emulab.net/kb-manage.php3

  The form is fairly obvious but here are details anyway:

    Section Name: Choose an existing title, or make up a new one.
    Title:        The title of the KB (or FAQ) entry.
    Faq Entry:    Check this box if the new entry should show up in the FAQ.
    X Ref Tag:    A token so you can refer to other KB entries by name,
                  instead of by its index. Within the KB entry you would
                  write: <a href=kb-show.php3?xref_tag=sometag>
    Body:         Whatever you like. I took the existing FAQ entries and
                  stuck them with no changes except for the xref_tag
                  mentioned about (since some entries referenced other
                  entries).

* Once you click on sumbit, you will see the entry as it will appear to
  users, along with a submenu to Modify/Delete/Add entries. You can modify
  the current entry from that menu. Mere users do not see this menu, only
  when in reddot mode.

* The intent here is that we can generate new entries really easy, right
  from email if you like (with appropriate <pre> or <xmp> tags around it).

* I have added sql/knowlbase-create.sql and a makefile target to
  generate that file when creating a distribution. I also added a section
  to install/boss-install to insert the entries into the new DB.

* I hooked the search function into the existing Search Documentation link.
  We know search both the Knowledge Base *and* the Documentation on doc
  searches. This probably needs a little more work to get right.

* I changed a lot of faq links to be more consistent and to reference
  the proper xref_tags (#swapping instead of #UTT-34).
parent 95fe00b4
...@@ -620,6 +620,18 @@ Phase "database", "Setting up database", sub { ...@@ -620,6 +620,18 @@ Phase "database", "Setting up database", sub {
} }
ExecQuietFatal("$MYSQL $DBNAME < $TOP_SRCDIR/sql/sitevars-create.sql"); ExecQuietFatal("$MYSQL $DBNAME < $TOP_SRCDIR/sql/sitevars-create.sql");
}; };
Phase "knowlbase", "Filling knowledge_base_entries table", sub {
my ($exitval, @rows) = ExecQuiet("echo 'select * from " .
"knowledge_base_entries' | $MYSQL -s $DBNAME");
if ($exitval) {
PhaseFail("Error running query");
}
if (scalar @rows) {
PhaseSkip("Already done");
}
ExecQuietFatal("$MYSQL $DBNAME < ".
"$TOP_SRCDIR/sql/knowlbase-create.sql");
};
}; };
Phase "rc.conf", "Adding testbed content to $RCCONF", sub { Phase "rc.conf", "Adding testbed content to $RCCONF", sub {
......
...@@ -32,13 +32,16 @@ db-fill: ...@@ -32,13 +32,16 @@ db-fill:
@/usr/testbed/sbin/wap /usr/testbed/sbin/export_tables \ @/usr/testbed/sbin/wap /usr/testbed/sbin/export_tables \
> database-fill.sql > database-fill.sql
db-fill-dist: db-fill-dist: knowlbase
@/usr/testbed/sbin/wap /usr/testbed/sbin/export_tables -i \ @/usr/testbed/sbin/wap /usr/testbed/sbin/export_tables -i \
> database-fill.sql > database-fill.sql
sitevars: sitevars:
@mysqldump -t tbdb sitevariables > sitevars-create.sql @mysqldump -t tbdb sitevariables > sitevars-create.sql
knowlbase:
@mysqldump -c -t tbdb knowledge_base_entries | sed -e 's,^INSERT,REPLACE,' > knowlbase-create.sql
fwrules: fwrules:
@echo "*** Use 'gmake insertrules' in the firewall subdir to do this" @echo "*** Use 'gmake insertrules' in the firewall subdir to do this"
......
...@@ -717,6 +717,28 @@ CREATE TABLE ipsubnets ( ...@@ -717,6 +717,28 @@ CREATE TABLE ipsubnets (
PRIMARY KEY (idx) PRIMARY KEY (idx)
) TYPE=MyISAM; ) TYPE=MyISAM;
--
-- Table structure for table `knowledge_base_entries`
--
CREATE TABLE knowledge_base_entries (
idx int(11) NOT NULL auto_increment,
creator_uid varchar(8) NOT NULL default '',
date_created datetime default NULL,
section tinytext,
title tinytext,
body text,
xref_tag tinytext,
faq_entry tinyint(1) NOT NULL default '0',
date_modified datetime default NULL,
modifier_uid varchar(8) default NULL,
archived tinyint(1) NOT NULL default '0',
date_archived datetime default NULL,
archiver_uid varchar(8) default NULL,
PRIMARY KEY (idx),
UNIQUE KEY (xref_tag)
) TYPE=MyISAM;
-- --
-- Table structure for table `last_reservation` -- Table structure for table `last_reservation`
-- --
......
...@@ -2765,4 +2765,22 @@ last_net_act,last_cpu_act,last_ext_act); ...@@ -2765,4 +2765,22 @@ last_net_act,last_cpu_act,last_ext_act);
UNIX_TIMESTAMP(exptswapout_last), UNIX_TIMESTAMP(exptswapout_last),
UNIX_TIMESTAMP(exptswapmod_last))); UNIX_TIMESTAMP(exptswapmod_last)));
4.5: 4.5: Add knowledge_base_entries table.
CREATE TABLE knowledge_base_entries (
idx int(11) NOT NULL auto_increment,
creator_uid varchar(8) NOT NULL default '',
date_created datetime default NULL,
section tinytext,
title tinytext,
body text,
xref_tag tinytext,
faq_entry tinyint(1) NOT NULL default '0',
date_modified datetime default NULL,
modifier_uid varchar(8) default NULL,
archived tinyint(1) NOT NULL default '0',
date_archived datetime default NULL,
archiver_uid varchar(8) default NULL,
PRIMARY KEY (idx),
UNIQUE KEY (xref_tag)
) TYPE=MyISAM;
This diff is collapsed.
<!-- <!--
EMULAB-COPYRIGHT EMULAB-COPYRIGHT
Copyright (c) 2000-2003 University of Utah and the Flux Group. Copyright (c) 2000-2003, 2005 University of Utah and the Flux Group.
All rights reserved. All rights reserved.
--> -->
<center> <center>
...@@ -66,7 +66,7 @@ reserve and configure nodes, etc. ...@@ -66,7 +66,7 @@ reserve and configure nodes, etc.
<p> <p>
More detailed information on this More detailed information on this
process can be found in the process can be found in the
<a href="docwrapper.php3?docname=faq.html">Emulab FAQ</a>. <a href="faq.php3">Emulab FAQ</a>.
<h3>Another way of saying the same thing</h3> <h3>Another way of saying the same thing</h3>
......
<?php <?php
# #
# EMULAB-COPYRIGHT # EMULAB-COPYRIGHT
# Copyright (c) 2000-2004 University of Utah and the Flux Group. # Copyright (c) 2000-2005 University of Utah and the Flux Group.
# All rights reserved. # All rights reserved.
# #
...@@ -197,7 +197,7 @@ function SPITFORM($formfields, $errors) ...@@ -197,7 +197,7 @@ function SPITFORM($formfields, $errors)
<li><b>If you do not have an NS file:</b><br> You may want to <li><b>If you do not have an NS file:</b><br> You may want to
<b><a href='buildui/bui.php3'>go to the NetBuild GUI</a></b> <b><a href='buildui/bui.php3'>go to the NetBuild GUI</a></b>
to graphically create one online<font size=-2> to graphically create one online<font size=-2>
(<a href='$TBDOCBASE/faq.php3#UTT-NETBUILD'>Additional (<a href='$TBDOCBASE/faq.php3#netbuild'>Additional
information</a>)</font>.<br> information</a>)</font>.<br>
Or, you can download the Emulab Or, you can download the Emulab
<a href='netlab/client.php3'><b>client</b></a> and graphically <a href='netlab/client.php3'><b>client</b></a> and graphically
......
<?php <?php
# #
# EMULAB-COPYRIGHT # EMULAB-COPYRIGHT
# Copyright (c) 2000-2002, 2004 University of Utah and the Flux Group. # Copyright (c) 2000-2002, 2004, 2005 University of Utah and the Flux Group.
# All rights reserved. # All rights reserved.
# #
require("defs.php3"); require("defs.php3");
...@@ -32,7 +32,7 @@ PAGEHEADER("Documentation"); ...@@ -32,7 +32,7 @@ PAGEHEADER("Documentation");
</dl><br> </dl><br>
<li><dl> <li><dl>
<dt><b><a href="docwrapper.php3?docname=faq.html"> <dt><b><a href="faq.php3">
Frequently Asked Questions (FAQ)</a></b></dt> Frequently Asked Questions (FAQ)</a></b></dt>
<dd>Probably our most important reference document. Check here before <dd>Probably our most important reference document. Check here before
asking for help. Includes sections on "Getting Started" on Emulab, asking for help. Includes sections on "Getting Started" on Emulab,
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
<body> <body>
<!-- <!--
EMULAB-COPYRIGHT EMULAB-COPYRIGHT
Copyright (c) 2000-2004 University of Utah and the Flux Group. Copyright (c) 2000-2005 University of Utah and the Flux Group.
All rights reserved. All rights reserved.
--> -->
<h2>Windows XP in Emulab</h2> <h2>Windows XP in Emulab</h2>
...@@ -218,7 +218,7 @@ buttons work, from FreeBSD, Linux, and Windows workstations: ...@@ -218,7 +218,7 @@ buttons work, from FreeBSD, Linux, and Windows workstations:
node in a /sshkeys subdirectory, you have to type your password to SSH node in a /sshkeys subdirectory, you have to type your password to SSH
logins to enable remote directory mounts through SMB. </li> logins to enable remote directory mounts through SMB. </li>
<li> <a href="https://www.emulab.net/faq.php3#UTT-TUNNEL"> <b>Console</b> <li> <a href="https://www.emulab.net/faq.php3#tiptunnel"> <b>Console</b>
</a> - The <b>Console</b> button won't do much good now, because </a> - The <b>Console</b> button won't do much good now, because
Windows doesn't normally provide logins on the com serial device. Windows doesn't normally provide logins on the com serial device.
(This will be remedied with <i>agetty</i> eventually.) An exception is (This will be remedied with <i>agetty</i> eventually.) An exception is
......
This diff is collapsed.
<?php <?php
# #
# EMULAB-COPYRIGHT # EMULAB-COPYRIGHT
# Copyright (c) 2000-2002 University of Utah and the Flux Group. # Copyright (c) 2000-2005 University of Utah and the Flux Group.
# All rights reserved. # All rights reserved.
# #
require("defs.php3"); require("defs.php3");
# header("Location: kb-faq.php3");
# Standard Testbed Header
#
PAGEHEADER("Frequently Asked Questions");
echo("<b><a href=$TBDOCBASE/docwrapper.php3?docname=faq.html&printable=1>Printable version of this document</a></b><br>");
#
# I don't want to stick the html code in here.
#
readfile("faq.html");
#
# Standard Testbed Footer
#
PAGEFOOTER();
?> ?>
<?php
#
# EMULAB-COPYRIGHT
# Copyright (c) 2005 University of Utah and the Flux Group.
# All rights reserved.
#
require("defs.php3");
#
# Standard Testbed Header
#
PAGEHEADER("Emulab FAQ");
#
# Get all FAQ entries.
#
$search_result =
DBQueryFatal("select * from knowledge_base_entries ".
"where faq_entry=1 ".
"order by section,date_created");
if (! mysql_num_rows($search_result)) {
USERERROR("There are no FAQ entries in the Emulab Knowledge Base!", 1);
return;
}
echo "<center><h2>Frequently Asked Questions</h2></center>\n";
echo "<blockquote>\n";
echo "<ul>\n";
$lastsection = "";
while ($row = mysql_fetch_array($search_result)) {
$section = $row['section'];
$title = $row['title'];
$idx = $row['idx'];
$xref_tag = $row['xref_tag'];
if ($lastsection != $section) {
if ($lastsection != "") {
echo "</ul><hr>\n";
}
$lastsection = $section;
echo "<li><font size=+1><b>$section</b></font>\n";
echo "<ul>\n";
}
echo "<li>";
if (isset($xref_tag) && $xref_tag != "") {
echo "<a NAME='$xref_tag'></a>";
}
echo "<a href=kb-show.php3?idx=$idx>$title</a>\n";
}
echo "</ul>\n";
echo "</blockquote>\n";
#
# Standard Testbed Footer
#
PAGEFOOTER();
?>
This diff is collapsed.
<?php
#
# EMULAB-COPYRIGHT
# Copyright (c) 2005 University of Utah and the Flux Group.
# All rights reserved.
#
if (!isset($embedded)) {
require("defs.php3");
}
#
# Standard Testbed Header
#
if (!isset($embedded)) {
PAGEHEADER("Search Emulab Knowledge Base");
}
function SPITFORM($query, $query_type, $query_which, $error)
{
echo "<table align=center border=1>
<form action=kb-search.php3 method=get>\n";
$query = htmlspecialchars($query);
if ($error) {
echo "<center><font color=red>
$error
</font></center><br>\n";
}
#
# The query
#
echo "<tr>
<td>Keywords:</td>
<td class=left>
<input type=text name=query value=\"$query\"
size=50 maxlength=100>
</td>
</tr>\n";
#
# The query type
#
$temp_array = array("and" => "All Words",
"or" => "Any Words",
"exact" => "Exact Phrase");
if (!$query_type)
$query_type = "and";
echo "<tr>
<td>Search for:</td>
<td class=left>\n";
foreach ($temp_array as $key => $phrase) {
$checked = "";
if ($query_type == $key)
$checked = "checked";
echo "<input type=radio $checked name=query_type value=$key>$phrase\n";
}
echo " </td>
</tr>\n";
#
# What to search
#
$temp_array = array("title" => "Title",
"body" => "Body",
"both" => "Both");
if (!$query_which)
$query_which = "title";
echo "<tr>
<td>Search what:</td>
<td class=left>\n";
foreach ($temp_array as $key => $phrase) {
$checked = "";
if ($query_which == $key)
$checked = "checked";
echo "<input type=radio $checked name=query_which value=$key>".
"$phrase\n";
}
echo " </td>
</tr>\n";
echo "<tr>
<td colspan=2 align=center>
<b><input type=submit name=submit value='Submit Query'></b>
</td>
</tr>\n";
echo "</form>
</table><br>\n";
echo "<center>".
"Enter a space or comma separated list of keywords<br>".
"Use <font size=+2><b>*</b></font> to get all entries".
"</center>\n";
}
#
# First page load ...
#
if (!isset($submit) && !isset($embedded)) {
SPITFORM("", null, null, null);
PAGEFOOTER();
return;
}
#
# Check the query type
#
if (!isset($query_type) || $query_type == "") {
$query_type == "and";
}
if (! ($query_type == "and" || $query_type == "or" ||
$query_type == "exact")) {
PAGEARGERROR("Improper query type $query_type");
}
#
# Check the query which
#
if (!isset($query_which) || $query_which == "") {
$query_which == "title";
}
if (! ($query_which == "title" ||
$query_which == "body" || $query_which == "both")) {
PAGEARGERROR("Improper query which $query_which");
}
#
# Must supply a query!
#
if (!isset($query) || $query == "") {
SPITFORM("", $query_type, $query_which, "Please provide a query!");
PAGEFOOTER();
return;
}
#
# Check the query
#
if (! TBvalid_userdata($query)) {
SPITFORM($query, $query_type, $query_which, "Illegal characters in query");
PAGEFOOTER();
return;
}
#
# Look for special "*" query; just get everything and list it.
#
if ($query == "*") {
$search_result =
DBQueryFatal("select * from knowledge_base_entries ".
"order by section,date_created");
}
else {
#
# Mysql 4.0 has all this stuff built in, but not 3.23. So, do it by hand.
#
#
# Exact phrase search is easy!
#
if ($query_type == "exact") {
$clause = "";
$qsafe = addslashes($query);
if ($query_which == "title") {
$clause = "where title like '%${qsafe}%'";
}
elseif ($query_which == "body") {
$clause = "where body like '%${qsafe}%'";
}
elseif ($query_which == "both") {
$clause = "where body like '%${qsafe}%' ".
"or title like '%${qsafe}%'";
}
}
elseif ($query_type == "or") {
$wordarray = preg_split("/[\s,]+/", $query);
foreach ($wordarray as $i => $word) {
$wordarray[$i] = addslashes($word);
}
$qstring = implode("|", $wordarray);
if ($query_which == "title") {
$clause = "where title regexp '$qstring'";
}
elseif ($query_which == "body") {
$clause = "where body regexp '$qstring'";
}
elseif ($query_which == "both") {
$clause = "where title regexp '$qstring' ".
"or body regexp '$qstring'";
}
}
else {
$wordarray = preg_split("/[\s,]+/", $query);
foreach ($wordarray as $i => $word) {
if ($query_which == "title") {
$wordarray[$i] = "title regexp '" . addslashes($word) . "'";
}
elseif ($query_which == "body") {
$wordarray[$i] = "body regexp '" . addslashes($word) . "'";
}
else {
$wordarray[$i] = "(title regexp '" . addslashes($word) . "' ".
"or body regexp '" . addslashes($word) . "')";
}
}
$clause = "where ". implode(" and ", $wordarray);
}
$search_result =
DBQueryFatal("select * from knowledge_base_entries ".
"$clause ".
"order by section,date_created");
}
if (! mysql_num_rows($search_result)) {
if (!isset($embedded)) {
SPITFORM($query, $query_type, $query_which,
"No Matches. Please try again");
PAGEFOOTER();
}
return;
}
#
# Okay, format the list ...
#
if (!isset($embedded)) {
SPITFORM($query, $query_type, $query_which, null);
}
if (!isset($embedded)) {
echo "<blockquote><blockquote>\n";
}
echo "<font size=+2>Knowledge Base search results</font>\n";
echo "<ul>\n";
$lastsection = "";
while ($row = mysql_fetch_array($search_result)) {
$section = $row['section'];
$title = $row['title'];
$idx = $row['idx'];
$xref_tag = $row['xref_tag'];
if ($lastsection != $section) {
if ($lastsection != "") {
echo "</ul><hr>\n";
}
$lastsection = $section;
echo "<li><font size=+1><b>$section</b></font>\n";
echo "<ul>\n";
}
echo "<li>";
if (isset($xref_tag) && $xref_tag != "") {
echo "<a NAME='$xref_tag'></a>";
}
echo "<a href=kb-show.php3?idx=$idx>$title</a>\n";
}
echo "</ul></ul>\n";
if (!isset($embedded)) {
echo "</blockquote></blockquote>\n";
}
#
# Standard Testbed Footer
#
if (!isset($embedded)) {
PAGEFOOTER();
}
?>
<?php
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2005 University of Utah and the Flux Group.
# All rights reserved.
#
include("defs.php3");
#
# Standard Testbed Header
#
PAGEHEADER("Knowledge Base Entry");
#
# Admin users get a menu.
#
$uid = GETLOGIN();
$isadmin = 0;
if (CHECKLOGIN($uid) & CHECKLOGIN_LOGGEDIN) {
$isadmin = ISADMIN();
}
#
# Check record.
#
if (isset($idx) && $idx != "") {
if (! TBvalid_integer($idx)) {
PAGEARGERROR("Invalid characters in $idx");
}
$query_result =
DBQueryFatal("select * from knowledge_base_entries ".
"where idx='$idx'");
if (! mysql_num_rows($query_result)) {
USERERROR("No such knowledge_base entry: $idx", 1);
}
$row = mysql_fetch_array($query_result);
}
elseif (isset($xref_tag) && $xref_tag != "") {
if (! preg_match("/^[-\w]+$/", $xref_tag)) {
PAGEARGERROR("Invalid characters in $xref_tag");
}
$query_result =
DBQueryFatal("select * from knowledge_base_entries ".
"where xref_tag='$xref_tag'");
if (! mysql_num_rows($query_result)) {
USERERROR("No such knowledge_base entry: $xref_tag", 1);
}
$row = mysql_fetch_array($query_result);
$idx = $row['idx'];
}
else {
PAGEARGERROR("Must supply a knowledge_base index or xref tag");
}
echo "<center><b>Knowledge Base Entry: $idx</b><br>".
"(<a href=kb-search.php3>Search Again</a>)</center>\n";
echo "<br><br>\n";
if ($isadmin) {
SUBPAGESTART();
SUBMENUSTART("Options");
WRITESUBMENUBUTTON("Modify Entry",
"kb-manage.php3?idx=$idx&action=edit");
WRITESUBMENUBUTTON("Delete Entry",
"kb-manage.php3?idx=$idx&action=delete");
WRITESUBMENUBUTTON("Add Entry", "kb-manage.php3");
SUBMENUEND();
}
echo "<blockquote>\n";
echo "<font size=+1>" . $row['title'] . "</font>\n";
echo "<br><br>\n";
echo $row['body'];
echo "<br><br><br><br>\n";
echo "<font size=-2>Posted by " . $row['creator_uid'] . " on " .
$row['date_created'] . "</font><br><br>\n";
if ($isadmin) {
echo "</blockquote>\n";
SUBPAGEEND();
}
PAGEFOOTER();
?>
...@@ -588,6 +588,9 @@ function WRITESIDEBAR() { ...@@ -588,6 +588,9 @@ function WRITESIDEBAR() {
WRITESIDEBARBUTTON("Edit Site Variables", WRITESIDEBARBUTTON("Edit Site Variables",
$TBBASE, "editsitevars.php3"); $TBBASE, "editsitevars.php3");
WRITESIDEBARBUTTON("Edit Knowledge Base",
$TBBASE, "kb-manage.php3");
$query_result $query_result
= DBQUeryFatal("select new_node_id from new_nodes"); = DBQUeryFatal("select new_node_id from new_nodes");
if (mysql_num_rows($query_result) > 0) { if (mysql_num_rows($query_result) > 0) {
......
...@@ -102,7 +102,7 @@ echo "<br><br>\n"; ...@@ -102,7 +102,7 @@ echo "<br><br>\n";
# Put up the modify form on first load. # Put up the modify form on first load.
# #
if (! isset($go)) { if (! isset($go)) {
echo "<a href='faq.php3#UTT-Modify'>". echo "<a href='faq.php3#swapmod'>".
"Modify Experiment Documentation (FAQ)</a></h3>"; "Modify Experiment Documentation (FAQ)</a></h3>";
echo "<br>"; echo "<br>";
......
<?php <?php
# #
# EMULAB-COPYRIGHT # EMULAB-COPYRIGHT
# Copyright (c) 2000-2002, 2004 University of Utah and the Flux Group. # Copyright (c) 2000-2002, 2004, 2005 University of Utah and the Flux Group.
# All rights reserved. # All rights reserved.
# #
require("defs.php3"); require("defs.php3");
...@@ -15,7 +15,7 @@ PAGEHEADER("Search Emulab Documentation"); ...@@ -15,7 +15,7 @@ PAGEHEADER("Search Emulab Documentation");
# We no longer support an advanced search option. We might bring it back # We no longer support an advanced search option. We might bring it back
# someday. # someday.
# #
function SPITFORM($query) function SPITSEARCHFORM($query)
{ {
echo "<table align=center border=1> echo "<table align=center border=1>
<form action=search.php3 method=get>\n"; <form action=search.php3 method=get>\n";
...@@ -43,14 +43,14 @@ function SPITFORM($query) ...@@ -43,14 +43,14 @@ function SPITFORM($query)
} }
if (!isset($query) || $query == "") { if (!isset($query) || $query == "") {