Commit 6f08c442 authored by Leigh B. Stoller's avatar Leigh B. 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 {
}
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 {
......
......@@ -32,13 +32,16 @@ db-fill:
@/usr/testbed/sbin/wap /usr/testbed/sbin/export_tables \
> database-fill.sql
db-fill-dist:
db-fill-dist: knowlbase
@/usr/testbed/sbin/wap /usr/testbed/sbin/export_tables -i \
> database-fill.sql
sitevars:
@mysqldump -t tbdb sitevariables > sitevars-create.sql
knowlbase:
@mysqldump -c -t tbdb knowledge_base_entries | sed -e 's,^INSERT,REPLACE,' > knowlbase-create.sql
fwrules:
@echo "*** Use 'gmake insertrules' in the firewall subdir to do this"
......
......@@ -717,6 +717,28 @@ CREATE TABLE ipsubnets (
PRIMARY KEY (idx)
) 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`
--
......
......@@ -2765,4 +2765,22 @@ last_net_act,last_cpu_act,last_ext_act);
UNIX_TIMESTAMP(exptswapout_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
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.
-->
<center>
......@@ -66,7 +66,7 @@ reserve and configure nodes, etc.
<p>
More detailed information on this
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>
......
<?php
#
# 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.
#
......@@ -197,7 +197,7 @@ function SPITFORM($formfields, $errors)
<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>
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>
Or, you can download the Emulab
<a href='netlab/client.php3'><b>client</b></a> and graphically
......
<?php
#
# 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.
#
require("defs.php3");
......@@ -32,7 +32,7 @@ PAGEHEADER("Documentation");
</dl><br>
<li><dl>
<dt><b><a href="docwrapper.php3?docname=faq.html">
<dt><b><a href="faq.php3">
Frequently Asked Questions (FAQ)</a></b></dt>
<dd>Probably our most important reference document. Check here before
asking for help. Includes sections on "Getting Started" on Emulab,
......
......@@ -8,7 +8,7 @@
<body>
<!--
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.
-->
<h2>Windows XP in Emulab</h2>
......@@ -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
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
Windows doesn't normally provide logins on the com serial device.
(This will be remedied with <i>agetty</i> eventually.) An exception is
......
This diff is collapsed.
<?php
#
# 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.
#
require("defs.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();
header("Location: kb-faq.php3");
?>
<?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();
?>
<?php
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2005 University of Utah and the Flux Group.
# All rights reserved.
#
include("defs.php3");
#
# No Standard Testbed Header; going to spit out a redirect later.
#
# Some constants.
define("TBDB_KB_SECTIONLEN", 80);
define("TBDB_KB_TITLELEN", 140);
define("TBDB_KB_XREFTAGLEN", 30);
define("TBDB_KB_BODYLEN", 10000);
#
# Only known and logged in users.
#
$uid = GETLOGIN();
LOGGEDINORDIE($uid);
$isadmin = ISADMIN($uid);
# Summary data for admins only.
if (!ISADMIN()) {
USERERROR("You are not allowed to view this page!", 1);
}
#
# Spit the form out using the array of data.
#
function SPITFORM($formfields, $errors)
{
global $idx, $action;
PAGEHEADER("Add/Modify a Knowledge Base Entry");
if (isset($idx)) {
if ($action == "edit") {
echo "<center><h3>Modify Entry</h3>\n";
}
elseif ($action == "delete") {
echo "<center><h3>Delete Entry</h3>\n";
}
else {
echo "<center><h3>Add Entry</h3>\n";
}
}
else {
echo "<center><h3>Add Entry</h3>\n";
}
echo "(<a href=kb-search.php3>Search For Entries</a>)</center>\n";
if ($errors) {
echo "<table class=nogrid
align=center border=0 cellpadding=6 cellspacing=0>
<tr>
<th align=center colspan=2>
<font size=+1 color=red>
&nbsp;Oops, please fix the following errors!&nbsp;
</font>
</td>
</tr>\n";
while (list ($name, $message) = each ($errors)) {
echo "<tr>
<td align=right>
<font color=red>$name:&nbsp;</font></td>
<td align=left>
<font color=red>$message</font></td>
</tr>\n";
}
echo "</table><br>\n";
}
echo "<br>
<table align=center border=1>
<tr>
<td align=center colspan=2>
<em>(Fields marked with * are required)</em>
</td>
</tr>
<form action='kb-manage.php3' method=post>\n";
$sections_result =
DBQueryFatal("select distinct section from knowledge_base_entries");
#
# If the form is being edited, then throw up some extra detail.
#
if (isset($idx)) {
echo "<input type='hidden' name='idx' value='$idx'>\n";
echo "<input type='hidden' name='action' value='$action'>\n";
echo "<tr>
<td>Posted by:</td>
<td>$formfields[creator_uid]</td>
</tr>\n";
echo "<tr>
<td>Posted on:</td>
<td>$formfields[date_created]</td>
</tr>\n";
if (isset($formfields[date_modified])) {
echo "<tr>
<td>Last Modified by:</td>
<td>$formfields[modifier_uid]</td>
</tr>\n";
echo "<tr>
<td>Modified on:</td>
<td>$formfields[date_modified]</td>
</tr>\n";
}
echo "<tr></tr>\n";
}
#
# Select Section.
#
echo "<tr>
<td>*Section Name:</td>
<td><select name=\"formfields[section]\">
<option value=none>Please Select </option>\n";
while ($section_row = mysql_fetch_array($sections_result)) {
$section = $section_row['section'];
$selected = "";
if (isset($formfields[section]) &&
strcmp($formfields[section], $section) == 0)
$selected = "selected";
echo "<option $selected value='$section'>$section &nbsp </option>\n";
}
echo " </select>\n";
# And allow for a new section name to be created
echo " or add: ";
echo " <input type=text
name=\"formfields[new_section]\"
value=\"" . $formfields[new_section] . "\"
size=50>\n";
echo " </td>
</tr>\n";
#
# Title
#
echo "<tr>
<td>*Title:<br>
(a short pithy sentence)</td>
<td class=left>
<input type=text
name=\"formfields[title]\"
value=\"" . $formfields[title] . "\"
size=70>
</td>
</tr>\n";
#
# Make it a FAQ entry?
#
echo "<tr>
<td>Faq Entry?</td>
<td class=left>
<input type=checkbox
name=\"formfields[faq_entry]\"
value=1";
if (isset($formfields[faq_entry]) && $formfields[faq_entry] == "1")
echo " checked";
echo " > Yes
</td>
</tr>\n";
#
# Cross Reference Tag
#
echo "<tr>
<td>Cross Reference Tag</td>
<td class=left>
<input type=text
name=\"formfields[xref_tag]\"
value=\"" . $formfields[xref_tag] . "\"
size=20>
</td>
</tr>\n";
#
# Body
#
echo "<tr>
<td colspan=2>
*Please enter (HTML) text of entry
</td>
</tr>
<tr>
<td colspan=2 align=center class=left>
<textarea name=\"formfields[body]\"
rows=30 cols=80>$formfields[body]</textarea>
</td>
</tr>\n";
echo "<tr>
<td align=center colspan=2>
<b><input type=submit name=submit ";
if (isset($idx)) {
if ($action == "edit") {
echo "value=Modify></b>";
}
elseif ($action == "delete") {
echo "value=Delete></b>";
}
else {
echo "value=Submit></b>";
}
}
else {
echo "value=Submit></b>";
}
echo " </td>
</tr>\n";
echo "</form>
</table>\n";
echo "<br>
<blockquote><blockquote>
</blockquote></blockquote>\n";
}
#
# For delete/modify, check record.
#
if (isset($idx)) {
if (!isset($action) || !($action == "edit" || $action == "delete")) {
PAGEARGERROR("Invalid action");
}
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);
}
$defaults = mysql_fetch_array($query_result);
}
#
# On first load, display a virgin form and exit.
#
if (! $submit) {
#
# If doing an edit of an existing entry, then need to get that from
# the DB.
#
if (!isset($idx)) {
$defaults = array();
#
# Allow formfields that are already set to override defaults
#
if (isset($formfields)) {
while (list ($field, $value) = each ($formfields)) {
$defaults[$field] = $formfields[$field];
}
}
}
SPITFORM($defaults, 0);
PAGEFOOTER();
return;
}
#
# Deal with deletion.
#
if (isset($submit) && $submit == "delete" && isset($idx)) {
DBQueryFatal("delete from knowledge_base_entries where idx='$idx'");
PAGEFOOTER();
return;
}
#
# Otherwise, must validate and redisplay if errors
#
$errors = array();
#
# Section Name.
#
if (isset($formfields[new_section]) && $formfields[new_section] != "") {
if (! TBvalid_userdata($formfields[new_section])) {
$errors["Section"] = "Invalid characters";
}
elseif (strlen($formfields[new_section]) > TBDB_KB_SECTIONLEN) {
$errors["Section"] =
"Too long! ".
"Must be less than or equal to " . TBDB_KB_SECTIONLEN;
}
$section = addslashes($formfields[new_section]);
}
elseif (isset($formfields[section]) &&
$formfields[section] != "" && $formfields[section] != "none") {
if (! TBvalid_userdata($formfields[section])) {
$errors["Section"] = "Invalid characters";
}
elseif (strlen($formfields[section]) > TBDB_KB_SECTIONLEN) {
$errors["Section"] =
"Too long! ".
"Must be less than or equal to " . TBDB_KB_SECTIONLEN;
}
$section = addslashes($formfields[section]);
}
else {
$errors["Section"] = "Missing Field";
}
#
# Title
#
if (isset($formfields[title]) && $formfields[title] != "") {
if (! TBvalid_userdata($formfields[title])) {
$errors["Title"] = "Invalid characters";
}
elseif (strlen($formfields[title]) > TBDB_KB_TITLELEN) {
$errors["Title"] =
"Too long! ".
"Must be less than or equal to " . TBDB_KB_TITLELEN;
}
$title = addslashes($formfields[title]);
}
else {
$errors["Title"] = "Missing Field";
}
#
# Faq Entry?
#
if (isset($formfields[faq_entry]) && $formfields[faq_entry] == "1") {
$faq_entry = 1;
}
else {
$faq_entry = 0;
}
#
# Cross Reference Tag
#
if (isset($formfields[xref_tag]) && $formfields[xref_tag] != "") {
if (! preg_match("/^[-\w]+$/", $formfields[xref_tag])) {
$errors["Cross Reference Tag"] = "Invalid characters";
}
elseif (strlen($formfields[xref_tag]) > TBDB_KB_XREFTAGLEN) {
$errors["Cross Reference Tag"] =
"Too long! ".
"Must be less than or equal to " . TBDB_KB_XREFTAGLEN;
}
$xref_tag = addslashes($formfields[xref_tag]);
$xref_tag = "'$xref_tag'";
#
# Make sure the xref_tag is not already in use (by another entry).
#
$xref_result =
DBQueryFatal("select idx from knowledge_base_entries ".
"where xref_tag=$xref_tag ".
(isset($idx) ? "and idx!='$idx'" : ""));
if (mysql_num_rows($xref_result)) {
$errors["Cross Reference Tag"] = "Already in use";
}
}
else {
$xref_tag = "NULL";
}
#
# Body
#
if (isset($formfields[body]) && $formfields[body] != "") {
if (! TBvalid_fulltext($formfields[body])) {
$errors["Body"] = "Invalid characters";
}
elseif (strlen($formfields[body]) > TBDB_KB_BODYLEN) {
$errors["Body"] =
"Too long! ".