;;; rolodex.mhtml: -*- Meta-HTML -*- 

;;;  Copyright (c) 1996 Universal Access Inc.
;;;  Author: Henry Minsky (hqm@ua.com) Sat Sep 14 13:42:28 1996.

;;; Implement a simple web rolodex using an MSQL database.
;;;
;;; This is a simple demonstration of how to access MSQL databases
;;; via Meta-HTML

;;; Set this to the hostname of your MSQL server
<set-var rolodex::msqlserver="localhost">

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; We assume that the db administrator has created a database
;;; named "rolodex"

<set-var create-people-table = 
  "create table people (
	name	char(64) primary key,   
        lastname char(64),   ;;; We need this to do sorts properly!
	street  char(32),
        city    char(32),
	state   char(2),
	country char(12),
	zip     char(10),

	email      char(64),
	dayphone   char(24),
	evephone   char(24),
	fax        char(24),
	comments   char(512),

	last_modified_t      int  ;;; This field will be update using the <TIME> tag.
)">


;;; OK, this is real crude, but we just go ahead and try to create the
;;; the PEOPLE table before every access. This is so someone who is
;;; using this example app for the first time will not have to know or
;;; remember to create the table the first time the app is used.

<msql::with-open-database mydb database=rolodex
			  host=<get-var rolodex::msqlserver>>

  ;;; msql::database-exec-query always returns a cursor, even if we
  ;;; don't need to use it for anything.
  <set-var cursor=<msql::database-exec-query mydb 
					     <get-var create-people-table>>>
</msql::with-open-database>

;;; This function is used to add a person to the database.
;;;
;;; It shows an example of a way to pass arguments by keyword, using the
;;; an alist (association list). An alist is created from a package
;;; using the <package-to-alist pkgname> function.
;;;
;;; We convert the alist to a temporary package called tmp,
;;; and then save it to the database, using msql::database-save-package.
;;;
;;; The database field names for the rolodex table are
;;; (entry-name, street, city, state, country, zip, email,
;;;  dayphone, evephone, comments)
<defun add-entry alist>
  <package-delete tmp>
  <alist-to-package <get-var alist> tmp>
  <msql::with-open-database rdb database=rolodex
			    host=<get-var rolodex::msqlserver>>
    <set-var tmp::last_modified_t=<time>>
    <set-var done=<msql::database-save-package rdb <get-var tmp::entry-name>
					       tmp
					       table=people keyname=name>>
  </msql::with-open-database>
</defun>


;;; Loads a people entry from the database, keying on the name field.
;;; This loads the symbols into the current default package.
;;;
;;; The keyword argument package=pkgname to msql::database-load-record
;;; would let you you load the record values into a specific package 
;;; named pkgname if you desire.
;;; 
;;; <msql::database-load-record rdb keyvalue package=pkgname>

<defsubst load-entry name>
  <msql::with-open-database rdb database=rolodex
			    host=<get-var rolodex::msqlserver>>
    <msql::database-load-record rdb <get-var name> table=people keyname=name>
  </msql::with-open-database>
</defsubst>

<defsubst delete-entry name>
  <msql::with-open-database rdb database=rolodex
			    host=<get-var rolodex::msqlserver>>
    <set-var result=<msql::database-delete-record rdb <get-var name>
						  table=people keyname=name>>
  </msql::with-open-database>
</defsubst>



;;; Here is an alternate of <add-entry>, using named arguments
;;; and msql::database-save-record, instead of database-save-package.
;;;
;;; It would take its arguments positionally, instead of as a package alist
;;; of key-value pairs.
;;; 
;;;<defun add-entry name street city state
;;;                  country zip email
;;;		  dayphone evephone fax comments>
;;;  <msql::with-open-database rdb database=rolodex
;;;			    host=<get-var rolodex::msqlserver>>
;;;    <set-var last_modified_t=<time>>
;;;    <set-var done=<msql::database-save-record rdb <get-var name>
;;;					     street city state
;;;					     country zip email
;;;					     dayphone evephone fax comments  
;;;                                          last_modified_t
;;;					     table=people keyname=name
;;;					     >>
;;;  </msql::with-open-database>
;;;</defun>
;;;
;;;
;;;

;;; List the people in the rolodex database.
;;; Finds all entries which contain matching strings.
;;;

<defun show-people-list matching>
  <msql::with-open-database rdb database=rolodex
			    host=<get-var rolodex::msqlserver>>
    <dl>
    <msql::database-query rdb true 
    query="SELECT * FROM people WHERE name like '<get-var matching>'ORDER BY lastname"
    format=<show-person>>
    <p>
    </dl>
  </msql::with-open-database>
</defun>

;;; If var contains the name of a variable which has a 
;;; non-null value, show it in bold with a title.
<defsubst maybe-show title var>
  <when <get-var <get-var var>>>
    <b><get-var title>:</b> <get-var <get-var var>>
  </when>
</defsubst>

;;; If the variable has a value, print it following a <br>
<defsubst get-var-br var>
  <when <get-var <get-var var>>>
    <br> <get-var <get-var var>> 
  </when>
</defsubst>

;;; This assumes that the current default package has 
;;; all variables bound to their corresponding database
;;; field names, i.e., name, email, comments ...
<defsubst show-person>
  <form action="add-entry.mhtml" method=post>
    <dt>
    <b><get-var name></b>
    <dd> 
    <when <get-var email>>
      <b>email:</b> <get-var email><br>
    </when>
    <maybe-show day dayphone>
    <maybe-show eve evephone>
    <maybe-show fax fax>
    <br>
    <get-var street>
    <get-var-br city>
    <get-var-br state> <get-var zip>
    <get-var country>
    <br><b>Comments: </b><br>

    <quote><subst-in-string <get-var comments>
			    "\n\n" "<p>"
			    "\n" " <br> ">
    </quote>
    <br>
    <input type=submit name=action value="Change">
    <input type=submit name=action value="Delete">
    <input type=hidden name=entry-name value="<get-var name>">
    <br>
  </form>
</defsubst>


;;; Uncomment the following two lines to enable a very simple password
;;; session authentication for users to modify the database.
<defun require-rolodex-login>
;;;  <if <not <get-session-var tasks::username>>
;;;      <redirect login.mhtml>>
</defun>

;;; Make a bunch of search index links
<defun letters chars>
  <subst-in-string <get-var chars>
		   "([A-Z]) " "<a href=<get-var mhtml::current-doc>?matching=\\1>\\1</a> ">
</defun>

;;; Makes a nice bar with alphabet letters on it, and a form entry
;;; field for typing a name.
<defun show-navbar>
  <form action="<get-var mhtml::current-doc>" method=post>
    <font size=+2>
    <letters "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ">
    <a href=<get-var mhtml::current-doc>?matching=*>*</a>
    </font>
    <p>
    <b>Name:</b>
    <input type=text size=24 name=matching value="<get-session-var matching>">
    <input type=submit name=action value="Find">      
    <input type=submit name=action value="Create">
  </form>
</defun>