Index: openacs-4/contrib/packages/vocabulary/vocabulary.info
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/vocabulary.info,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/vocabulary.info	23 Feb 2004 23:00:05 -0000	1.1
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!-- Generated by the OpenACS Package Manager -->
+
+<package key="vocabulary" url="http://openacs.org/repository/apm/packages/vocabulary" type="apm_application">
+    <package-name>Vocabulary</package-name>
+    <pretty-plural>Vocabularies</pretty-plural>
+    <initial-install-p>f</initial-install-p>
+    <singleton-p>f</singleton-p>
+    
+    <version name="0.10" url="http://openacs.org/repository/download/apm/vocabulary-0.10.apm">
+        <owner url="mailto:joel@aufrecht.org">Joel Aufrecht</owner>
+        <summary>A list of vocabulary words.</summary>
+        <description format="text/html">A list of vocubulary words plus tools to edit them; intended as a resource for other packages.</description>
+
+        <provides url="vocabulary" version="0.10"/>
+
+        <callbacks>
+        </callbacks>
+        <parameters>
+        <!-- No version parameters -->
+        </parameters>
+
+    </version>
+</package>
Index: openacs-4/contrib/packages/vocabulary/lib/definition-list.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/lib/definition-list.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/lib/definition-list.adp	23 Feb 2004 23:00:05 -0000	1.1
@@ -0,0 +1 @@
+<listtemplate name="definition"></listtemplate>
Index: openacs-4/contrib/packages/vocabulary/lib/definition-list.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/lib/definition-list.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/lib/definition-list.tcl	23 Feb 2004 23:00:05 -0000	1.1
@@ -0,0 +1,48 @@
+# expects word_id
+# expect return_url
+
+if { ![info exists package_id] } {
+    set package_id [ad_conn package_id]
+}
+
+set add_url [export_vars -base "definition-edit" {word_id return_url}]
+
+template::list::create \
+    -name definition \
+    -multirow definition \
+    -actions " {Add a Definition} $add_url " \
+    -no_data "No Definitions" \
+    -elements {
+        definition
+        { 
+            link_url_col edit_url
+            label "Definition" 
+        }
+        locale {label "Locale"}
+	delete {
+	    link_url_col delete_url 
+	    display_template {
+		<img src="/resources/acs-subsite/Delete16.gif" width="16" height="16" border="0">
+	    }
+	    sub_class narrow
+	}
+    }
+
+db_multirow \
+    -extend {
+	edit_url
+	delete_url
+    } definition definition_select {
+        select vd.id,
+               substr(vd.definition,0,20) as definition,
+               al.label as locale
+          from vocab_definition vd,
+               ad_locales al
+         where vd.word_id = :word_id
+           and al.locale = vd.locale
+         order by sort_order
+
+    } {
+	set edit_url [export_vars -base "definition-edit" { id }]
+	set delete_url [export_vars -base "definition-delete" { id return_url}]
+    }
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/lib/locale-count.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/lib/locale-count.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/lib/locale-count.adp	23 Feb 2004 23:00:05 -0000	1.1
@@ -0,0 +1,2 @@
+<listtemplate name="locale"></listtemplate>
+
Index: openacs-4/contrib/packages/vocabulary/lib/locale-count.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/lib/locale-count.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/lib/locale-count.tcl	23 Feb 2004 23:00:06 -0000	1.1
@@ -0,0 +1,75 @@
+ad_page_contract {
+    includelet for grouped locale information
+} {
+    locale_b:optional
+    orderby:optional
+}
+
+if { ![info exists package_id] } {
+    set package_id [ad_conn package_id]
+}
+
+set action [list "Add a word" [export_vars -base word-edit {return_url}] "Add a word"]
+
+template::list::create \
+    -name locale \
+    -multirow locale \
+    -actions $action \
+    -elements {
+        locale {
+	    label "Locale"
+            display_template {
+                @locale.locale_label@
+            }
+	    orderby {locale_label}
+	}
+        word_count {
+	    link_url_col word_locale_url
+            label "Words"
+	    orderby {word_count}
+        }
+        sentence_count {
+	    link_url_col sentence_locale_url
+            label "Sentences"
+	    orderby {sentence_count}
+        }
+        test_count {
+	    link_url_col test_locale_url
+            label "Tests"
+	    orderby {test_count}
+        }
+    }
+
+# note that the entire query is selected into another query
+# with the meaningless name of superquery in order to 
+# create a virtual table to which to apply the where clause
+db_multirow \
+    -extend {
+	word_locale_url
+	sentence_locale_url
+	test_locale_url
+    } locale word_select "
+        select * from (
+        select al.label as locale_label,
+               al.locale,
+               (select count(*)
+                  from vocab_word
+                 where locale = al.locale
+                   and package_id = :package_id) as word_count,
+               (select distinct count(item_id)
+                  from vocab_sentencex
+                 where locale2 = al.locale) as sentence_count,
+               (select count(id)
+                  from vocab_test
+                 where locale_a = al.locale
+                   and package_id = :package_id) as test_count
+          from ad_locales al
+         group by locale, locale_label) superquery
+        where word_count > 0
+	[template::list::orderby_clause -name locale -orderby]
+
+    " {
+	set word_locale_url [export_vars -base "word-list" {{locale $locale}}]
+	set sentence_locale_url [export_vars -base "sentence-list" {{locale $locale}}]
+	set test_locale_url [export_vars -base "test-list" {{locale $locale}}]
+    }
Index: openacs-4/contrib/packages/vocabulary/lib/phonetic-example-list.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/lib/phonetic-example-list.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/lib/phonetic-example-list.adp	23 Feb 2004 23:00:06 -0000	1.1
@@ -0,0 +1 @@
+<listtemplate name="phonetic_example"></listtemplate>
Index: openacs-4/contrib/packages/vocabulary/lib/phonetic-example-list.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/lib/phonetic-example-list.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/lib/phonetic-example-list.tcl	23 Feb 2004 23:00:06 -0000	1.1
@@ -0,0 +1,45 @@
+# expects phonetic_id
+
+if { ![info exists package_id] } {
+    set package_id [ad_conn package_id]
+}
+
+set add_url [export_vars -base "phonetic-example-edit" {phonetic_id return_url}]
+
+template::list::create \
+    -name phonetic_example \
+    -multirow phonetic_example \
+    -actions " {Add an example} $add_url " \
+    -no_data "No examples" \
+    -elements {
+        word { 
+	    link_url_col edit_url
+            label "Example Word" 
+        }
+        comments { label "Comments" }
+	delete {
+	    link_url_col delete_url 
+	    display_template {
+		<img src="/resources/acs-subsite/Delete16.gif" width="16" height="16" border="0">
+	    }
+	    sub_class narrow
+	}
+    }
+
+db_multirow \
+    -extend {
+	edit_url
+	delete_url
+    } phonetic_example phonetic_example_select {
+        select vw.word,
+               vpe.word_id,
+               vpe.comments
+          from vocab_phonetic_example vpe,
+               vocab_word vw
+         where vpe.phonetic_id = :phonetic_id
+           and vw.id = vpe.word_id
+
+    } {
+	set edit_url [export_vars -base "phonetic-example-edit" { {id $word_id} }]
+	set delete_url [export_vars -base "phonetic-example-delete" { {id $word_id} }]
+    }
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/lib/phonetic-list.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/lib/phonetic-list.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/lib/phonetic-list.adp	23 Feb 2004 23:00:06 -0000	1.1
@@ -0,0 +1,3 @@
+@phonetic_name@
+<listtemplate name="phonetic_list"></listtemplate>
+
Index: openacs-4/contrib/packages/vocabulary/lib/phonetic-list.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/lib/phonetic-list.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/lib/phonetic-list.tcl	23 Feb 2004 23:00:06 -0000	1.1
@@ -0,0 +1,85 @@
+# expects phonetic_locale
+# expects working_locale
+# button_target:optional
+
+set package_id [ad_conn package_id]
+if { [exists_and_not_null button_target ] } { 
+    set buttons 1
+} else {
+    set buttons 0
+}
+
+# are we doing IPA or local?
+if { [exists_and_not_null phonetic_locale] } {
+    # we're doing a local phonetic
+    set where_clause "where vp.locale = :phonetic_locale"
+    set field "local_phonetic"
+} else {
+    # we're doing IPA
+    set where_clause "where vp.locale is null"
+    set field "ipa_phonetic"
+}
+
+set add_action [list "Add a Symbol" [export_vars -base phonetic-edit {return_url phonetic_locale} ]]
+
+template::list::create \
+    -name phonetic_list \
+    -multirow phonetic_list \
+    -actions $add_action \
+    -elements {
+        paste {
+	    sub_class narrow
+            label "Symbol" 
+            display_template "@phonetic_list.paste_html;noquote@"
+        }
+	example {
+	    label "as in"
+	}
+        edit { 
+	    link_url_col edit_url
+	    sub_class narrow
+            display_template {
+                @phonetic_list.edit_html;noquote@
+            }
+        }
+    }
+
+db_multirow \
+    -extend {
+        view_url
+	edit_url
+	delete_url
+        onClick
+        paste_html
+        edit_html
+    } phonetic_list phonetic_select "
+        select vp.id,
+               (select word 
+                  from vocab_word
+                 where $field like '%' || vp.phonetic_symbol || '%'
+                   and locale = :working_locale
+                   and package_id = :package_id
+                 limit 1) as example,
+               vp.phonetic_symbol
+          from vocab_phonetic vp
+        $where_clause
+	 order by vp.sort_order, vp.phonetic_symbol
+    " {
+	set edit_url [export_vars -base "phonetic-edit" {id}]
+
+        if { $buttons } {
+	    set onClick "javascript:$button_target+='$phonetic_symbol'"
+            set paste_html "<input style=\"margin: 0px; font-size: 1.3em; padding: 0px; width: 2em; border-width: thin;\" type=\"button\" onClick=\"$onClick\" value=\"$phonetic_symbol\" />"
+            set edit_html "<img src=\"/resources/acs-subsite/Edit16.gif\" height=\"16\" width=\"16\" border=\"0\" alt=\"Edit\">"
+	} else {
+	    set onClick ""
+            set paste_html "<a href=\"$edit_url\">$phonetic_symbol</a>"
+            set edit_html ""
+	}
+    }
+
+set phonetic_name [vocab::phonetic_alphabet_for_locale -locale $phonetic_locale]
+
+if { [exists_and_not_null csv] } {
+    template::list::write_csv -name phonetic_list
+}
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/lib/sentence-count.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/lib/sentence-count.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/lib/sentence-count.adp	23 Feb 2004 23:00:06 -0000	1.1
@@ -0,0 +1,2 @@
+<listtemplate name="sentences"></listtemplate>
+<listfilters name="sentences"></listfilters>
Index: openacs-4/contrib/packages/vocabulary/lib/sentence-count.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/lib/sentence-count.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/lib/sentence-count.tcl	23 Feb 2004 23:00:06 -0000	1.1
@@ -0,0 +1,48 @@
+ad_page_contract {
+    includelet for grouped sentence list
+} {
+    locale_b:optional
+    {return_url "."}
+}
+
+if { ![info exists package_id] } {
+    set package_id [ad_conn package_id]
+}
+set return_url [export_vars -base $return_url {locale} ]
+set actions [list "Add a Sentence" sentence-edit "Add a Sentence" "Add a Sentence Group" sentence-group-add "Add a Sentence Group"]
+template::list::create \
+    -name sentences \
+    -multirow sentences \
+    -actions $actions \
+    -elements {
+        sentence_count {
+	    link_url_col sentence_locale_url
+            label "Sentences"
+        }
+        locale {
+	    label "Locale"
+            display_template {
+                @sentences.locale_label@
+            }
+	}
+    }
+
+# TODO: filter by package
+
+db_multirow \
+    -extend {
+	sentence_locale_url
+    } sentences sentence_select "
+        select count(ci.item_id) as sentence_count,
+               al.label as locale_label,
+               al.locale as locale
+          from cr_items ci,
+               vocab_sentence vs,
+               ad_locales al
+         where ci.content_type = 'vocab_sentence'
+           and vs.id = ci.live_revision
+           and al.locale = vs.locale2
+         group by al.label, al.locale
+    " {
+	set sentence_locale_url [export_vars -base "sentence-list" {{locale $locale}}]
+    }
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/lib/sentence-list.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/lib/sentence-list.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/lib/sentence-list.adp	23 Feb 2004 23:00:06 -0000	1.1
@@ -0,0 +1,2 @@
+<listtemplate name="sentences"></listtemplate>
+<listfilters name="sentences"></listfilters>
Index: openacs-4/contrib/packages/vocabulary/lib/sentence-list.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/lib/sentence-list.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/lib/sentence-list.tcl	23 Feb 2004 23:00:06 -0000	1.1
@@ -0,0 +1,64 @@
+ad_page_contract {
+    includelet for word list
+} {
+    locale:optional
+    orderby:optional
+    {return_url "."}
+}
+
+set actions [list "Add a Sentence" sentence-edit "Add a Sentence" "Add a Sentence Group" sentence-group-add "Add a Sentence Group"]
+template::list::create \
+    -name sentences \
+    -multirow sentences \
+    -actions $actions \
+    -elements {
+	text {
+	    link_url_col edit_url
+	    label "Sentence"
+	}
+        locale {
+            label "Language"
+        }
+	delete {
+	    link_url_col delete_url 
+	    display_template {
+		<img src="/resources/acs-subsite/Delete16.gif" width="16" height="16" border="0">
+	    }
+	    sub_class narrow
+	}
+    } \
+    -filters {
+	locale {
+	    label "Locale"
+	    where_clause {
+		al.locale = :locale
+	    }
+	    values {
+                [vocab::locale_list]
+	    }
+	}
+    }
+
+
+db_multirow \
+    -extend {
+	edit_url
+	delete_url
+    } sentences sentences_select "
+	select ci.item_id as id,
+               al.label as locale,
+               substring(cr.content,0,30) as text
+          from cr_items ci, 
+               cr_revisions cr,
+               ad_locales al,
+               vocab_sentence vs
+         where ci.content_type = 'vocab_sentence'
+           and cr.revision_id = ci.live_revision
+           and vs.id = ci.live_revision
+           and al.locale = vs.locale2
+	[template::list::filter_where_clauses -name sentences -and]
+         order by vs.sort_order
+    " {
+	set edit_url [export_vars -base "sentence-edit" {id}]
+	set delete_url [export_vars -base "sentence-delete" {id}]
+    }
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/lib/sentence-train-list.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/lib/sentence-train-list.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/lib/sentence-train-list.adp	23 Feb 2004 23:00:06 -0000	1.1
@@ -0,0 +1,2 @@
+<listtemplate name="sentences"></listtemplate>
+<listfilters name="sentences"></listfilters>
Index: openacs-4/contrib/packages/vocabulary/lib/sentence-train-list.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/lib/sentence-train-list.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/lib/sentence-train-list.tcl	23 Feb 2004 23:00:06 -0000	1.1
@@ -0,0 +1,99 @@
+ad_page_contract {
+    includelet for sentence list
+} {
+    orderby:optional
+    locale:optional
+    {return_url "."}
+    {group ""}
+}
+
+if { ![info exists package_id] } {
+    set package_id [ad_conn package_id]
+}
+
+set user_id [auth::get_user_id]
+set vocab_url [apm_package_url_from_key vocabulary]
+set return_url [export_vars -base [ad_conn url] {locale orderby}]
+#set action [list "Add a new sentence" [export_vars -base sentence-add-wizard] "Add a new sentence"]
+set action [list "Add a group of sentences" [export_vars -base sentence-group-add] "Add a group of sentences"]
+
+template::list::create \
+    -name sentences \
+    -multirow sentences \
+    -actions "$action" \
+    -elements {
+        on_list {
+            label "On your list"
+            display_template {
+                @sentences.on_list_html;noquote@
+            }
+	    orderby {on_list}
+        }
+        sentence {
+            label "Sentence"
+	    orderby {lower(sentence)}
+        }
+        locale {
+	    label "Sentence Locale"
+	    orderby {locale}
+	}
+	edit {
+	    link_url_col edit_url 
+	    display_template {
+		<img src="/resources/acs-subsite/Edit16.gif" width="16" height="16" border="0">
+	    }
+	    sub_class narrow
+	}
+    } \
+    -filters {
+	locale {
+	    label "Locale"
+	    where_clause {
+		al.locale = :locale
+	    }
+	    values {
+                [vocab::locale_list]
+	    }
+	}
+    }
+
+if { [exists_and_not_null group] } {
+    set group_clause " and ci.item_id in (select subject_id from vocab_test_subject where test_id = :group)"
+} else {
+    set group_clause ""
+}
+
+db_multirow \
+    -extend {
+        on_list_html
+        edit_url
+    } sentences sentence_select "
+        select ci.item_id as id,
+               vs.sort_order,
+               cr.content as sentence,
+               al.label as locale,
+               coalesce((select 1
+                  from vocab_test 
+                 where test_type='sentence'
+                   and test_subject = ci.item_id),0) as on_list      
+          from vocab_sentence vs,
+               cr_items ci,
+               cr_revisions cr,
+               ad_locales al
+         where ci.content_type = 'vocab_sentence'
+           and cr.revision_id = ci.live_revision
+           and vs.id = ci.live_revision
+           and al.locale = vs.locale2
+               $group_clause
+	[template::list::filter_where_clauses -name sentences -and]
+	[template::list::orderby_clause -name sentences -orderby]
+    " {
+	set delete_url [export_vars -base "test-delete" {id return_url}]
+	set edit_url [export_vars -base "${vocab_url}sentence-edit" {id return_url}]
+        set add_url [export_vars -base "sentence-add" {id return_url}]
+        if { $on_list } {
+            set on_list_html "<a href=\"$delete_url\" title=\"Remove this sentence\"><button style=\"margin: 1px; padding: 2px; border-width: thin;\"><img src=\"/shared/images/checkboxchecked\" height=\"13\" width=\"13\" border=\"0\" style=\"background-color: white;\"></button></a>"
+        } else {
+            set on_list_html "<a href=\"$add_url\" title=\"Add this sentence\"><button style=\"margin: 1px; padding: 2px; border-width: thin;\"><img src=\"/shared/images/checkbox\" height=\"13\" width=\"13\" border=\"0\" style=\"background-color: white;\"></button></a>"
+        }
+    }
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/lib/test-count.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/lib/test-count.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/lib/test-count.adp	23 Feb 2004 23:00:06 -0000	1.1
@@ -0,0 +1,2 @@
+<listtemplate name="tests"></listtemplate>
+
Index: openacs-4/contrib/packages/vocabulary/lib/test-count.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/lib/test-count.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/lib/test-count.tcl	23 Feb 2004 23:00:06 -0000	1.1
@@ -0,0 +1,48 @@
+ad_page_contract {
+    includelet for grouped count of tests
+} {
+    orderby:optional
+    locale:optional
+    {return_url "."}
+}
+
+if { ![info exists package_id] } {
+    set package_id [ad_conn package_id]
+}
+
+set user_id [auth::get_user_id]
+set vocab_url [apm_package_url_from_key vocabulary]
+set return_url [ad_conn url]
+set action1 [list "Create a New Test" [export_vars -base test-edit {return_url}] "Create a New Test"]
+
+template::list::create \
+    -name tests \
+    -multirow tests \
+    -actions $action1 \
+    -elements {
+        test_type {
+            link_url_col test_list_url
+            label "Test Type"
+        }
+        label {
+	    label "Locale"
+	}
+        count {
+	    label "Count"
+	}
+    }
+
+db_multirow -extend { test_list_url } tests test_select "
+        select count(vt.id) as count,
+               vt.test_type,
+               vt.locale_a as locale,
+               al.label
+          from vocab_test vt,
+               ad_locales al
+         where vt.package_id = :package_id
+           and al.locale = vt.locale_a
+         group by vt.test_type, vt.locale_a, al.label
+" {
+    set test_list_url [export_vars -base "test-list" {test_type locale}]
+}
+
Index: openacs-4/contrib/packages/vocabulary/lib/test-list.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/lib/test-list.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/lib/test-list.adp	23 Feb 2004 23:00:06 -0000	1.1
@@ -0,0 +1,3 @@
+(Only 50 items are shown at a time.  Real pagination forthcoming ...)
+<listtemplate name="tests"></listtemplate>
+<listfilters name="tests"></listfilters>
Index: openacs-4/contrib/packages/vocabulary/lib/test-list.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/lib/test-list.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/lib/test-list.tcl	23 Feb 2004 23:00:06 -0000	1.1
@@ -0,0 +1,108 @@
+ad_page_contract {
+    includelet for word list
+} {
+    orderby:optional
+    locale_filter:optional
+    test_type_filter:optional
+    {result_count_filter ""}
+    {return_url "."}
+}
+
+if { ![info exists package_id] } {
+    set package_id [ad_conn package_id]
+}
+
+set user_id [auth::get_user_id]
+
+set return_url [export_vars -base [ad_conn url] {test_type orderby locale return_url}]
+
+template::list::create \
+    -name tests \
+    -multirow tests \
+    -elements {
+        on_list {
+            label "On your list"
+            display_template {
+                @tests.on_list_html;noquote@
+            }
+	    orderby {on_list}
+        }
+        question {
+            link_url_col test_url
+            display_template {
+                @tests.question;noquote@
+            }
+            label "Test"
+	    orderby {question}
+        }
+        result_count {
+            label "Count"
+	    orderby {result_count}
+        }
+        result_sum {
+            label "Sum"
+	    orderby {result_sum}
+        }
+	delete {
+	    link_url_col test_delete_url 
+	    display_template {
+		<img src="/resources/acs-subsite/Delete16.gif" width="16" height="16" border="0">
+	    }
+	    sub_class narrow
+	}
+    } \
+    -filters {
+	locale_filter {
+	    label "Locale"
+	    where_clause {
+		vt.locale_a = :locale_filter
+	    }
+	    values {
+                [vocab::locale_list]
+	    }
+	}
+	test_type {
+	    label "Test Type"
+	    where_clause {
+		vt.test_type = :test_type_filter
+	    }
+	    values {
+                [vocab::train::test_type_list]
+	    }
+	}
+    }
+
+db_multirow -extend { test_url test_train_add_url test_train_delete_url on_list_html test_delete_url } tests select_tests "
+    select vt.id,
+           vt.test_type,
+           coalesce((select 1 
+                       from vocab_user_test_map
+                      where test_id = vt.id
+                        and user_id = :user_id),0) as on_list,
+           question,
+           coalesce((select count(*) 
+                       from vocab_test_result
+                      where test_id = vt.id
+                        and user_id = :user_id),0) as result_count,
+           coalesce((select sum(result1) 
+                       from vocab_test_result
+                      where test_id = vt.id
+                        and user_id = :user_id),0) as result_sum
+      from vocab_test vt
+     where package_id = :package_id
+	[template::list::filter_where_clauses -name tests -and]
+	[template::list::orderby_clause -name tests -orderby]
+limit 500
+" {
+    set test_train_add_url [export_vars -base "test-train-add" {id return_url}]
+    set test_train_delete_url [export_vars -base "test-train-delete" {id return_url}]
+    set test_url [export_vars -base "test" {{test_id $id} test_type }]
+    set test_delete_url [export_vars -base "test-delete" {id return_url}]
+
+    if { $on_list } {
+        set on_list_html "<a href=\"$test_train_delete_url\" title=\"Remove this test\"><img src=\"/shared/images/checkboxchecked\" height=\"13\" width=\"13\" border=\"0\" style=\"background-color: white;\"></a>"
+    } else {
+        set on_list_html "<a href=\"$test_train_add_url\" title=\"Add this test\"><img src=\"/shared/images/checkbox\" height=\"13\" width=\"13\" border=\"0\" style=\"background-color: white;\"></a>"
+    }
+
+}
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/lib/test-user-count.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/lib/test-user-count.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/lib/test-user-count.adp	23 Feb 2004 23:00:06 -0000	1.1
@@ -0,0 +1,2 @@
+<listtemplate name="tests"></listtemplate>
+
Index: openacs-4/contrib/packages/vocabulary/lib/test-user-count.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/lib/test-user-count.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/lib/test-user-count.tcl	23 Feb 2004 23:00:06 -0000	1.1
@@ -0,0 +1,48 @@
+ad_page_contract {
+    includelet for grouped count of tests
+} {
+    orderby:optional
+    locale:optional
+    {return_url "."}
+}
+
+if { ![info exists package_id] } {
+    set package_id [ad_conn package_id]
+}
+
+set user_id [auth::get_user_id]
+set vocab_url [apm_package_url_from_key vocabulary]
+set return_url [ad_conn url]
+set action1 [list "Add test" [export_vars -base test-add {return_url}] "Add test"]
+
+template::list::create \
+    -name tests \
+    -multirow tests \
+    -actions $action1 \
+    -no_data "You don't have any tests." \
+    -elements {
+        test_type {
+            label "Test Type"
+        }
+        label {
+	    label "Locale"
+	}
+        count {
+	    label "Count"
+	}
+    }
+
+db_multirow tests test_select "
+        select count(vt.id) as count,
+               vt.test_type,
+               al.label
+          from vocab_test vt,
+               vocab_user_test_map vutm,
+               ad_locales al
+         where vutm.user_id = :user_id
+           and vt.id = vutm.test_id
+           and vt.package_id = :package_id
+           and al.locale = vt.locale_a
+         group by vt.test_type, al.label
+"
+
Index: openacs-4/contrib/packages/vocabulary/lib/test-user-list.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/lib/test-user-list.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/lib/test-user-list.adp	23 Feb 2004 23:00:06 -0000	1.1
@@ -0,0 +1,2 @@
+<listtemplate name="tests"></listtemplate>
+<listfilters name="tests"></listfilters>
Index: openacs-4/contrib/packages/vocabulary/lib/test-user-list.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/lib/test-user-list.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/lib/test-user-list.tcl	23 Feb 2004 23:00:06 -0000	1.1
@@ -0,0 +1,93 @@
+ad_page_contract {
+    includelet for word list
+} {
+    orderby:optional
+    locale:optional
+    test_type:optional
+    {return_url "."}
+}
+
+if { ![info exists package_id] } {
+    set package_id [ad_conn package_id]
+}
+
+set user_id [auth::get_user_id]
+
+set return_url [export_vars -base [ad_conn url] {test_type orderby locale return_url}]
+
+template::list::create \
+    -name tests \
+    -multirow tests \
+    -elements {
+        id {
+            link_url_col test_url
+            label "Test"
+        }
+        test_type {
+            label "Type"
+        }
+        result_count {
+            label "Count"
+        }
+        result_sum {
+            label "Sum"
+        }
+        score {
+            label "Average"
+        }
+
+        last_five {
+            label "Last 5"
+        }
+    } \
+    -filters {
+	test_type {
+	    label "Test Type"
+	    where_clause {
+		vt.test_type = :test_type
+	    }
+	    values {
+                [vocab::train::test_type_list]
+	    }
+	}
+    }
+
+# TODO: this query is not package-away - should join with tests as well
+# get all tests with results or on the test list
+# TODO: this query needs more help
+db_multirow -extend { test_url test_train_add_url test_train_delete_url on_list_html score last_five } tests select_tests "
+    select vt.id,
+           max(vt.test_type) as test_type,
+           coalesce(count(vtr.answer1),0) as result_count,
+           coalesce(sum(vtr.result1),0) as result_sum
+      from vocab_test vt 
+             inner join vocab_test_result vtr on vtr.test_id = vt.id and vtr.user_id = :user_id
+	[template::list::filter_where_clauses -name tests -and]
+     group by vt.id
+    UNION
+    select vt.id,
+           max(vt.test_type) as test_type,
+           0 as result_count,
+           0  as result_sum
+      from vocab_test vt 
+             inner join vocab_user_test_map vutm on vutm.test_id = vt.id and vutm.user_id = :user_id
+	[template::list::filter_where_clauses -name tests -and]
+     group by vt.id
+	[template::list::orderby_clause -name tests -orderby]
+" {
+    set test_train_add_url [export_vars -base "test-train-add" {id return_url}]
+    set test_train_delete_url [export_vars -base "test-train-delete" {id return_url}]
+    set test_url [export_vars -base "test" {{test_id $id} test_type }]
+    if { $result_count > 0} {
+	set score "[expr {$result_sum * 100} / $result_count]%"
+    } else {
+	set score "n/a"
+    }
+    
+#    if { $result_five_count == 5 } {
+#        set last_five [expr ($result_sum * 100) / 5]
+#    } else {
+        set last_five ""
+#    }
+
+}
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/lib/word-count.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/lib/word-count.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/lib/word-count.adp	23 Feb 2004 23:00:06 -0000	1.1
@@ -0,0 +1,2 @@
+<listtemplate name="words"></listtemplate>
+<listfilters name="words"></listfilters>
Index: openacs-4/contrib/packages/vocabulary/lib/word-count.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/lib/word-count.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/lib/word-count.tcl	23 Feb 2004 23:00:06 -0000	1.1
@@ -0,0 +1,49 @@
+ad_page_contract {
+    includelet for grouped word list
+} {
+    locale_b:optional
+    orderby:optional
+    {return_url "."}
+}
+
+if { ![info exists package_id] } {
+    set package_id [ad_conn package_id]
+}
+set return_url [export_vars -base "." {locale orderby} ]
+set action [list "Add a word" [export_vars -base word-edit {return_url}]]
+
+template::list::create \
+    -name words \
+    -multirow words \
+    -actions $action \
+    -elements {
+        word_count {
+	    link_url_col word_locale_url
+            label "Words"
+	    orderby {word_count}
+        }
+        locale {
+	    label "Locale"
+            display_template {
+                @words.locale_label@
+            }
+	    orderby {locale_label}
+	}
+    }
+
+db_multirow \
+    -extend {
+	word_locale_url
+    } words word_select "
+        select count(vw.id) as word_count,
+               al.label as locale_label,
+               al.locale as locale
+          from vocab_word vw,
+               ad_locales al
+         where vw.package_id = :package_id
+           and al.locale = vw.locale
+         group by al.label, al.locale
+	[template::list::orderby_clause -name words -orderby]
+    " {
+	set word_locale_url [export_vars -base "word-list" {{locale $locale}}]
+    }
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/lib/word-list.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/lib/word-list.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/lib/word-list.adp	23 Feb 2004 23:00:06 -0000	1.1
@@ -0,0 +1,10 @@
+<table>
+  <tr>
+    <td valign="top" style="padding-right:2em;">
+      <listtemplate name="words"></listtemplate>
+    </td>
+    <td valign="top">
+      <listfilters name="words"></listfilters>
+    </td>
+  </tr>
+</table>
Index: openacs-4/contrib/packages/vocabulary/lib/word-list.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/lib/word-list.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/lib/word-list.tcl	23 Feb 2004 23:00:06 -0000	1.1
@@ -0,0 +1,121 @@
+ad_page_contract {
+    includelet for word list
+} {
+    locale_b:optional
+    locale:optional
+    orderby:optional
+    {paginate "a"}
+    return_url:optional
+}
+
+# doing this here instead of page_contract because defaults work
+# unexpectedly in page contract in includelet
+if { ![exists_and_not_null return_url] } {
+    set return_url [ad_conn url][ad_conn query]
+}
+
+# do some cheap-ass pagination until somebody explains to me how to
+# use Jon Griffin's paginator from within a template::list
+
+set paginate_like $paginate%    
+vocab::conn
+set action [list "Add a word" [export_vars -base word-edit {return_url}] "Add a word"]
+set action [concat $action [list "Import word list" [export_vars -base word-list-add {return_url}] "Import a word list"]]
+set locale_b_label [ad_locale_get_label $locale_b]
+template::list::create \
+    -name words \
+    -multirow words \
+    -actions $action \
+    -elements {
+        word {
+	    link_url_col edit_url
+            label "Word"
+	    orderby {lower(word)}
+        }
+        phonetic {
+            label ""
+	    sub_class narrow
+        }
+        label {
+	    label "Locale"
+	    orderby {lower(al.locale)}
+	}
+        definition {
+            label "Meaning in [set locale_b_label]"
+        }
+        on_list {
+            label "On your list"
+            display_template {
+                @words.on_list_html;noquote@
+            }
+	    orderby {on_list}
+        }
+	delete {
+	    link_url_col delete_url 
+	    display_template {
+		<img src="/resources/acs-subsite/Delete16.gif" width="16" height="16" border="0">
+	    }
+	    sub_class narrow
+	}
+
+    } \
+    -filters {
+	locale {
+	    label "Locale"
+	    where_clause {
+		al.locale = :locale
+	    }
+	    values {
+                [vocab::locale_list]
+	    }
+	}
+	paginate {
+	    label "Letter"
+	    where_clause "
+		(vw.word like :paginate_like or vw.local_phonetic like :paginate_like)
+	    "
+	    values {
+                [vocab::letter_list]
+	    }
+	}
+    }
+
+db_multirow \
+    -extend {
+	edit_url
+	delete_url
+        on_list_html
+    } words word_select "
+        select vw.id,
+               vw.word,
+               al.label as label,
+               coalesce(local_phonetic,ipa_phonetic) as phonetic,
+               (select substring(definition,0,20)
+                  from vocab_definition
+                 where word_id = vw.id
+                   and locale = :locale_b
+                 limit 1) as definition,
+               coalesce((select 1 
+                  from vocab_word_list 
+                 where word_id = vw.id
+                   and user_id = :user_id
+                   and package_id = :package_id),0) as on_list
+          from vocab_word vw,
+               ad_locales al
+         where vw.package_id = :package_id
+           and al.locale = vw.locale
+	[template::list::filter_where_clauses -name words -and]
+	[template::list::orderby_clause -name words -orderby]
+    " {
+	set edit_url [export_vars -base "word-edit" {id}]
+	set delete_url [export_vars -base "word-delete" {id return_url}]
+
+	set delete_train_url [export_vars -base "word-train-delete" {id return_url}]
+        set add_train_url [export_vars -base "word-train-add" {id return_url}]
+        if { $on_list } {
+            set on_list_html "<a href=\"$delete_train_url\" title=\"Remove this word\">[vocab::img::checkedbox]</a>"
+        } else {
+            set on_list_html "<a href=\"$add_train_url\" title=\"Add this word\">[vocab::img::checkbox]</a>"
+        }
+    }
+
Index: openacs-4/contrib/packages/vocabulary/lib/word-train-list.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/lib/word-train-list.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/lib/word-train-list.adp	23 Feb 2004 23:00:06 -0000	1.1
@@ -0,0 +1,2 @@
+<listtemplate name="words"></listtemplate>
+<listfilters name="words"></listfilters>
Index: openacs-4/contrib/packages/vocabulary/lib/word-train-list.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/lib/word-train-list.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/lib/word-train-list.tcl	23 Feb 2004 23:00:06 -0000	1.1
@@ -0,0 +1,85 @@
+ad_page_contract {
+    includelet for word list
+} {
+    orderby:optional
+    locale:optional
+    {return_url "."}
+}
+
+if { ![info exists package_id] } {
+    set package_id [ad_conn package_id]
+}
+
+set user_id [auth::get_user_id]
+set vocab_url [apm_package_url_from_key vocabulary]
+set return_url [export_vars -base [ad_conn url] {locale orderby}]
+#set action2 [list "Add a new word" [export_vars -base word-add-wizard] "Add a new word"]
+
+template::list::create \
+    -name words \
+    -multirow words \
+    -elements {
+        on_list {
+            label "On your list"
+            display_template {
+                @words.on_list_html;noquote@
+            }
+	    orderby {on_list}
+        }
+        word {
+            label "Word"
+	    orderby {lower(word)}
+        }
+        locale {
+	    label "Word Locale"
+	    orderby {locale}
+	}
+        ipa_phonetic {
+	    label "IPA Phonetic"
+	    orderby {ipa_phonetic}
+	}
+        local_phonetic {
+	    label "Local Phonetic"
+	    orderby {local_phonetic}
+	}
+    } \
+    -filters {
+	locale {
+	    label "Locale"
+	    where_clause {
+		al.locale = :locale
+	    }
+	    values {
+                [vocab::locale_list]
+	    }
+	}
+    }
+
+db_multirow \
+    -extend {
+        on_list_html
+    } words word_select "
+        select vw.id,
+               vw.word,
+               al.label as locale,
+               vw.ipa_phonetic,
+               vw.local_phonetic,
+               coalesce((select 1 
+                  from vocab_word_list 
+                 where word_id = vw.id
+                   and user_id = :user_id
+                   and package_id = :package_id),0) as on_list
+          from vocab_word vw,
+               ad_locales al
+         where al.locale = vw.locale
+	[template::list::filter_where_clauses -name words -and]
+	[template::list::orderby_clause -name words -orderby]
+    " {
+	set delete_url [export_vars -base "word-delete" {id return_url}]
+        set add_url [export_vars -base "word-add" {id return_url}]
+        if { $on_list } {
+            set on_list_html "<a href=\"$delete_url\" title=\"Remove this word\"><img src=\"/shared/images/checkboxchecked\" height=\"13\" width=\"13\" border=\"0\" style=\"background-color: white;\"></a>"
+        } else {
+            set on_list_html "<a href=\"$add_url\" title=\"Add this word\"><img src=\"/shared/images/checkbox\" height=\"13\" width=\"13\" border=\"0\" style=\"background-color: white;\"></a>"
+        }
+    }
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/sql/postgresql/ipa.sql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/sql/postgresql/ipa.sql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/sql/postgresql/ipa.sql	23 Feb 2004 23:00:06 -0000	1.1
@@ -0,0 +1,55 @@
+--
+-- PostgreSQL database dump
+--
+
+COPY vocab_phonetic (id, locale, sort_order, phonetic_symbol) FROM stdin;
+8	\N	\N	ʌ
+9	\N	\N	ɑ
+10	\N	\N	æ
+11	\N	\N	e
+12	\N	\N	ə
+13	\N	\N	ɪ
+14	\N	\N	i
+15	\N	\N	u
+16	\N	\N	o
+17	\N	\N	ɔ
+18	\N	\N	ð
+19	\N	\N	p
+20	\N	\N	b
+21	\N	\N	t
+22	\N	\N	d
+23	\N	\N	k
+24	\N	\N	g
+25	\N	\N	m
+26	\N	\N	n
+27	\N	\N	f
+28	\N	\N	v
+29	\N	\N	s
+30	\N	\N	h
+31	\N	\N	l
+32	\N	\N	w
+33	\N	\N	ʙ
+34	\N	\N	ɐ
+35	\N	\N	ɒ
+36	\N	\N	ɓ
+37	\N	\N	β
+38	\N	\N	ɕ
+39	\N	\N	ç
+40	\N	\N	ɗ
+41	\N	\N	ɖ
+42	\N	\N	ʤ
+43	\N	\N	ˈ
+44	\N	\N	ˌ
+45	\N	\N	ʴ
+46	\N	\N	̚
+47	\N	\N	ɛ
+48	\N	\N	ŋ
+49	\N	\N	ʒ
+50	\N	\N	j
+51	\N	\N	ʃ
+52	\N	\N	θ
+\.
+
+SELECT pg_catalog.setval ('vocab_phonetic_id_seq', 52, true);
+
+
Index: openacs-4/contrib/packages/vocabulary/sql/postgresql/vocabulary-create.sql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/sql/postgresql/vocabulary-create.sql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/sql/postgresql/vocabulary-create.sql	23 Feb 2004 23:00:06 -0000	1.1
@@ -0,0 +1,10 @@
+-- 
+
+-- @author Joel Aufrecht (joel@aufrecht.org)
+-- @creation-date 2004-02-01
+-- @cvs-id $Id: vocabulary-create.sql,v 1.1 2004/02/23 23:00:06 joela Exp $
+--
+
+\i vocabulary-tables-create.sql
+\i vocabulary-train-tables-create.sql
+\i ipa.sql
Index: openacs-4/contrib/packages/vocabulary/sql/postgresql/vocabulary-drop.sql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/sql/postgresql/vocabulary-drop.sql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/sql/postgresql/vocabulary-drop.sql	23 Feb 2004 23:00:06 -0000	1.1
@@ -0,0 +1,8 @@
+-- 
+
+-- @author Joel Aufrecht (joel@aufrecht.org)
+-- @creation-date 2004-02-01
+-- @cvs-id $Id: vocabulary-drop.sql,v 1.1 2004/02/23 23:00:06 joela Exp $
+--
+\i vocabulary-train-tables-drop.sql
+\i vocabulary-tables-drop.sql
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/sql/postgresql/vocabulary-tables-create.sql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/sql/postgresql/vocabulary-tables-create.sql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/sql/postgresql/vocabulary-tables-create.sql	23 Feb 2004 23:00:07 -0000	1.1
@@ -0,0 +1,215 @@
+--
+
+-- @author Joel Aufrecht (joel@aufrecht.org)
+-- @creation-date 2004-02-01
+-- @cvs-id $Id: vocabulary-tables-create.sql,v 1.1 2004/02/23 23:00:07 joela Exp $
+--
+
+select acs_object_type__create_type (
+    'vocab_word',                   -- object_type
+    'word',                         -- pretty_name
+    'words',                        -- pretty_plural
+    'acs_object',                   -- supertype
+    'vocab_word',                   -- table_name
+    'id',                           -- id_column
+    null,                           -- package_name
+    'f',                            -- abstract_p
+    null,                           -- type_extension_table
+    'vocab_word__name'              -- name_method
+);
+
+create table vocab_word (
+        id              integer
+                          constraint vocab_word_id_fk
+                            references acs_objects(object_id)
+                          constraint vocab_word_pk 
+                            primary key,
+        word            varchar(50)
+                          not null,
+        locale          varchar(30)
+                          constraint vocab_word_locale_fk 
+                            references ad_locales,
+        ipa_phonetic    varchar(100),
+        local_phonetic  varchar(100),
+        package_id      integer 
+                          not null
+                          constraint vocab_word_package_id_fk
+                            references apm_packages
+);
+
+comment on table vocab_word is '
+Each record is a single word in a single locale.  Homonyms should have their own entries.';
+
+comment on column vocab_word.word is 'The spelling of the word in its native character set.  Should be unicode.';
+
+comment on column vocab_word.ipa_phonetic is 'International Phonetic Alphabet spelling.';
+
+comment on column vocab_word.local_phonetic is 'Spelling in appropriate phonetic alphabet.';
+
+create or replace function vocab_word__name (integer)
+returns varchar as '
+declare
+    p_id              alias for $1;
+    v_name            varchar;
+begin
+    select word into v_name
+        from vocab_word
+        where id = p_id;
+    return v_name;
+end;
+' language 'plpgsql';
+
+create or replace function vocab_word__new (
+    integer,     -- package_id
+    varchar,     -- word
+    varchar,     -- locale
+    varchar,     -- ipa_phonetic
+    varchar,     -- local_phonetic
+    integer,     -- creation_user
+    varchar      -- creation_ip
+) returns integer as '
+declare
+    p_package_id           alias for $1;
+    p_word                 alias for $2;
+    p_locale               alias for $3;
+    p_ipa_phonetic         alias for $4;
+    p_local_phonetic       alias for $5;
+    p_creation_user        alias for $6;
+    p_creation_ip          alias for $7;
+    v_id             integer;
+begin
+    v_id := acs_object__new (
+        null,
+        ''vocab_word'',
+        current_timestamp,
+        p_creation_user,
+        p_creation_ip,
+        p_package_id
+    );
+
+    insert into vocab_word (
+      id, 
+      package_id,
+      word,
+      locale,
+      ipa_phonetic,
+      local_phonetic
+    ) values (
+      v_id, 
+      p_package_id,
+      p_word,
+      p_locale,   
+      p_ipa_phonetic,
+      p_local_phonetic
+    );
+
+    PERFORM acs_permission__grant_permission(
+        v_id,
+	p_creation_user,
+	''admin''
+    );
+
+    return v_id;   
+end;
+' language 'plpgsql';
+
+create or replace function vocab_word__delete (integer)
+returns integer as '
+declare
+    p_id alias for $1;
+begin
+    delete from vocab_word
+        where id = p_id;
+    PERFORM acs_object__delete(p_id);
+    return 0;
+
+end;
+' language 'plpgsql';
+
+create table vocab_definition (
+        id              serial    
+                          constraint vocab_def_pk
+                            primary key,   
+        word_id         integer 
+                          constraint vocab_def_word_fk
+                          references vocab_word,
+        sort_order      integer,
+        locale          varchar(30)
+                          constraint vocab_word_locale_fk 
+                            references ad_locales,
+        definition      text,
+        unique     (word_id, locale, sort_order)
+);
+
+comment on table vocab_definition is 'Each record is one definition of one word in one locale.  This means that definitions are arbitrary text instead of links to other words.  TODO: Perhaps we should optionally link to another word here. so that a definition can be more or less structured.';
+        
+create table vocab_phonetic (
+        id              serial    
+                          constraint vocab_phonetic_pk
+                            primary key,   
+        phonetic_symbol varchar(5),
+        locale          varchar(30)
+                          constraint vocab_word_locale_fk 
+                            references ad_locales,
+        sort_order      integer
+        );
+
+comment on table vocab_phonetic is 'Each record is a letter in a phonetic alphabet.  Deliberately package_ignorant.';
+
+comment on column vocab_phonetic.locale is 'Null is assumed to be IPA.';
+
+create table vocab_phonetic_example (
+        id              serial    
+                          constraint vocab_phonetic_example_pk
+                            primary key,   
+        phonetic_id     integer 
+                          constraint vocab_phonetic_phonetic_fk
+                            references vocab_phonetic,
+        word_id         integer 
+                          constraint vocab_phonetic_word_fk
+                            references vocab_word,
+        comments        varchar(100),
+        unique         (phonetic_id, word_id)
+);      
+
+comment on table vocab_phonetic_example is 'Each record is a word with contains a sound of an PHONETIC symbol.  Each record is indirectly locale-specific and package-specific, via word.  Since we can auto-generate some of this via direct lookup of phonetic symbols within word text, this table is semi-superfluous.  It might see life again holding additional or special comments.';
+
+select content_type__create_type(
+    'vocab_sentence',             -- content_type
+    'content_revision',           -- supertype
+    'sentence',                   -- pretty_name,
+    'sentences',                  -- pretty_plural
+    'vocab_sentence',             -- table_name
+    'id',                         -- id_column
+    null                          -- name_method
+);
+
+-- we have to create a custom locale field because locale in cr_items
+-- is varchar(4), even though ad_locales locale is varchar(30)
+-- TODO: make this FK to ad_locales
+select content_type__create_attribute(
+    'vocab_sentence',              -- content_type
+    'locale2',                     -- attribute_name
+    'string',                      -- datatype
+    'Locale',                      -- pretty_name
+    'Locales',                     -- pretty_plural
+    1,                             -- sort_order
+    null,                          -- default_value
+    'varchar(30)'                  -- column_spec
+);
+
+select content_type__create_attribute(
+    'vocab_sentence',              -- content_type
+    'sort_order',                   -- attribute_name
+    'integer',                     -- datatype
+    'Sort Order',                  -- pretty_name
+    'Sort Orders',                 -- pretty_plural
+    2,                             -- sort_order
+    null,                          -- default_value
+    'integer'                      -- column_spec
+);
+
+-- necessary to work around limitation of content repository:
+select content_folder__register_content_type(-100,'vocab_sentence','t');
+
+create sequence vocab_sentence_name_seq;
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/sql/postgresql/vocabulary-tables-drop.sql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/sql/postgresql/vocabulary-tables-drop.sql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/sql/postgresql/vocabulary-tables-drop.sql	23 Feb 2004 23:00:07 -0000	1.1
@@ -0,0 +1,34 @@
+-- 
+
+-- @author Joel Aufrecht (joel@aufrecht.org)
+-- @creation-date 2004-02-01
+-- @cvs-id $Id: vocabulary-tables-drop.sql,v 1.1 2004/02/23 23:00:07 joela Exp $
+--
+
+
+select content_type__drop_type(
+    'vocab_sentence',
+    't',
+    't'
+);
+
+drop table vocab_phonetic_example;
+drop table vocab_phonetic;
+drop table vocab_definition;
+
+drop function vocab_word__name (integer);
+drop function vocab_word__new(
+    integer,     -- id
+    integer,     -- package_id
+    varchar,     -- word
+    varchar,     -- locale
+    varchar,     -- ipa_phonetic
+    varchar,     -- local_phonetic
+    timestamptz, -- entry_date
+    integer,     -- creation_user
+    varchar      -- creation_ip
+);
+drop function vocab_word__delete (integer);
+
+drop table vocab_word;
+select acs_object_type__drop_type ('vocab_word', true);
Index: openacs-4/contrib/packages/vocabulary/sql/postgresql/vocabulary-train-tables-create.sql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/sql/postgresql/vocabulary-train-tables-create.sql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/sql/postgresql/vocabulary-train-tables-create.sql	23 Feb 2004 23:00:07 -0000	1.1
@@ -0,0 +1,93 @@
+-- @author Joel Aufrecht (joel@aufrecht.org)
+-- @creation-date 2004-02-01
+-- @cvs-id $Id: vocabulary-train-tables-create.sql,v 1.1 2004/02/23 23:00:07 joela Exp $
+--
+
+create table vocab_word_list (
+        id              serial
+                           primary key,
+        user_id         integer
+                          not null
+                          references users,
+        word_id         integer
+                          not null
+                          references vocab_word,
+        locale          varchar(30)
+                            references ad_locales,
+        date_added      timestamptz,
+        package_id      integer 
+                          not null
+                            references apm_packages,
+        unique     (user_id, word_id, package_id)
+);
+
+comment on table vocab_word_list is '
+Each record is one word which one user has been assigned.';
+
+create table vocab_test (
+        id              serial
+                           primary key,
+        test_type       varchar(30)
+                          not null,
+        test_subject    integer,
+        question        text,
+        answer          text,
+        locale_a        varchar(30)
+                            references ad_locales,
+        locale_b        varchar(30)
+                            references ad_locales,
+        package_id      integer 
+                          not null
+                            references apm_packages
+);
+
+comment on table vocab_test is '
+Each record is one test.  Question and Answer may be null, which implies they are derived; or populated, which could be unique data or caching, depending on the test type.';
+
+comment on column vocab_test.test_subject is '
+Depending on test_type, this is the id of a word, sentence, or other object.';
+
+create table vocab_test_subject (
+        test_id         integer
+                          not null
+                          references vocab_test (id),
+        subject_id      integer,
+        unique (test_id, subject_id)
+);
+
+comment on table vocab_test_subject is '
+If a test has multiple subjects, store them here.';
+
+create table vocab_user_test_map (
+        id              serial
+                           primary key,
+        user_id         integer
+                          not null
+                          references users,
+        test_id         integer
+                          not null
+                          references vocab_test (id),
+        date_added      timestamptz,
+        unique (user_id, test_id)
+);
+
+comment on table vocab_user_test_map is '
+Each record is the assignment of one test to one user.';
+
+
+create table vocab_test_result (
+        test_id         integer
+                          not null
+                          references vocab_test (id),
+        user_id         integer
+                          not null
+                          references users,
+        date            timestamptz,
+        answer1         varchar(500),
+        result1         integer,
+        answer2         varchar(500),
+        result2         integer,
+        answer3         varchar(500),
+        result3         integer
+);
+
Index: openacs-4/contrib/packages/vocabulary/sql/postgresql/vocabulary-train-tables-drop.sql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/sql/postgresql/vocabulary-train-tables-drop.sql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/sql/postgresql/vocabulary-train-tables-drop.sql	23 Feb 2004 23:00:07 -0000	1.1
@@ -0,0 +1,10 @@
+-- @author Joel Aufrecht (joel@aufrecht.org)
+-- @creation-date 2004-02-01
+-- @cvs-id $Id: vocabulary-train-tables-drop.sql,v 1.1 2004/02/23 23:00:07 joela Exp $
+--
+
+drop table vocab_test_result;
+drop table vocab_test_subject;
+drop table vocab_user_test_map;
+drop table vocab_test;
+drop table vocab_word_list;
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/sql/postgresql/upgrade/upgrade-0.4-0.5d1.sql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/sql/postgresql/upgrade/upgrade-0.4-0.5d1.sql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/sql/postgresql/upgrade/upgrade-0.4-0.5d1.sql	23 Feb 2004 23:00:07 -0000	1.1
@@ -0,0 +1,4 @@
+alter table vocab_phonetic rename column phonetic_symbol to phonetic_symbol_old;
+alter table vocab_phonetic add column phonetic_symbol varchar(5);
+update vocab_phonetic set phonetic_symbol=phonetic_symbol_old;
+alter table vocab_phonetic drop column phonetic_symbol_old;
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/sql/postgresql/upgrade/upgrade-0.5d1-0.5d2.sql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/sql/postgresql/upgrade/upgrade-0.5d1-0.5d2.sql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/sql/postgresql/upgrade/upgrade-0.5d1-0.5d2.sql	23 Feb 2004 23:00:07 -0000	1.1
@@ -0,0 +1,47 @@
+select content_type__create_type(
+    'vocab_sentence',             -- content_type
+    'content_revision',           -- supertype
+    'sentence',                   -- pretty_name,
+    'sentences',                  -- pretty_plural
+    'vocab_sentence',             -- table_name
+    'id',                         -- id_column
+    null                          -- name_method
+);
+
+select content_type__create_attribute(
+    'vocab_sentence',              -- content_type
+    'locale2',                     -- attribute_name
+    'string',                      -- datatype
+    'Locale',                      -- pretty_name
+    'Locales',                     -- pretty_plural
+    1,                             -- sort_order
+    null,                          -- default_value
+    'varchar(30)'                  -- column_spec
+);
+
+select content_type__create_attribute(
+    'vocab_sentence',              -- content_type
+    'sort_order',                   -- attribute_name
+    'integer',                     -- datatype
+    'Sort Order',                  -- pretty_name
+    'Sort Orders',                 -- pretty_plural
+    2,                             -- sort_order
+    null,                          -- default_value
+    'integer'                      -- column_spec
+);
+
+select content_type__create_attribute(
+    'vocab_sentence',              -- content_type
+    'hint',                        -- attribute_name
+    'string',                      -- datatype
+    'Hint',                        -- pretty_name
+    'Hints',                       -- pretty_plural
+    3,                             -- sort_order
+    null,                          -- default_value
+    'varchar(100)'                 -- column_spec
+);
+
+-- necessary to work around limitation of content repository:
+select content_folder__register_content_type(-100,'vocab_sentence','t');
+
+create sequence vocab_sentence_name_seq;
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/sql/postgresql/upgrade/upgrade-0.6-0.7.sql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/sql/postgresql/upgrade/upgrade-0.6-0.7.sql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/sql/postgresql/upgrade/upgrade-0.6-0.7.sql	23 Feb 2004 23:00:07 -0000	1.1
@@ -0,0 +1,7 @@
+select content_type__drop_attribute(
+    'vocab_sentence',              -- content_type
+    'hint',                        -- attribute_name
+    't'                            -- drop_column
+);
+
+\i ../vocabulary-train-tables-create.sql
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/sql/postgresql/upgrade/upgrade-0.7-0.8.sql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/sql/postgresql/upgrade/upgrade-0.7-0.8.sql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/sql/postgresql/upgrade/upgrade-0.7-0.8.sql	23 Feb 2004 23:00:07 -0000	1.1
@@ -0,0 +1,20 @@
+-- 
+-- packages/vocabulary/sql/postgresql/upgrade/upgrade-0.7-0.8.sql
+-- 
+-- @author Joel Aufrecht (joel@aufrecht.org)
+-- @creation-date 2004-02-21
+-- @cvs-id $Id: upgrade-0.7-0.8.sql,v 1.1 2004/02/23 23:00:07 joela Exp $
+--
+
+create or replace function vocab_word__delete (integer)
+returns integer as '
+declare
+    p_id alias for $1;
+begin
+    delete from vocab_word
+        where id = p_id;
+    PERFORM acs_object__delete(p_id);
+    return 0;
+
+end;
+' language 'plpgsql';
Index: openacs-4/contrib/packages/vocabulary/tcl/sentence-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/tcl/sentence-procs.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/tcl/sentence-procs.tcl	23 Feb 2004 23:00:07 -0000	1.1
@@ -0,0 +1,141 @@
+# packages/vocabulary/tcl/vocabulary-sentence.tcl
+
+ad_library {
+    
+    Procs for sentences.
+    
+    @author Joel Aufrecht (joel@aufrecht.org)
+    @creation-date 2004-02-22
+    @cvs-id $Id: sentence-procs.tcl,v 1.1 2004/02/23 23:00:07 joela Exp $
+    
+}
+
+
+#####################################################################
+# vocab::sentence
+######################################################################
+
+namespace eval vocab::sentence {}
+
+#---------------------------------------------------------------------
+ad_proc -public vocab::sentence::get {
+    -id:required
+    -array:required
+} { 
+    Retrieve a sentence
+    @param id The id of the sentence
+    @array The name of an array to populate with sentence data
+
+    @return 0 for success, -1 if no row
+} {
+    upvar 1 $array row
+    set got_sentence [db_0or1row sentence_select {
+	select vs.locale2 as locale,
+               cr.content as text
+          from cr_items ci,
+               cr_revisions cr,
+               vocab_sentence vs
+         where ci.item_id = :id
+           and cr.revision_id = ci.live_revision
+           and vs.id = ci.live_revision
+    } -column_array row]
+    if { $got_sentence } {
+        return 0
+    } else {
+        return -1
+    }
+}
+
+#---------------------------------------------------------------------
+ad_proc -public vocab::sentence::new {
+    -user_id:required
+    -package_id:required
+    -text:required
+    -locale:required
+    -word_list
+    -group
+    {-sort_order 0}
+} { 
+    This proc adds a sentence.
+
+    @param word_list The name of a list to populate with the sentence's words. 
+    @param group The id of a test of type 'sentence_group' to associate this sentence with.
+    @return new sentence object id.
+} {
+
+    set name vocab_sentence[db_nextval vocab_sentence_name_seq]
+    db_transaction {
+	set id [db_string sentence_insert {
+	    select content_item__new(:name,-100,null,null,null,:user_id,:package_id,null,'content_item','vocab_sentence',null,null,null,null,null)
+	}]
+
+	set revision_id [db_nextval acs_object_id_seq]
+	if { $sort_order == 0} {
+            set sort_order [db_string get_next_sort "
+                                select max(sort_order) + 1
+                                  from vocab_sentence"]
+        }
+	db_dml revision_add {
+ 	    insert into vocab_sentencei (item_id, revision_id, text, locale2, sort_order) 
+	    values (:id, :revision_id, :text, :locale, :sort_order)
+	}
+
+	db_exec_plsql make_live {
+	    select content_item__set_live_revision(:revision_id)
+	}
+
+        # should probably regexp for all non-alpha
+        if { [exists_and_not_null word_list] } {
+            upvar 1 $word_list extracted_words
+            foreach word [split $text {.;,! ?}] {
+                if {[exists_and_not_null word] } {
+                    lappend extracted_words [string tolower $word]
+                }
+            }
+        } else {
+            set extracted_words [list]
+        }
+
+    }
+    return $id
+}
+
+
+#---------------------------------------------------------------------
+ad_proc -public vocab::sentence::edit {
+    -id:required
+    -text:required
+    -locale:required
+    {-sort_order 0}
+} { 
+    This proc edits a sentence.
+    @return the id of the sentence.
+} {
+    db_transaction {
+	set revision_id [db_nextval acs_object_id_seq]
+	
+	db_dml revision_add {
+ 	    insert into vocab_sentencei (item_id, revision_id, text, locale2, sort_order)
+	    values (:id, :revision_id, :text, :locale, :sort_order)
+	}
+
+	db_exec_plsql make_live {
+	    select content_item__set_live_revision(:revision_id)
+	}
+    }
+    return $id
+}
+
+#---------------------------------------------------------------------
+ad_proc -public vocab::sentence::delete {
+    -id:required
+} { 
+    This proc deletes a sentence.
+    @return 0 for success
+} {
+    auth::require_login
+    db_exec_plsql note_delete {
+	select content_item__delete(:id)
+    }
+    return 0
+}
Index: openacs-4/contrib/packages/vocabulary/tcl/util-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/tcl/util-procs.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/tcl/util-procs.tcl	23 Feb 2004 23:00:08 -0000	1.1
@@ -0,0 +1,373 @@
+ad_library {
+    
+    @author Joel Aufrecht (joel@aufrecht.org)
+    @creation-date 2004-02-20
+    @cvs-id $Id: util-procs.tcl,v 1.1 2004/02/23 23:00:08 joela Exp $
+    
+}
+
+######################################################################
+# vocab
+######################################################################
+
+namespace eval vocab {}
+
+######################################################################
+ad_proc -public vocab::subnav_prep {
+    -package_id
+} {
+    Build html for package-specific navigation.  This is architecturally dirty,
+    because we should use an adp includelet instead, but this will do for now.
+
+    @author Joel Aufrecht
+    @creation-date 2004-02-09
+    @return html 
+    
+} {
+    # for now, cheat and grab these variables from the ether.  This is bad
+    # because it assumes vocab::conn is always called before this proc
+    # hmm, maybe it's better to call vocab::conn from here?  naw, that
+    # would make all the upvaring confusing
+    upvar label_a  label_a
+    upvar label_b  label_b
+
+    set base_url [apm_package_url_from_key vocabulary]
+    set locale_url [export_vars -base "locale"]
+
+    lappend linklist [list [export_vars -base ${base_url}word-list] "Words"]
+    lappend linklist [list [export_vars -base ${base_url}sentence-list] "Sentences"]
+    lappend linklist [list [export_vars -base ${base_url}test-list] "Tests"]
+    lappend linklist [list [export_vars -base ${base_url}test] "Random Test"]
+    lappend linklist [list [export_vars -base ${base_url}$locale_url] "($label_a"]
+    lappend linklist [list [export_vars -base ${base_url}$locale_url] "--> $label_b)"]
+    if { [permission::permission_p -object_id $package_id -privilege admin]} {
+        lappend linklist { admin/ "Admin"}
+    }
+    lappend linklist [list [export_vars -base ${base_url}doc] "Help"]
+
+    foreach link $linklist {
+        lappend subnav "<a href=\"[lindex $link 0]\">[lindex $link 1]</a>"
+    }
+
+    set subnav [join $subnav " &nbsp;&nbsp;  "]
+
+    return $subnav
+}
+
+######################################################################
+ad_proc -public vocab::conn {
+} {
+    Set some variables that might be in cookies or might be passed in.
+
+    @author Joel Aufrecht
+    @creation-date 2004-02-09
+    @return 0 for success, and upvar some variables.
+} {
+    upvar package_id package_id
+    upvar user_id user_id
+    set user_id [auth::get_user_id]
+    if { ![exists_and_not_null package_id]} {
+        set package_id [ad_conn package_id]
+    }
+
+    upvar locale_a locale_a
+    upvar locale_b locale_b
+    upvar label_a  label_a
+    upvar label_b  label_b
+
+    set locale [lang::user::locale -package_id $package_id ]
+    if { ![exists_and_not_null locale_a] } {
+        set locale_a [ad_get_cookie locale_a $locale]
+    }
+
+    if { ![exists_and_not_null locale_b] } {
+        set locale_b [ad_get_cookie locale_b $locale]
+    }
+
+    # since these are tamper_susceptible cookies, check them for bad data
+    if { [catch {set label_a [ad_locale_get_label $locale_a] } ] } {
+        set locale_a [lang::user::locale]
+        set label_a  [ad_locale_get_label $locale_a ]
+    }
+
+    if { [catch {set label_b [ad_locale_get_label $locale_b] } ] } {
+        set locale_b [lang::user::locale]
+        set label_b  [ad_locale_get_label $locale_b ]
+    }
+
+    return 0
+}
+
+######################################################################
+ad_proc -public vocab::locale_list {
+    {-all}
+} {
+    Get a list of all locales in use
+    TODO: not package_aware
+
+    @author Joel Aufrecht
+    @creation-date 2004-02-02
+    @return list of pairs of label and locale
+} {
+
+    if {[exists_and_not_null all] } {
+        set where_clause ""
+    } else {
+        set where_clause "where al.locale in (select distinct locale 
+                                                from vocab_word
+                                               UNION 
+                                              select distinct locale2
+                                                from vocab_sentence) "
+    }
+    return [db_list_of_lists locales "
+    select al.label,
+           al.locale
+      from ad_locales al
+        $where_clause
+        order by label"]
+}
+
+
+######################################################################
+ad_proc -public vocab::letter_list {
+} {
+    Cheesy proc used for half-assed pagination
+
+    @author Joel Aufrecht
+    @creation-date 2004-02-22
+    @return list of pairs of letters and labels
+} {
+    set result [list]
+    foreach letter {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} {
+        lappend result [list $letter $letter]
+    }
+    return $result
+}
+
+######################################################################
+ad_proc -public vocab::phonetic_alphabet_for_locale {
+    {-locale ""}
+} {
+    This could theoretically be an extra attribute in locales, or even a new table to allow multiple phonetic alphabets per locale, but this is simpler:
+ 
+    @return name of phonetic alphabet for locale
+} {
+    return [switch $locale {
+        "" {format "International Phonetic Alphabet"}
+        da_DK {format "Dania"}
+        en_US {format "none"}
+        ch_ZH {format "Pinyin"}
+        ch_zh {format "Pinyin"}
+        default  {format "Phonetic Alphabet for $locale"}
+    }]
+}    
+
+
+
+ad_proc -public vocab::csv2list {
+    -str 
+    {-sepChar ,}
+} {
+    Split a CSV into a list.
+    http://mini.net/tcl/721.html
+
+    @return A list.
+} {
+    regsub -all {(\A\"|\"\Z)} $str \0 str
+    set str [string map [list $sepChar\"\"\" $sepChar\0\" \
+                              \"\"\"$sepChar \"\0$sepChar \
+                              $sepChar\"\"$sepChar $sepChar$sepChar \
+                              \"\" \" \" \0 ] $str]
+    set end 0
+    while {[regexp -indices -start $end {(\0)[^\0]*(\0)} $str \
+            -> start end]} {
+        set start [lindex $start 0]
+        set end   [lindex $end 0]
+        set range [string range $str $start $end]
+        set first [string first $sepChar $range]
+        if {$first >= 0} {
+            set str [string replace $str $start $end \
+                [string map [list $sepChar \1] $range]]
+        }
+        incr end
+    }
+    set str [string map [list $sepChar \0 \1 $sepChar \0 {} ] $str]
+    return [split $str \0]
+}
+
+######################################################################
+# vocab::locale
+######################################################################
+
+namespace eval vocab::locale {}
+
+ad_proc -public vocab::locale::pinyin_num_to_unicode {
+    -string:required
+} {
+    Replaces pinyin in plain ascii and numbers for tone markers with Unicode.  Inspired by:
+    
+    Pinyin to Unicode Converter
+    Copyright (C) 2002  Konrad Mitchell Lawson
+    http://www.foolsworkshop.com/
+    http://konrad.lawson.net/
+    
+    @return A unicode string.
+} {  
+    ####################################
+    # step 1:
+    # deal with any ending consonants before the tone number
+    # ie, turn cian1 into cia1n
+    # 1 is a preceeding vowel, if any, in a multi-vowel set
+    # 2 should be exactly one vowel (assume the last vowel is always the 
+    #                                one to get the tone)
+    # 3 is anything after the last vowel and before the first number
+    # 4 is exactly 1 number between 1 and 4
+    
+    regsub -all {([aeiou]*)([aeiou]{1}?)(.*)([1-4]{1}?)} $string {\1\2\4\3} newstring
+    #            (1       )(2          )(3 )(4        )
+
+    ####################################
+    # step 2:
+    # convert numeric pinyin to Unicode
+    
+    array set charMap {
+        a1 ā
+        a2 á
+        a3 ǎ
+        a4 à
+        e1 ē
+        e2 é
+        e3 ě
+        e4 è
+        i1 ī
+        i2 í
+        i3 ǐ
+        i4 ì
+        o1 ō
+        o2 ó
+        o3 ǒ
+        o4 ò
+        u1 ū
+        u2 ú
+        u3 ǔ
+        u4 ù
+        ü1 ǖ
+        ü2 ǘ
+        ü3 ǚ
+        ü4 ǜ
+    }
+
+    set string_unicode [string map -nocase [array get charMap] $newstring]
+    return $string_unicode
+}
+
+ad_proc -public  vocab::locale::moby_ascii_to_ipa {
+    -string:required
+} {
+
+    Replaces moby's ascii representation of IPA with unicode.  Depending on how standard moby's ascii ipa is, this could be pretty general. This is the second one of these; maybe I should build a general framework proc.  Oh head, and this is INCOMPLETEly implemented at the moment.  (Thanks emacs for making it nearly impossible to open, edit, and save a simple utf-8 file!)
+    http://www.dcs.shef.ac.uk/research/ilash/Moby/mpron.html
+
+} {
+
+    # should stress markers go before or after the modified letter?
+    # leave it alone for now, but maybe fix it later
+    # there's a good chance that this regsub will fix it:
+    # regsub -all {.*{1}?)([12]{1}?)} $string {\2\1} newstring
+
+    array set charMap {
+        0 {}
+        1 
+        2 
+        & æ  
+    }
+# (@)   "a" in "air"
+# A     "a" in "far"
+# eI    "a" in "day"
+# @     "a" in "ado"
+#         or the glide "e" in "system" (dipthong schwa)
+# -     "ir" glide in "tire"
+#         or the  "dl" glide in "handle"
+#         or the "den" glide in "sodden" (dipthong little schwa)
+# b     "b" in "nab"
+# tS    "ch" in "ouch"
+# d     "d" in "pod"
+# E     "e" in "red"
+# i     "e" in "see"
+# f     "f" in "elf"
+# g     "g" in "fig"
+# h     "h" in "had"
+# hw    "w" in "white"
+# I     "i" in "hid"
+# aI    "i" in "ice"
+# dZ    "g" in "vegetably"
+# k     "c" in "act"
+# l     "l" in "ail"
+# m     "m" in "aim"
+# N     "ng" in "bang"
+# n     "n" in "and"
+# Oi    "oi" in "oil"
+# A     "o" in "bob"
+# AU    "ow" in "how"
+# O     "o" in "dog"
+# oU    "o" in "boat"
+# u     "oo" in "too"
+# U     "oo" in "book"
+# p     "p" in "imp"
+# r     "r" in "ire"
+# S     "sh" in "she"
+# s     "s" in "sip"
+# T     "th" in "bath"
+# D     "th" in "the"
+# t     "t" in "tap"
+# @     "u" in "cup"
+# @r    "u" in "burn"
+# v     "v" in "average"
+# w     "w" in "win"
+# j     "y" in "you"
+
+# Z     "s" in "vision"
+# z     "z" in "zoo"
+
+# Stress or emphasis is marked in the data with the primary "'" or secondary "," marks: "'" (uncurled apostrophe) marks primary stress "," (comma) marks secondary stress. Moby Pronunciator contains many common names and phrases borrowed from other languages; special sounds include (case is significant):
+
+# "A"  "a" in "ami"
+# "N"  "n" in "Francoise"
+# "R"  "r" in "Der"
+# x  "ch" in "Bach"
+# y  "eu" in "cordon bleu"
+# "Y"  "u" in "Dubois"
+
+    set string_unicode [string map -nocase [array get charMap] $newstring]
+    return $string_unicode
+}
+
+
+######################################################################
+# vocab::img
+######################################################################
+
+namespace eval vocab::img {}
+
+
+ad_proc -public vocab::img::checkedbox {
+} {
+    @return HTML for a checked checkbox image
+} {
+    return  {<img src="/shared/images/checkboxchecked" height="13" width="13" border="0" style="background-color: white;">}
+}
+
+ad_proc -public vocab::img::checkbox {
+} {
+    @return HTML for a checkbox image
+} {
+    return {<img src="/shared/images/checkbox" height="13" width="13" border="0" style="background-color: white;">}
+}
+
+ad_proc -public vocab::img::help {
+} {
+    @return HTML for help icon
+} {
+    return {<img src="/shared/images/info.gif" width="12" height="9" alt="[i]" title="Help text" border="0">}
+}
+
Index: openacs-4/contrib/packages/vocabulary/tcl/vocabulary-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/tcl/vocabulary-procs.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/tcl/vocabulary-procs.tcl	23 Feb 2004 23:00:08 -0000	1.1
@@ -0,0 +1,272 @@
+# packages/vocabulary/tcl/vocabulary-procs.tcl
+
+ad_library {
+    
+    @author Joel Aufrecht (joel@aufrecht.org)
+    @creation-date 2004-02-02
+    @cvs-id $Id: vocabulary-procs.tcl,v 1.1 2004/02/23 23:00:08 joela Exp $
+    
+}
+
+#####################################################################
+# vocab
+######################################################################
+
+namespace eval vocab {}
+
+#####################################################################
+# vocab::word
+######################################################################
+
+namespace eval vocab::word {}
+
+#---------------------------------------------------------------------
+ad_proc -public vocab::word::new {
+    -package_id:required
+    -word:required
+    -locale:required
+    -user_id:required
+    {-ipa_phonetic ""}
+    {-local_phonetic ""}
+    {-def_locale ""}
+    {-def_list ""}
+} {
+    Insert a word into the system.  Insert definitions if provided.  Create tests.  If locale is Chinese, pinyin with numbers is changed to unicode.
+
+    @author Joel Aufrecht
+    @creation-date 2004-02-07
+    @return The object id of the new word.
+
+    @param def_list A comma-separated list of definitions that apply to the word.
+    @param def_locale The locale of all of the definitions in def_list.  If def_locale is null, def_list is ignored.
+} {
+
+    if { [string equal [string tolower $locale] "ch_zh"] && [exists_and_not_null local_phonetic] } {
+        set local_phonetic [vocab::locale::pinyin_num_to_unicode -string $local_phonetic]
+    }
+    set id [db_string word_insert "
+        select vocab_word__new(:package_id, :word, :locale, :ipa_phonetic, :local_phonetic, :user_id,null) 
+    "]
+
+    if { [exists_and_not_null def_list] } {
+        if { ![exists_and_not_null def_locale]} {
+            # defs but no def_locale?  Give up.
+            return $id
+        }
+
+        # TODO: either here or in def::new, should check for
+        # existing words and try to match
+        # and try to link to existing words instead of creating def text
+
+        foreach def [split $def_list ,] {
+            vocab::word::def::new \
+                -word_id $id \
+                -locale $def_locale \
+                -definition [string trim $def]
+        }
+
+        vocab::train::test::new \
+            -package_id $package_id \
+            -test_type "ortho_from_ortho" \
+            -test_subject $id \
+            -locale_a $locale \
+            -locale_b $def_locale
+    }
+    return $id
+}
+
+#---------------------------------------------------------------------
+ad_proc -public vocab::word::edit {
+    -id:required
+    -word:required
+    -locale:required
+    -ipa_phonetic
+    -local_phonetic
+} {
+    Edit a word into the system.
+
+    @author Joel Aufrecht
+    @creation-date 2004-02-07
+    @return The object id of the new word.
+
+} {
+    db_dml word_edit {
+        update vocab_word
+           set word   = :word,
+               locale = :locale,
+               ipa_phonetic = :ipa_phonetic,
+               local_phonetic = :local_phonetic
+         where id = :id
+    }
+    return $id
+}
+
+#---------------------------------------------------------------------
+ad_proc -public vocab::word::get {
+    -id:required
+    -array:required
+    {-locale_b ""}
+} {
+    Return word, ipa_phonetic, local_phonetic, and a list of definitions in the specified locale.
+
+    @author Joel Aufrecht
+    @creation-date 2004-02-07
+    @return id for success, -1 for failure.
+
+    @param array The name of an array to return
+} {
+    upvar 1 $array row
+    set got_word [db_0or1row question_select "
+                select vw.word,
+                       vw.locale,
+                       vw.ipa_phonetic,
+                       vw.local_phonetic
+                  from vocab_word vw
+                 where vw.id = :id
+    "  -column_array row]
+
+    if { !$got_word} {
+        return -1
+    }
+
+    if { [exists_and_not_null locale_b] } {
+        set def_list [db_list select_def_list "
+                        select definition 
+                          from vocab_definition 
+                         where word_id = :id
+                           and locale = :locale_b
+                      "]
+    }
+
+    if { [exists_and_not_null def_list] } {
+        array set row [list definitions $def_list]
+    } else {
+        array set row [list definitions ""]
+    }
+    return $id
+}
+
+ad_proc -public vocab::word::delete {
+    -id:required
+} { 
+    This proc deletes a word.
+    @return 0 for success
+} {
+    # TODO: improve security
+    auth::require_login
+    db_transaction {
+        db_dml word_defs_delete "
+            delete 
+              from vocab_definition 
+             where word_id = :id"
+        db_dml word_phonetic_example_delete "
+            delete
+              from vocab_phonetic_example
+             where word_id = :id"
+        db_dml word_word_list_delete "
+            delete
+              from vocab_word_list
+             where word_id = :id"
+
+        db_exec_plsql word_delete "
+	select vocab_word__delete(:id)
+    "
+    }
+    return 0
+}
+
+
+#####################################################################
+# vocab::word::def
+######################################################################
+
+namespace eval vocab::word::def {}
+
+#---------------------------------------------------------------------
+ad_proc -public vocab::word::def::new {
+    -word_id:required
+    -locale:required
+    -definition:required
+} {
+    Add a definition to a word.
+
+    @author Joel Aufrecht
+    @creation-date 2004-02-10
+    @return 0 for success
+    
+} {
+    db_dml definition_insert {
+        insert into vocab_definition
+        (word_id, locale, definition)
+        values (:word_id, :locale, :definition)
+    }
+}
+
+#---------------------------------------------------------------------
+ad_proc -public vocab::word::def::get {
+    -id:required
+    -array:required
+} {
+    Get a definition.
+
+    @author Joel Aufrecht
+    @creation-date 2004-02-10
+    @return 0 for success
+
+    @param array The name of an array to return    
+} {
+    upvar 1 $array row   
+    db_1row definition_select {
+        select vw.word,
+               vd.word_id,
+               vd.sort_order,
+               vd.locale,
+               vd.definition
+          from vocab_definition vd,
+               vocab_word vw
+         where vd.id = :id
+           and vw.id = vd.word_id
+    } -column_array row
+
+    return 0
+}
+
+#---------------------------------------------------------------------
+ad_proc -public vocab::word::def::get_by_word {
+    -word:required
+    -locale:required
+} {
+    Get a definition.
+
+    @author Joel Aufrecht
+    @creation-date 2004-02-10
+    @return A list of definitions for the given word in the given locale.
+
+} {
+    return [db_list get_defs "
+            select vd.definition
+              from vocab_definition vd,
+	           vocab_word vw
+             where vw.word = :word
+               and vw.id = vd.word_id
+               and vd.locale = :locale"]
+}
+
+
+#---------------------------------------------------------------------
+ad_proc -public vocab::word::def::delete {
+    -id:required
+} { 
+    This proc deletes a definition.
+    @return 0 for success
+} {
+    auth::require_login
+    db_dml def_delete "
+        delete
+          from vocab_definition
+         where id = :id"
+    }
+    return 0
+}
+
+
Index: openacs-4/contrib/packages/vocabulary/tcl/vocabulary-train-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/tcl/vocabulary-train-procs.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/tcl/vocabulary-train-procs.tcl	23 Feb 2004 23:00:08 -0000	1.1
@@ -0,0 +1,245 @@
+ad_library {
+    
+    @author Joel Aufrecht (joel@aufrecht.org)
+    @creation-date 2004-02-08
+    @cvs-id $Id: vocabulary-train-procs.tcl,v 1.1 2004/02/23 23:00:08 joela Exp $
+    
+}
+
+#---------------------------------------------------------------------
+# vocab::train
+#---------------------------------------------------------------------
+namespace eval vocab::train {}
+
+#---------------------------------------------------------------------
+ad_proc -public vocab::train::test_type_list {
+} {
+    Get a list of all test types.
+
+    @author Joel Aufrecht
+    @creation-date 2004-02-18
+    @return list of pairs of label and test_type
+} {
+    lappend list [list sentence sentence]
+    lappend list [list sentence_group sentence_group]
+    lappend list [list sentence_from_hint sentence_from_hint]
+    lappend list [list ortho_from_ortho  ortho_from_ortho]
+    return $list
+}
+
+#---------------------------------------------------------------------
+# vocab::train::test
+#---------------------------------------------------------------------
+namespace eval vocab::train::test {}
+
+ad_proc -public vocab::train::test::new {
+    -package_id:required
+    -test_type:required
+    {-test_subject ""}
+    {-question ""}
+    {-answer ""}
+    {-locale_a ""}
+    {-locale_b ""}
+} {
+    Create a new test.  Ste the question and answer values.  TODO: if test_subject is a list, create multiple records in vocab_test_subject.
+
+    @author Joel Aufrecht
+    @creation-date 2004-02-16
+    @return ID of new test, or 0 if there was a problem.
+    
+} {
+    # make sure we have all of the data necessary, and get it if we can
+    switch $test_type {
+        sentence {
+            # this isn't implemented yet
+            return 0
+            vocab::sentence::get -id $test_subject -array sentence_array
+            set row(question) "not implemented yet" ;#TODO: get the locale_b translation of a sentence as question,
+            set row(answer) $sentence_array(text)
+        }
+        sentence_from_hint {
+            if { ![exists_and_not_null question] } {
+                return 0
+            }
+            vocab::sentence::get -id $test_subject -array sentence_array
+            if { ![exists_and_not_null answer] } {
+                set answer $sentence_array(text)
+            }
+        }
+        ortho_from_ortho { 
+            if { ![exists_and_not_null answer] || ![exists_and_not_null question] } {
+                vocab::word::get \
+                    -id $test_subject \
+                    -locale_b $locale_b \
+                    -array word
+                if { ![exists_and_not_null question] } {
+                    if { [exists_and_not_null word(local_phonetic)] } {
+                        set phonetic "/<em>$word(local_phonetic)</em>/"
+                    } elseif { [exists_and_not_null word(ipa_phonetic)] } {
+                        set phonetic "/<em>$word(ipa_phonetic)</em>/"
+                    } else {
+                        set phonetic ""
+                    }
+                    set label [ad_locale_get_label $locale_b]
+                    set question "What does <em>$word(word)</em> $phonetic mean in $label?"
+                }
+                if { ![exists_and_not_null answer] } {
+                    set answer [list $word(definitions)]
+                }
+            }
+        }
+        default  {
+            return 0
+        }
+    }
+    set test_id [db_string get_next_testid "
+                 select nextval('vocab_test_id_seq')
+                 "]
+
+    db_dml insert_test "insert into vocab_test
+    (id, package_id, test_type, test_subject, question, answer, locale_a, locale_b) 
+    values (:test_id, :package_id, :test_type, :test_subject, :question, :answer, :locale_a, :locale_b)"
+    return $test_id
+}
+
+
+ad_proc -public vocab::train::test::get {
+    -test_id:required
+    -array:required
+    -target_locale:required
+} {
+    Return the question and answer for a test, depending on test type.
+    @author Joel Aufrecht
+    @creation-date 2004-02-07
+    @return The test id.
+
+    @param test_id The test's unique ID
+    @param array The name of an array to return question, answer.
+    @param target_locale The locale_b of the answer.
+} {
+    upvar 1 $array row
+
+    db_1row get_test_info "
+                   select test_type,
+                          test_subject,
+                          question,
+                          answer,
+                          locale_a
+                     from vocab_test
+                    where id = :test_id
+                      and locale_b = :target_locale
+    " -column_array row
+    return $test_id
+}
+
+ad_proc -public vocab::train::test::delete {
+    -test_id:required
+} {
+    Return the question and answer for a test, depending on test type.
+    @author Joel Aufrecht
+    @creation-date 2004-02-07
+    @return 0 for success; 1 for failure.
+
+    @param test_id The test's unique ID
+} {
+
+    # TODO: not checking permission here yet; instead just assuming that 
+    # this can only get called by an admin
+    db_transaction {
+        db_dml delete_test_results "
+               delete
+                 from vocab_test_result
+                where test_id = :test_id"
+
+        db_dml delete_test "
+               delete
+                 from vocab_test
+                where id = :test_id"
+    }
+}
+
+#---------------------------------------------------------------------
+# vocab::train::result
+#---------------------------------------------------------------------
+namespace eval vocab::train::result {}
+
+ad_proc -public vocab::train::result::submit {
+    -test_id:required
+    -user_id:required
+    {-date  ""}
+    -answer1:required
+    -array:required
+    -target_locale
+} {
+    Accept a user's answer to a test question, grade it, store and return the results.
+
+    @author Joel Aufrecht
+    @creation-date 2004-02-07
+    @return id of new test result
+    
+    @param test_id The intended test id
+    @param answer1 The user's answer to the question.
+    @param date The date of the answer.  Defaults to now.
+    @param array The name of an array to return quesion, user's answer, correct answer, and score.
+} {
+    if {![exists_and_not_null date]} {
+        set date [clock_to_ansi [clock seconds]]
+    }
+    upvar 1 $array row
+
+    vocab::train::test::get \
+        -test_id $test_id \
+        -array answer_key \
+        -target_locale $target_locale
+
+    # make sure we have everything we need
+    if {  [exists_and_not_null answer_key(answer)] && 
+          ![string equal $answer_key(answer) ""] &&
+          [exists_and_not_null answer_key(test_type)]  } {
+
+        switch $answer_key(test_type) {
+            sentence_from_hint {
+                set correct_answer $answer_key(answer)
+                if { [string equal $correct_answer $answer1] } {
+                    set score 1
+                } else {
+                    set score 0
+                }
+            }
+            ortho_from_ortho { 
+                # see if the answer is in the list
+                set answer [join $answer_key(answer)]
+                # lsearch returns -1 for no match, 0 for match on 1st item
+                if { [lsearch $answer $answer1] > -1 } {
+                    set score 1
+                } else {
+                    set score 0
+                }
+                if { [llength $answer_key(answer)] > 0 } { 
+                    set correct_answer "[join $answer ", "]"
+                } else {
+                    set correct_answer $answer_key(answer)
+                }
+            }
+            default  {
+                format "question missing" 
+            }
+        }
+        db_dml set_answer "
+            insert into vocab_test_result 
+                   (test_id, user_id, date, answer1, result1)
+            values (:test_id, :user_id, :date, :answer1, :score)
+         "
+        array set row [list question $answer_key(question)]
+        array set row [list user_answer $answer1]
+	array set row [list correct_answer $correct_answer]
+        array set row [list score $score]
+        array set row [list date $date]
+        array set row [list test_subject $answer_key(test_subject)]
+    } else {
+        array set row [list question $answer_key(question)]
+        array set row [list user_answer $answer1]
+        array set row [list correct_answer "No correct answer defined."]
+    }
+    return 0
+}
Index: openacs-4/contrib/packages/vocabulary/tcl/test/vocabulary-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/tcl/test/vocabulary-procs.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/tcl/test/vocabulary-procs.tcl	23 Feb 2004 23:00:08 -0000	1.1
@@ -0,0 +1,218 @@
+ad_library {
+    @author Joel Aufrecht (joel@aufrecht.org)
+    @creation-date 2004-02-01
+    @cvs-id $Id: vocabulary-procs.tcl,v 1.1 2004/02/23 23:00:08 joela Exp $
+}
+    
+aa_register_case -cats { smoke api } \
+    -procs {
+        vocab::word::new  
+        vocab::word::get
+        vocab::word::edit
+        vocab::word::delete
+    } \
+ vocabulary__word_basic_tests {
+        Put some sample data in and then retrieve it.
+    } {
+        aa_run_with_teardown \
+            -rollback \
+            -test_code  {   
+    # use auto-test package id to avoid polluting any real instances
+    set package_id [ad_conn package_id]
+    aa_log "package_id is $package_id"
+    set user_id [auth::require_login]
+    
+    #-----------------------------------------------------------------
+    # Generate test data
+    #-----------------------------------------------------------------
+
+    set word "test-wordæ[ad_generate_random_string]"
+    set locale en_US
+    set ipa_phonetic "test-ipa-phoneticø[ad_generate_random_string]"
+    set local_phonetic "test-local-phoneticæ[ad_generate_random_string]"
+    set def_locale da_DK
+    set def_list [list "test-def1[ad_generate_random_string]" "test-def2[ad_generate_random_string]"]
+
+    #-----------------------------------------------------------------
+    # New Record
+    #-----------------------------------------------------------------
+    
+    set new_id [vocab::word::new \
+                    -package_id $package_id \
+                    -word $word \
+                    -locale $locale \
+                    -ipa_phonetic $ipa_phonetic \
+                    -local_phonetic $local_phonetic \
+                    -user_id $user_id \
+                    -def_locale $def_locale \
+                    -def_list $def_list
+               ]
+    aa_true "Creation succeeds" [exists_and_not_null new_id]
+    aa_log "new_id is $new_id"
+
+    #-----------------------------------------------------------------
+    # Retrieve Record
+    #-----------------------------------------------------------------
+
+    vocab::word::get \
+        -id $new_id \
+        -array test_array  -locale_b $def_locale
+    
+    aa_equals "Word match is successful" $test_array(word) $word
+    aa_equals "ipa_phonetic match is successful" $test_array(ipa_phonetic) $ipa_phonetic
+    aa_equals "local_phonetic match is successful" $test_array(local_phonetic) $local_phonetic
+    aa_equals "definition is successful" [join $test_array(definitions)] $def_list
+
+    #-----------------------------------------------------------------
+    # Edit Record
+    #-----------------------------------------------------------------
+
+    set word_edit "test-wordæ[ad_generate_random_string]"
+    set locale_edit en_US
+    set ipa_phonetic_edit "test-ipa-phoneticø[ad_generate_random_string]"
+    set local_phonetic_edit "test-local-phoneticæ[ad_generate_random_string]"
+    
+    set edit_id [vocab::word::edit \
+                     -id $new_id \
+                     -word $word_edit \
+                     -locale $locale_edit \
+                     -ipa_phonetic $ipa_phonetic_edit \
+                     -local_phonetic $local_phonetic_edit]
+
+    aa_equals "Edit succeeds" $edit_id $new_id 
+    # to be pedantic, should also check that neither is null or 0, within the same test
+
+    #-----------------------------------------------------------------
+    # Retrieve Edited
+    #-----------------------------------------------------------------
+
+    vocab::word::get \
+        -id $new_id \
+        -array test_edit_array
+    
+    aa_equals "Word match is successful" $test_edit_array(word) $word_edit
+    aa_equals "ipa_phonetic_edit match is successful" $test_edit_array(ipa_phonetic) $ipa_phonetic_edit
+    aa_equals "local_phonetic_edit match is successful" $test_edit_array(local_phonetic) $local_phonetic_edit
+
+    #-----------------------------------------------------------------
+    # Delete 
+    #-----------------------------------------------------------------
+
+    set delete_id [vocab::word::delete \
+                       -id $new_id]
+
+    aa_equals "Delete succeeds" $delete_id 0
+
+    set after_delete_id [vocab::word::get \
+                             -id $new_id \
+                             -array after_delete_array]
+
+    aa_equals "Cannot retrieve deleted row" $after_delete_id -1
+    aa_false "Retrieved array should be empty" [array exists after_delete_array]
+    }
+}
+
+aa_register_case -cats { smoke api } \
+    -procs {
+        vocab::sentence::new  
+        vocab::sentence::get
+        vocab::sentence::edit
+        vocab::sentence::delete
+    } \
+ vocabulary__sentence_basic_tests {
+        Put some sample data in and then retrieve it.
+    } {
+        aa_run_with_teardown \
+            -rollback \
+            -test_code  {   
+    # use auto-test package id to avoid polluting any real instances
+    set package_id [ad_conn package_id]
+    aa_log "package_id is $package_id"
+    set user_id [auth::require_login]
+    
+    #-----------------------------------------------------------------
+    # Generate test data
+    #-----------------------------------------------------------------
+
+    set text "test sentence [string tolower [ad_generate_random_string]]"
+    set locale en_US
+    set word_list test_word_list
+
+    #-----------------------------------------------------------------
+    # New Record
+    #-----------------------------------------------------------------
+    
+    set new_id [vocab::sentence::new \
+                    -package_id $package_id \
+                    -user_id $user_id \
+                    -text $text \
+                    -locale $locale \
+                    -word_list $word_list]
+
+    aa_true "Creation succeeds" [exists_and_not_null new_id]
+    aa_log "new_id is $new_id"
+
+    #-----------------------------------------------------------------
+    # Retrieve Record
+    #-----------------------------------------------------------------
+
+    vocab::sentence::get \
+        -id $new_id \
+        -array test_array
+    
+    aa_equals "Sentence match is successful" $test_array(text) $text
+    aa_equals "locale match is successful" $test_array(locale) $locale
+    aa_equals "word list is correct" $test_word_list [split $text]
+
+    #-----------------------------------------------------------------
+    # Edit Record
+    #-----------------------------------------------------------------
+
+    set text_edit "test sentenceæ [ad_generate_random_string]"
+    set locale_edit en_US
+    
+    set edit_id [vocab::sentence::edit \
+                     -id $new_id \
+                     -text $text_edit \
+                     -locale $locale_edit]
+
+    aa_equals "Edit succeeds" $edit_id $new_id 
+
+    #-----------------------------------------------------------------
+    # Retrieve Edited
+    #-----------------------------------------------------------------
+
+    vocab::sentence::get \
+        -id $new_id \
+        -array test_edit_array
+    
+    aa_equals "Sentence match is successful" $test_edit_array(text) $text_edit
+    aa_equals "Locale match is successful" $test_edit_array(locale) $locale_edit
+    #-----------------------------------------------------------------
+    # Delete 
+    #-----------------------------------------------------------------
+
+    set delete_id [vocab::sentence::delete \
+                       -id $new_id]
+
+    aa_equals "Delete succeeds" $delete_id 0
+
+    set after_delete_id [vocab::sentence::get \
+                             -id $new_id \
+                             -array after_delete_array]
+
+    aa_equals "Cannot retrieve deleted row" $after_delete_id -1
+    aa_false "Retrieved array should be empty" [array exists after_delete_array]
+    }
+}
+
+aa_register_case vocabulary__put_and_get_sentence {
+    Put some sample data in and then retrieve it.
+} {
+    aa_log "If you are only seeing this test case, it's because you don't have acs-automatic-testing 5.1 or greater and so you can't see the other cases."
+}
+    
+
+
+    
+
Index: openacs-4/contrib/packages/vocabulary/www/definition-delete.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/definition-delete.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/definition-delete.tcl	23 Feb 2004 23:00:08 -0000	1.1
@@ -0,0 +1,19 @@
+ad_page_contract {
+    Delete a definition
+
+    @author Joel Aufrecht
+    @cvs-id $Id: definition-delete.tcl,v 1.1 2004/02/23 23:00:08 joela Exp $
+ 
+    @param id The id of the word to delete
+} {
+    id:integer
+    {return_url "word-list"}
+}
+
+auth::require_login
+
+vocab::word::def::delete \
+    -id $id
+
+ad_returnredirect $return_url
+ad_script_abort
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/www/definition-edit.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/definition-edit.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/definition-edit.adp	23 Feb 2004 23:00:09 -0000	1.1
@@ -0,0 +1,8 @@
+<master src="master">
+  <property name="title">@page_title@</property>
+  <property name="context">@context@</property>
+  <property name="subnavbar_link">@subnavbar_link;noquote@</property>
+   
+<formtemplate id="definition"></formtemplate>
+
+
Index: openacs-4/contrib/packages/vocabulary/www/definition-edit.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/definition-edit.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/definition-edit.tcl	23 Feb 2004 23:00:09 -0000	1.1
@@ -0,0 +1,88 @@
+ad_page_contract {
+    
+    Form for editing a definition.
+    
+    @author Joel Aufrecht (joel@aufrecht.org)
+    @creation-date 2004-02-01
+    @cvs-id $Id: definition-edit.tcl,v 1.1 2004/02/23 23:00:09 joela Exp $
+    
+} {
+    word_id:integer,optional
+    id:integer,optional
+} -validate {
+    some_id_provided {
+        if { ![exists_and_not_null word_id] } {
+            if { [exists_and_not_null id] } {
+                set word_id [db_string get_word_id "select word_id 
+                                                     from vocab_definition
+                                                    where id = :id"]
+            } else {
+                ad_complain "Neither id nor word_id provided."
+            }
+        }
+    }
+}
+
+set page_title "Add a Definition"
+set word_title [db_string word_title_select "select word from vocab_word where id=:word_id"]
+set context [list [list [export_vars -base "word-edit" { {id $word_id} }] $word_title]]
+
+vocab::conn
+set subnavbar_link [vocab::subnav_prep -package_id $package_id]
+
+set locale_options [vocab::locale_list -all all]
+
+set form_mode [ad_decode [ad_form_new_p -key id] 1 "edit" "display"]
+ad_form \
+    -name definition \
+    -mode $form_mode \
+    -form {
+        {word:text(inform)}
+        {id:key}
+        {word_id:integer(hidden)}
+        {definition:text}
+        {locale:text(select)
+            {label Locale}
+            {options $locale_options}
+            {value $locale_b}
+        }
+    } -new_request {
+
+        set page_title "Add a definition"
+        lappend context $page_title
+
+    } -edit_request {
+
+	vocab::word::def::get -id $id -array def_array
+        set word $def_array(word)
+        set definition $def_array(definition)
+        set locale $def_array(locale)
+
+        set page_title "Edit Definition"
+        lappend context $page_title
+
+    } -new_data {
+        
+        auth::require_login
+        vocab::word::def::new \
+            -word_id $word_id \
+            -locale $locale_b \
+            -definition $definition
+        
+    } -edit_data {
+        
+        auth::require_login
+        db_dml ipa_edit {
+            update vocab_definition
+            set word_id = :word_id,
+            locale = :locale_b,
+            definition = :definition
+            where id = :id
+        }
+        
+    } -after_submit {
+        
+        ad_returnredirect [export_vars -base "word-edit" { { id $word_id } }]
+        ad_script_abort
+        
+    }
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/www/index.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/index.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/index.adp	23 Feb 2004 23:00:09 -0000	1.1
@@ -0,0 +1,32 @@
+<master src="master">
+  <property name="title">@title@</property>
+  <property name="context">@context@</property>
+  <property name="subnavbar_link">@subnavbar_link;noquote@</property>
+
+<table>
+<tr>
+<td valign="top"><b>Vocabulary</b> stores words, sentences,
+  and phonetic spellings in any language.  Definitions in any language
+  can be added to any word.  Add words, sentences, or sentence lists
+  to your personal testing list.  Test yourself in flashcard or
+  sentence mode.
+<p>Planned features include: import and export of word lists (leading
+  to full network syncing between vocabulary sites; audio
+  clips and audio testing; printable tests; a framework for asking
+  other people to score your tests and to do the same in exchange.  <a
+  href="doc/">Full TODO list</a>.  My goal is to produce a site that
+  supports an active community of language learners helping each
+  other, and is a public domain vocabulary resource.
+<p>To use the vocabulary, browse the <a href="word-list">word list</a> and
+  add words to your personal list.  Sentences can be added <a
+  href="test-list?test%5ftype=sentence%5fgroup">in groups</a>.  You
+  can <a href="locale">change the language</a> you are being tested
+  in.  Good sources of words can be found in the <a
+  href="doc/resources">Resources Section</a>.  Currently you must
+  mangle word lists into CSV but a dict import filter is pending.
+</td>
+<td align="center" valign="top" width="50%">
+<include src="/packages/vocabulary/lib/locale-count">
+</td>
+</tr>
+</table>
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/www/index.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/index.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/index.tcl	23 Feb 2004 23:00:09 -0000	1.1
@@ -0,0 +1,15 @@
+ad_page_contract {
+    
+    Front page for vocabulary package.
+    
+    @author Joel Aufrecht (joel@aufrecht.org)
+    @creation-date 2004-02-01
+    @cvs-id $Id: index.tcl,v 1.1 2004/02/23 23:00:09 joela Exp $
+    
+} {
+}
+
+set title "Vocabulary"
+set context [list]
+vocab::conn
+set subnavbar_link [vocab::subnav_prep -package_id $package_id]
Index: openacs-4/contrib/packages/vocabulary/www/locale.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/locale.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/locale.adp	23 Feb 2004 23:00:09 -0000	1.1
@@ -0,0 +1,6 @@
+<master src="master">
+  <property name="title">@page_title@</property>
+  <property name="context">@context@</property>
+  <property name="subnavbar_link">@subnavbar_link;noquote@</property>
+
+<formtemplate id="locale"></formtemplate>
Index: openacs-4/contrib/packages/vocabulary/www/locale.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/locale.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/locale.tcl	23 Feb 2004 23:00:09 -0000	1.1
@@ -0,0 +1,58 @@
+ad_page_contract {
+    
+    Front page for vocabulary admin
+    
+    @author Joel Aufrecht (joel@aufrecht.org)
+    @creation-date 2004-02-01
+    @cvs-id $Id: locale.tcl,v 1.1 2004/02/23 23:00:09 joela Exp $
+    
+} {
+    {return_url "."}
+}
+
+set page_title "Language Defaults"
+set context [list $page_title]
+vocab::conn
+set subnavbar_link [vocab::subnav_prep -package_id $package_id]
+
+set locale_options [vocab::locale_list -all all]
+
+ad_form \
+    -name locale \
+    -on_request {
+        set package_locale [lang::user::locale -package_id $package_id]
+    } -form {
+        {locale_a:text(select)
+            {label "Foreign Language"}
+            {options $locale_options}
+            {help_text "Default language for new words and questions."}
+        }
+        {locale_b:text(select)
+            {label "Native Language"}
+            {options $locale_options}
+            {help_text "Default language for new definitions and answers."}
+        }
+        {package_locale:text(select)
+            {label Locale}
+            {options $locale_options}
+            {help_text "Language for program interface."}
+        }
+    } -validate {
+        # just in case
+        { locale_a
+            { [catch ad_locale_get_label $locale_a] }
+            "$locale_a is not a valid locale"
+        }
+        { locale_b
+            { [catch ad_locale_get_label $locale_b] }
+            "$locale_b is not a valid locale"
+        }
+    } -on_submit {
+        # we assume this data has been validated 
+        ad_set_cookie locale_a $locale_a
+        ad_set_cookie locale_b $locale_b
+        lang::user::set_locale -package_id $package_id $package_locale
+    } -after_submit {
+        ad_returnredirect $return_url
+        ad_script_abort
+    }
Index: openacs-4/contrib/packages/vocabulary/www/master.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/master.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/master.adp	23 Feb 2004 23:00:09 -0000	1.1
@@ -0,0 +1,10 @@
+<master>
+  <property name="title">@title;noquote@</property>
+  <property name="context">@context;noquote@</property>
+<div class="subnavbar">@subnavbar_link;noquote@</div>
+<div style="align:center">
+<div style="display: table; margin-top: 0.5em; margin-bottom: 0.5em; margin-left: auto; margin-right: auto">
+
+<slave>
+</div>
+</div>
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/www/phonetic-edit.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/phonetic-edit.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/phonetic-edit.adp	23 Feb 2004 23:00:09 -0000	1.1
@@ -0,0 +1,12 @@
+<master src="master">
+  <property name="title">@page_title@</property>
+  <property name="context">@context@</property>
+  <property name="subnavbar_link">@subnavbar_link;noquote@</property>
+
+<formtemplate id="ipa"></formtemplate>
+
+  <if @id@ not nil>
+    <include src="/packages/vocabulary/lib/phonetic-example-list"
+      phonetic_id="@id@" return_url="@return_url@">
+  </if>
+
Index: openacs-4/contrib/packages/vocabulary/www/phonetic-edit.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/phonetic-edit.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/phonetic-edit.tcl	23 Feb 2004 23:00:09 -0000	1.1
@@ -0,0 +1,86 @@
+ad_page_contract {
+    
+    Form for editing IPA symbols.
+    
+    @author Joel Aufrecht (joel@aufrecht.org)
+    @creation-date 2004-02-01
+    @cvs-id $Id: phonetic-edit.tcl,v 1.1 2004/02/23 23:00:09 joela Exp $
+    
+} {
+    id:integer,optional
+    {return_url ""}
+    {phonetic_locale ""}
+}
+
+vocab::conn
+set subnavbar_link [vocab::subnav_prep -package_id $package_id]
+
+# decide whether or not to do this when not sleepy:
+#set phonetic_locale $locale_b
+set phonetic_name [vocab::phonetic_alphabet_for_locale -locale $phonetic_locale]
+set page_title "Add a $phonetic_name symbol"
+set context $page_title
+set form_mode [ad_decode [ad_form_new_p -key id] 1 "edit" "display"]
+
+ad_form \
+    -name ipa \
+    -mode $form_mode \
+    -form {
+        {return_url:text(hidden),optional
+            {value $return_url}
+        }
+        {phonetic_locale:text(hidden),optional
+            {value $phonetic_locale}
+        }
+        {id:key}
+        {phonetic_symbol:text 
+        {label Symbol}
+        {html {size 1}}
+    }
+} -edit_request {
+
+    db_1row ipa_select {
+        select phonetic_symbol
+          from vocab_phonetic
+         where id = :id
+    }
+    set page_title "$phonetic_symbol"
+    append context $page_title
+
+} -new_data {
+
+    set user_id [auth::require_login]
+    db_dml ipa_insert {
+        insert into vocab_phonetic
+        (phonetic_symbol,locale)
+        values (:phonetic_symbol,:phonetic_locale)
+    }
+
+} -edit_data {
+
+    auth::require_login
+    db_dml ipa_edit {
+        update vocab_phonetic
+           set phonetic_symbol   = :phonetic_symbol,
+        where id = :id
+    }
+
+} -after_submit {
+
+    ad_returnredirect $return_url
+    ad_script_abort
+
+} -validate {
+
+    {phonetic_symbol
+        { [expr [string length $phonetic_symbol] == 1] }
+        "Symbol must be one character"
+    }
+
+}
+
+# fix null ids here instead of in page contract because doing it in page contract screws
+# up ad_form's assumptions
+if { ![exists_and_not_null id] } {
+    set id ""
+}
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/www/phonetic-example-edit.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/phonetic-example-edit.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/phonetic-example-edit.adp	23 Feb 2004 23:00:09 -0000	1.1
@@ -0,0 +1,8 @@
+<master src="master">
+  <property name="title">@page_title@</property>
+  <property name="context">@context@</property>
+  <property name="subnavbar_link">@subnavbar_link;noquote@</property>
+
+<formtemplate id="phonetic_example_edit"></formtemplate>
+
+
Index: openacs-4/contrib/packages/vocabulary/www/phonetic-example-edit.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/phonetic-example-edit.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/phonetic-example-edit.tcl	23 Feb 2004 23:00:09 -0000	1.1
@@ -0,0 +1,98 @@
+ad_page_contract {
+    
+    Form for editing IPA symbol examples.
+    
+    @author Joel Aufrecht (joel@aufrecht.org)
+    @creation-date 2004-02-01
+    @cvs-id $Id: phonetic-example-edit.tcl,v 1.1 2004/02/23 23:00:09 joela Exp $
+    
+} {
+    phonetic_id:integer,optional
+    id:integer,optional
+} -validate {
+    some_id_provided {
+        if { ![exists_and_not_null phonetic_id] } {
+            if { [exists_and_not_null id] } {
+                set phonetic_id [db_string get_phonetic_id "select phonetic_id 
+                                                    from vocab_phonetic_example
+                                                   where id = :id"]
+            } else {
+                ad_complain "Neither id nor phonetic_id provided."
+            }
+        }
+    }
+}
+
+set page_title ""
+set package_id [ad_conn package_id]
+vocab::subnav_prep -package_id $package_id
+
+set phonetic_title [db_string phonetic_title_select "select phonetic_symbol from vocab_phonetic where id=:phonetic_id"]
+
+set context [list [list [export_vars -base "phonetic-edit" { {id $phonetic_id} }] $phonetic_title]]
+
+set word_options [db_list_of_lists words {
+    select (vw.word || ' (' || (select label 
+  		          from ad_locales
+                         where locale = vw.locale) || ')'),
+           vw.id
+      from vocab_word vw
+     where vw.package_id = :package_id
+    order by word
+}]
+
+
+set form_mode [ad_decode [ad_form_new_p -key id] 1 "edit" "display"]
+
+ad_form \
+    -name phonetic_example_edit \
+    -mode $form_mode \
+    -form {
+        {id:key}
+        {phonetic_id:integer(hidden)}
+        {symbol:text(inform) {value $phonetic_title}}
+        {word_id:integer(select)
+            {label Word}
+            {options $word_options}
+        }
+        {comments:text,optional}
+    } -new_request {
+        permission::require_permission -object_id [ad_conn package_id] -privilege create
+        set page_title "Add an example"
+        lappend context $page_title
+    } -edit_request {
+        permission::require_write_permission -object_id $package_id
+        db_1row phonetic_example_select {
+            select word_id,
+               comments
+          from vocab_phonetic_example
+         where id = :id
+    }
+    set page_title "Edit $phonetic_title"
+    lappend context $page_title
+
+} -new_data {
+
+    auth::require_login
+    db_dml phonetic_example_insert {
+        insert into vocab_phonetic_example
+        (phonetic_id, word_id, comments)
+        values (:phonetic_id, :word_id, :comments)
+    }
+
+} -edit_data {
+
+    auth::require_login
+    db_dml phonetic_edit {
+        update vocab_phonetic_example
+           set word_id = :word_id,
+               comments = :comments
+        where id = :id
+    }
+
+} -after_submit {
+
+    ad_returnredirect [export_vars -base "phonetic-example" { {id $phonetic_id} } ]
+    ad_script_abort
+
+}
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/www/sentence-delete.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/sentence-delete.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/sentence-delete.tcl	23 Feb 2004 23:00:09 -0000	1.1
@@ -0,0 +1,18 @@
+ad_page_contract {
+    Delete a sentence
+
+    @author Joel Aufrecht
+    @cvs-id $Id: sentence-delete.tcl,v 1.1 2004/02/23 23:00:09 joela Exp $
+ 
+    @param item_id The item_id of the note to delete
+} {
+    id:integer
+    {return_url "sentence-list"}
+}
+
+auth::require_login
+
+vocab::sentence::delete -id $id
+
+ad_returnredirect $return_url
+abort
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/www/sentence-edit.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/sentence-edit.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/sentence-edit.adp	23 Feb 2004 23:00:09 -0000	1.1
@@ -0,0 +1,6 @@
+<master src="master">
+  <property name="title">@page_title@</property>
+  <property name="context">@context@</property>
+  <property name="subnavbar_link">@subnavbar_link;noquote@</property>
+
+<formtemplate id="sentence"></formtemplate>
Index: openacs-4/contrib/packages/vocabulary/www/sentence-edit.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/sentence-edit.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/sentence-edit.tcl	23 Feb 2004 23:00:09 -0000	1.1
@@ -0,0 +1,89 @@
+ad_page_contract {
+    This is the view-edit page for sentences.
+
+    @author Your Name (you@example.com)
+    @cvs-id $Id: sentence-edit.tcl,v 1.1 2004/02/23 23:00:09 joela Exp $
+} {
+    id:integer,optional
+}
+
+set page_title "Edit"
+set context [list [list sentence-list Sentences]]
+vocab::conn
+set subnavbar_link [vocab::subnav_prep -package_id $package_id]
+
+set locale_options [vocab::locale_list -all all]
+
+ad_form -name sentence -form {
+    {id:key}
+    {locale:text(select)
+        {label Locale}
+        {options $locale_options}
+    }
+    {text:text(textarea)
+        {label Sentence}
+        {html {cols 50}
+            {rows 4}
+        }
+    }
+
+} -new_request {
+    
+    set page_title "Add a Sentence"
+    lappend context $page_title
+    # this pre-sets the form locale
+    set locale $locale_a
+    
+} -edit_request {
+    
+    vocab::sentence::get \
+	-id $id \
+	-array sentence_array 
+    
+    set locale $sentence_array(locale)
+    set text $sentence_array(text)
+    set page_title "Edit a Sentence"
+    lappend context $page_title
+
+} -new_data {
+    
+    set user_id [auth::require_login]
+    set id [vocab::sentence::new \
+                -locale $locale \
+                -package_id $package_id \
+                -user_id $user_id \
+                -text $text \
+                -word_list new_words]
+    set return_url [export_vars -base sentence-words { locale new_words }]
+    
+} -edit_data {
+
+    auth::require_login
+    vocab::sentence::edit \
+	-id $id \
+	-text $text \
+        -locale $locale 
+    set return_url [export_vars -base [ad_conn url] {id}]
+
+} -after_submit {
+
+    ad_returnredirect $return_url
+    ad_script_abort
+}
+
+# this code must lie fallow until 5.1
+# category::ad_form::add_widgets \
+#     -form_name sentence \
+#    -container_object_id [ad_conn package_id] \
+#     -categorized_object_id [value_if_exists item_id]
+
+#     category::map_object \
+#         -remove_old \
+#         -object_id $item_id \
+#        $category_ids
+
+#     category::map_object \
+#         -remove_old \
+#         -object_id $item_id \
+#         $category_ids
+
Index: openacs-4/contrib/packages/vocabulary/www/sentence-group-add-2.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/sentence-group-add-2.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/sentence-group-add-2.tcl	23 Feb 2004 23:00:09 -0000	1.1
@@ -0,0 +1,67 @@
+ad_page_contract {
+    
+    Batch sentence add
+   
+    @author Joel Aufrecht (joel@aufrecht.org)
+    @creation-date 2004-02-01
+    @cvs-id $Id: sentence-group-add-2.tcl,v 1.1 2004/02/23 23:00:09 joela Exp $
+    
+} {
+    locale
+    sentence_group
+}
+
+set page_title "Add a group of Sentences"
+set context $page_title
+vocab::conn
+set subnavbar_link [vocab::subnav_prep -package_id $package_id]
+
+# always create a new test, under the assumption that if sentence_group
+# was null, we wouldn't even be here
+
+set test_id [vocab::train::test::new \
+                 -package_id $package_id \
+                 -test_type "sentence_group" \
+                 -locale_a $locale]
+
+# split up each line
+set total_new_words [list]
+while {[regexp {(.[^\n]+)} $sentence_group match_fodder row] } {
+    # remove each row as it's handled
+    set remove_count [string length $row]
+    set sentence_group [string range $sentence_group [expr $remove_count + 1] end]
+    set row [split $row |]
+    set sentence [string trim [lindex $row 0]]
+    set hint [string trim [lindex $row 1]]
+
+    # make a sentence
+    set sentence_id [vocab::sentence::new \
+                         -user_id $user_id \
+                         -package_id $package_id \
+                         -text $sentence \
+                         -locale $locale \
+                         -word_list new_words]
+
+    append total_new_words $new_words
+    append total_new_words " "
+    # make a test
+    set sentence_test_id [vocab::train::test::new \
+                              -package_id $package_id \
+                              -test_type "sentence_from_hint" \
+                              -test_subject $sentence_id \
+                              -locale_a $locale \
+                              -question $hint ]
+
+    # put the test in the group
+    db_dml link_sentence_to_group "
+              insert 
+                into vocab_test_subject (test_id, subject_id)
+              values (:test_id, :sentence_test_id)"
+
+}    
+
+# now give a chance to add all of these lovely new words
+ad_returnredirect [export_vars -base sentence-words { locale {new_words $total_new_words} }]
+
+#ad_returnredirect [export_vars -base sentence-list {{group $test_id}}]
+abort
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/www/sentence-group-add.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/sentence-group-add.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/sentence-group-add.adp	23 Feb 2004 23:00:09 -0000	1.1
@@ -0,0 +1,6 @@
+<master src="master">
+  <property name="title">@page_title@</property>
+  <property name="context">@context@</property>
+  <property name="subnavbar_link">@subnavbar_link;noquote@</property>
+
+  <formtemplate id="group_sentence_add"></formtemplate>
Index: openacs-4/contrib/packages/vocabulary/www/sentence-group-add.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/sentence-group-add.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/sentence-group-add.tcl	23 Feb 2004 23:00:09 -0000	1.1
@@ -0,0 +1,36 @@
+ad_page_contract {
+    
+    Batch sentence add
+   
+    @author Joel Aufrecht (joel@aufrecht.org)
+    @creation-date 2004-02-01
+    @cvs-id $Id: sentence-group-add.tcl,v 1.1 2004/02/23 23:00:09 joela Exp $
+    
+} {
+}
+
+set page_title "Add a group of Sentences"
+set context $page_title
+
+vocab::conn
+set subnavbar_link [vocab::subnav_prep -package_id $package_id]
+
+set locale_options [vocab::locale_list -all all]
+
+ad_form -name group_sentence_add \
+    -action "sentence-group-add-2" \
+    -form {
+        {
+            locale:text(select)
+	    {label Locale}
+	    {options $locale_options}
+            {value $locale_a}
+            {help_text "The locale of the sentences."}
+	}
+        {
+            sentence_group:text(textarea)
+            {label "List of sentences."}
+            {html { rows 15 cols 60}}
+            {help_text "To create sentence tests with hints, put each hint at the end of the sentence, after a |."}
+        }
+    }
Index: openacs-4/contrib/packages/vocabulary/www/sentence-list.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/sentence-list.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/sentence-list.adp	23 Feb 2004 23:00:09 -0000	1.1
@@ -0,0 +1,6 @@
+<master src="master">
+  <property name="title">@title@</property>
+  <property name="context">@context@</property>
+  <property name="subnavbar_link">@subnavbar_link;noquote@</property>
+
+<include src="/packages/vocabulary/lib/sentence-list">
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/www/sentence-list.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/sentence-list.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/sentence-list.tcl	23 Feb 2004 23:00:09 -0000	1.1
@@ -0,0 +1,18 @@
+ad_page_contract {
+    
+    List of sentences.
+    
+    @author Joel Aufrecht (joel@aufrecht.org)
+    @creation-date 2004-02-01
+    @cvs-id $Id: sentence-list.tcl,v 1.1 2004/02/23 23:00:09 joela Exp $
+    
+} {
+}
+
+set title "Sentences"
+
+set context [list $title]
+vocab::conn
+set subnavbar_link [vocab::subnav_prep -package_id $package_id]
+
+
Index: openacs-4/contrib/packages/vocabulary/www/sentence-train-add.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/sentence-train-add.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/sentence-train-add.tcl	23 Feb 2004 23:00:09 -0000	1.1
@@ -0,0 +1,22 @@
+ad_page_contract {
+    Add a sentence.
+
+    @author Joel Aufrecht
+    @cvs-id $Id: sentence-train-add.tcl,v 1.1 2004/02/23 23:00:09 joela Exp $
+ 
+    @param id The id of the sentence to add
+} {
+    id:integer
+    return_url
+}
+
+set user_id [auth::require_login]
+# Assign this test
+db_dml add_sentence_to_list "
+    insert 
+      into vocab_user_test_map (user_id, test_id, date_added) 
+    values (:user_id, :id, current_timestamp)
+"
+
+ad_returnredirect $return_url
+ad_script_abort
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/www/sentence-train-list.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/sentence-train-list.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/sentence-train-list.adp	23 Feb 2004 23:00:09 -0000	1.1
@@ -0,0 +1,7 @@
+<master src="master">
+  <property name="title">@title@</property>
+  <property name="context">@context@</property>
+  <property name="subnavbar_link">@subnavbar_link;noquote@</property>
+
+  <include src="/packages/vocabulary/lib/sentence-train-list">
+
Index: openacs-4/contrib/packages/vocabulary/www/sentence-train-list.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/sentence-train-list.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/sentence-train-list.tcl	23 Feb 2004 23:00:09 -0000	1.1
@@ -0,0 +1,16 @@
+ad_page_contract {
+    
+    Sentence list
+    
+    @author Joel Aufrecht (joel@aufrecht.org)
+    @creation-date 2004-02-01
+    @cvs-id $Id: sentence-train-list.tcl,v 1.1 2004/02/23 23:00:09 joela Exp $
+    
+} {
+}
+
+set title "Sentence List"
+set context $title
+
+vocab::conn
+set subnavbar_link [vocab::subnav_prep -package_id $package_id]
Index: openacs-4/contrib/packages/vocabulary/www/sentence-words-2.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/sentence-words-2.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/sentence-words-2.tcl	23 Feb 2004 23:00:09 -0000	1.1
@@ -0,0 +1,31 @@
+ad_page_contract {
+    Process incoming definitions.
+} {
+    locale_a
+    locale_b
+    word:array
+    def:array
+}
+
+if { ![info exists package_id] } {
+    set package_id [ad_conn package_id]
+}
+
+set user_id [auth::require_login]
+
+for {set i 1} { $i <= [array size word] } {incr i} {
+     if { [exists_and_not_null word($i)] && [exists_and_not_null def($i)] } {
+
+         vocab::word::new \
+             -package_id $package_id \
+             -word $word($i) \
+             -locale $locale_a \
+             -user_id $user_id \
+             -def_locale $locale_b \
+             -def_list [list $def($i)]
+
+     }
+}
+
+ad_returnredirect "sentence-list"
+abort
Index: openacs-4/contrib/packages/vocabulary/www/sentence-words.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/sentence-words.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/sentence-words.adp	23 Feb 2004 23:00:09 -0000	1.1
@@ -0,0 +1,12 @@
+<master src="master">
+  <property name="title">@page_title@</property>
+  <property name="context">@context@</property>
+  <property name="subnavbar_link">@subnavbar_link;noquote@</property>
+
+<p><form method="post" action="@action_url@">
+<form 
+<listtemplate name="words"></listtemplate>
+<input type="hidden" name="locale_a" value="@locale_a@">Words are in @label_a@.  Definitions are in @locale_b_html;noquote@.  Leave definition blank to skip.
+<br><input type="submit" value="Add Words and Definitions">
+</form>
+
Index: openacs-4/contrib/packages/vocabulary/www/sentence-words.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/sentence-words.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/sentence-words.tcl	23 Feb 2004 23:00:09 -0000	1.1
@@ -0,0 +1,63 @@
+ad_page_contract {
+    Receive a list of words, and show a form to allow definitions to be added.
+} {
+    {new_words ""}
+    {locale ""}
+}
+
+set page_title "Add words from sentences"
+set context [list $page_title]
+set locale_a $locale
+
+vocab::conn
+set subnavbar_link [vocab::subnav_prep -package_id $package_id]
+
+set def_locale $locale_b
+set locale_options [vocab::locale_list -all all]
+
+set locale_b_html [template::widget::menu locale_b $locale_options $locale_b attribute_reference]
+set action_url "sentence-words-2"
+
+if {![exists_and_not_null new_words]} {
+    ad_returnredirect "sentence-list"
+    abort
+}
+
+template::list::create \
+    -name words \
+    -multirow words \
+    -elements {
+        word {
+            label "Word from sentence"
+            display_template {
+                <input type="text" name="word.@words.rownum@" value="@words.word@">
+            }
+        }
+        definition {
+            label "New Definition"
+            display_template {
+                <input type="text" name="def.@words.rownum@" size="15">
+            }
+        }
+         old_definition {
+             label "Existing Definitions in [set label_b]"
+             display_template {
+                 @words.old_definition@
+             }
+         }
+    }
+
+template::multirow create words word old_definition
+
+# TODO: Instead of lsearching used_words, it would be more elegant
+# to just look in the multirow we're creating but I'm not sure 
+# how to do that
+
+set used_words [list]
+foreach word  $new_words {
+    if { [lsearch $used_words $word] < 0 } {
+        template::multirow append words $word [join [vocab::word::def::get_by_word -word $word -locale $def_locale] {, }]
+        lappend used_words $word
+    }
+}
+
Index: openacs-4/contrib/packages/vocabulary/www/test-2.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/test-2.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/test-2.adp	23 Feb 2004 23:00:09 -0000	1.1
@@ -0,0 +1,20 @@
+<master src="master">
+  <property name="title">@page_title@</property>
+  <property name="context">@context@</property>
+  <property name="subnavbar_link">@subnavbar_link;noquote@</property>
+
+<form method="post" action="@action_url@">
+<input type="hidden" name="repeat_url" value="@repeat_url@"/>
+<listtemplate name="test"></listtemplate>
+<if @total_count@ gt @correct_count@>
+<p style="text-align:right;">
+<input type="submit" value="Regrade">
+</if>
+</form>
+<p>
+<h3>@correct_count@ out of @total_count@</h3>
+<div class="form-help-text">
+<%=[vocab::img::help] %> If you think your answer is correct but it
+      isn't on the list, select it click for a regrading.<br>  Your answer
+      will also be added to the list of acceptable definitions.</div>
+
Index: openacs-4/contrib/packages/vocabulary/www/test-2.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/test-2.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/test-2.tcl	23 Feb 2004 23:00:10 -0000	1.1
@@ -0,0 +1,98 @@
+ad_page_contract {
+    Process incoming test and show results.
+} {
+    test_id:integer,array
+    a:array
+    {repeat_url ""}
+}
+
+set page_title "Test"
+set context $page_title
+
+vocab::conn
+set subnavbar_link [vocab::subnav_prep -package_id $package_id]
+
+set action_url "test-3"
+# kludge here because I can't understand the list stuff
+if { [exists_and_not_null repeat_url] } {
+    set actions [list "Test more" [export_vars -base test ] "Test More" "Repeat this test" $repeat_url "Repeat this test"]
+} else {
+    set actions [list "Test more" [export_vars -base test ] "Test More"]
+}
+
+template::list::create \
+    -name test \
+    -multirow test \
+    -actions $actions \
+    -elements {
+        question {
+            label "Question"
+            display_template {
+                @test.question;noquote@
+            }
+        }
+        user_answer {
+            label "Your Answer"
+            display_template {
+                <if @test.score@ eq 0>
+                <span style=\"background: pink; padding:0.2em;\">
+                @test.user_answer@</span>
+                </if>
+                <elseif @test.score@ eq 1>
+                <span style=\"background: lightgreen; padding:0.2em;\">
+                @test.user_answer@</span>
+                </elseif>
+                <else>
+                @test.user_answer@
+                </else>
+            }
+        }
+        answer {
+            link_url_col answer_url
+            label "Acceptable Answers"
+        }
+        score {
+            label "Score"
+        }
+        override {
+            display_template {
+            <if @test.score@ eq 0 and @test.test_subject@ not nil>
+            <input type="checkbox" name="test_id.@test.rownum@" value="@test.test_id@"> 
+            <input type="hidden" name="a.@test.rownum@" value="@test.user_answer@">
+            <input type="hidden" name="date.@test.rownum@" value="@test.date@">
+            </if>
+            }
+        }
+    }
+
+template::multirow create test test_id test_subject question user_answer answer score date answer_url
+
+set correct_count 0
+set total_count 0
+
+# TODO: by getting fancier with the array processing,
+# we lost the array ordering.  would be nice to fix that,
+# maybe by finding the highest i, then walking up to it,
+# assuming a sparse array
+set searchId [array startsearch test_id]
+for {set j 1} { $j <= [array size test_id] } {incr j} {
+    set i [array nextelement test_id $searchId]
+    set answer_url [export_vars -base "test-edit" {{id $test_id($i)}}]
+    if { [exists_and_not_null test_id($i)] && [exists_and_not_null a($i)] } {
+        vocab::train::result::submit \
+            -test_id $test_id($i) \
+            -user_id $user_id \
+            -answer1 $a($i) \
+            -target_locale $locale_b \
+            -array result
+        if { $result(score) } {
+            incr correct_count
+        }
+        incr total_count
+        template::multirow append test $test_id($i) $result(test_subject) $result(question) $result(user_answer) $result(correct_answer) $result(score) $result(date) $answer_url
+    } else {
+        vocab::train::test::get -test_id $test_id($i) -array result -target_locale $locale_b
+        template::multirow append test "" "" $result(question) "-" [join $result(answer)] "-" "" $answer_url
+    }
+}
+array donesearch test_id $searchId
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/www/test-3.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/test-3.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/test-3.tcl	23 Feb 2004 23:00:10 -0000	1.1
@@ -0,0 +1,77 @@
+ad_page_contract {
+    Process incoming test and show results.
+} {
+    test_id:integer,array
+    a:array
+    date:array
+}
+
+set page_title "Test"
+set context $page_title
+auth::require_login
+
+vocab::conn
+set subnavbar_link [vocab::subnav_prep -package_id $package_id]
+
+set action_url "test-2"
+
+# this probably won't work because we're counting 1,2,3 but 
+# array has values of 2, 7, etc.  Either rejigger test-2 to make 
+# linear numbers or get smarter here
+# and since test-2 uses checkboxes and thus we don't know what 
+# we're getting, better get smarter here
+set correct_count 0
+set total_count 0
+set searchId [array startsearch test_id]
+for {set j 1} { $j <= [array size test_id] } {incr j} {
+    set i [array nextelement test_id $searchId]
+    set working_test_id $test_id($i)
+    set working_date $date($i)
+    set working_answer $a($i)
+#    if { $test_id($i) = 0 || [exists_and_not_null a($i)] } {
+#        continue
+#    }
+    # correct an old answer, which means:
+
+    # step 1: erase the old record
+    db_dml delete_old_test "
+                    delete 
+                      from vocab_test_result
+                     where user_id = :user_id
+                       and test_id = :working_test_id
+                       and date = :working_date
+                       and answer1 = :working_answer
+                 "
+    
+    # here we assume that locale_b is unchanged from before
+    # that's probably not a completely safe assumption and we
+    # should pass it
+    vocab::train::test::get \
+        -test_id $working_test_id \
+        -array test_info \
+        -target_locale $locale_b
+    
+    # step 2: add the new definition
+    vocab::word::def::new \
+        -word_id $test_info(test_subject) \
+        -locale $locale_b \
+        -definition $working_answer
+    
+    set new_answer [list [join $test_info(answer)] $a($i)]
+
+    # step 3: add the new answer to the answer key
+    db_dml update_test_answer "
+                update vocab_test
+                   set answer=:new_answer
+                 where id = $working_test_id"
+    
+    # step 4: pass everything back to the test grader
+    # (sure hope it works!)
+    rp_form_put test_id($i) $working_test_id
+    rp_form_put a($i) $working_answer
+    
+}
+array donesearch test_id $searchId
+set internal_path "/packages/[ad_conn package_key]/www/test-2"
+
+rp_internal_redirect $internal_path
Index: openacs-4/contrib/packages/vocabulary/www/test-add.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/test-add.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/test-add.adp	23 Feb 2004 23:00:10 -0000	1.1
@@ -0,0 +1,20 @@
+<master src="master">
+  <property name="title">@title@</property>
+  <property name="context">@context@</property>
+  <property name="subnavbar_link">@subnavbar_link;noquote@</property>
+
+<ul>
+<li>Word tests
+<ul>
+<li><b>b_ortho_from_a_ortho</b>:  Given a written word in one language,
+              write a definition in another language.  These tests are generated
+              automatically when words are added to word lists. 
+</ul>
+<li>Sentence tests
+<ul>
+<li><b>sentence</b>
+<li><b>sentence_from_hint</b>
+</ul>
+
+
+
Index: openacs-4/contrib/packages/vocabulary/www/test-add.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/test-add.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/test-add.tcl	23 Feb 2004 23:00:10 -0000	1.1
@@ -0,0 +1,17 @@
+ad_page_contract {
+    
+    Front page for vocabulary training.
+    
+    @author Joel Aufrecht (joel@aufrecht.org)
+    @creation-date 2004-02-01
+    @cvs-id $Id: test-add.tcl,v 1.1 2004/02/23 23:00:10 joela Exp $
+    
+} {
+}
+
+set title "Create a New Test"
+set context $title
+
+vocab::conn
+set subnavbar_link [vocab::subnav_prep -package_id $package_id]
+
Index: openacs-4/contrib/packages/vocabulary/www/test-count.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/test-count.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/test-count.adp	23 Feb 2004 23:00:10 -0000	1.1
@@ -0,0 +1,6 @@
+<master src="master">
+  <property name="title">@page_title@</property>
+  <property name="context">@context@</property>
+  <property name="subnavbar_link">@subnavbar_link;noquote@</property>
+
+<include src="/packages/vocabulary/lib/test-count">
Index: openacs-4/contrib/packages/vocabulary/www/test-count.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/test-count.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/test-count.tcl	23 Feb 2004 23:00:10 -0000	1.1
@@ -0,0 +1,19 @@
+ad_page_contract {
+    
+    Test list
+    
+    @author Joel Aufrecht (joel@aufrecht.org)
+    @creation-date 2004-02-01
+    @cvs-id $Id: test-count.tcl,v 1.1 2004/02/23 23:00:10 joela Exp $
+    
+} {
+    {test_type ""}
+    {id ""}
+}
+
+set page_title "Test List"
+set context $page_title
+
+vocab::conn
+set subnavbar_link [vocab::subnav_prep -package_id $package_id]
+
Index: openacs-4/contrib/packages/vocabulary/www/test-delete.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/test-delete.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/test-delete.tcl	23 Feb 2004 23:00:10 -0000	1.1
@@ -0,0 +1,19 @@
+ad_page_contract {
+    Delete a test.
+
+    @author Joel Aufrecht
+    @cvs-id $Id: test-delete.tcl,v 1.1 2004/02/23 23:00:10 joela Exp $
+ 
+    @param id The id of the word to delete
+} {
+    id:integer
+    {return_url "test-list"}
+}
+
+auth::require_login
+
+vocab::train::test::delete \
+    -test_id $id     
+
+ad_returnredirect $return_url
+ad_script_abort
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/www/test-edit.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/test-edit.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/test-edit.adp	23 Feb 2004 23:00:10 -0000	1.1
@@ -0,0 +1,25 @@
+<master src="master">
+  <property name="title">@page_title@</property>
+  <property name="context">@context@</property>
+  <property name="subnavbar_link">@subnavbar_link;noquote@</property>
+
+  Direct adding and editing of tests not yet implemented.  Currently,
+  tests are created automatically by adding new words with
+  definitions, or by adding sentence groups, and tests can be deleted
+  from the test-list view.
+
+<ul>
+<li>Word tests
+<ul>
+<li><b>ortho_from_ortho</b>:  Given a written word in one language,
+              write a definition in another language.  These tests are generated
+              automatically when words are added to word lists. 
+</ul>
+<li>Sentence tests
+<ul>
+<li><b>sentence</b>  not implemented yet.  Given a sentence in one
+            language, produce the same sentence in another language.
+<li><b>sentence_from_hint</b>  Given a hint, produce an entire
+            sentence from memory.  
+</ul>
+
Index: openacs-4/contrib/packages/vocabulary/www/test-edit.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/test-edit.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/test-edit.tcl	23 Feb 2004 23:00:10 -0000	1.1
@@ -0,0 +1,138 @@
+ad_page_contract {
+    
+    Form for viewing and editing individual words
+    
+    @author Joel Aufrecht (joel@aufrecht.org)
+    @creation-date 2004-02-01
+    @cvs-id $Id: test-edit.tcl,v 1.1 2004/02/23 23:00:10 joela Exp $
+    
+} {
+    id:integer,optional
+    {return_url ""}
+}
+
+set page_title "Edit a test"
+set context [list [list word-list Words]]
+vocab::conn
+set subnavbar_link [vocab::subnav_prep -package_id $package_id]
+ad_return_template
+
+#---------------------------------------------------------------------
+
+# set form_mode [ad_decode [ad_form_new_p -key id] 1 "edit" "display"]
+# set new_test_url [export_vars -base test-edit]
+
+# ad_form \
+#     -name test \
+#     -mode "display" \
+#     -form {
+# 	{id:key}
+# 	{test:text 
+# 	    {label Test}
+# 	}
+# 	{locale:text(select)
+# 	    {label Locale}
+# 	    {options $locale_options}
+# 	}
+# 	{ipa_phonetic:text,optional
+# 	    {label "IPA"}
+# 	}
+#     }
+
+# if { ![string equal $phonetic_alphabet none]} {
+#     ad_form -extend -name test -form {
+# 	{local_phonetic:text,optional
+# 	    {label "[set phonetic_alphabet]"}
+# 	}
+#     }
+# }
+
+# if { [string equal $form_mode "edit"]} {
+#     ad_form -extend -name test -form {
+# 	{definition:text,optional
+# 	    {label "Definition in [set label_b]"}
+# 	}
+#     }
+# }
+
+# ad_form -extend -name test \
+#      -new_request {
+# 	set page_title "Add a Test"
+# 	lappend context $page_title
+# 	set button_target_ipa test.ipa_phonetic.value
+
+#         # this pre-sets the form locale
+#         set locale $locale_a
+# 	if { [exists_and_not_null phonetic_alphabet]} {
+# 	    set button_target_locale test.local_phonetic.value
+#         }
+#     } -edit_request {
+
+# 	vocab::test::get -id $id -array test_array -locale_b $locale_b
+#         set test $test_array(test)
+#         set locale $test_array(locale)
+#         set ipa_phonetic $test_array(ipa_phonetic)
+#         set local_phonetic $test_array(local_phonetic)
+
+# 	set page_title $test
+# 	lappend context $page_title
+
+#         # this may be redundant with the prefetch
+# 	set locale_a $locale
+# 	set phonetic_alphabet [vocab::phonetic_alphabet_for_locale -locale $locale_a]
+
+#         if { [string equal $form_mode edit] } {
+#             set button_target_ipa test.ipa_phonetic.value
+#             if { [exists_and_not_null phonetic_alphabet]} {
+#                 set button_target_locale test.local_phonetic.value
+#             }
+#         }
+
+# 	if {![exists_and_not_null return_url]} {
+# 	#TODO: new_id is the wrong thing to redirect to, probably thanks
+#         # to cr dumbness, so this breaks
+# 	    set return_url [export_vars -base [ad_conn url] {id}]
+# 	#    set return_url "."
+#         }
+
+#     } -new_data {
+        
+# 	set user_id [auth::require_login]
+# 	set new_id [vocab::test::new \
+#                         -package_id $package_id \
+#                         -test $test \
+#                         -locale $locale \
+#                         -ipa_phonetic $ipa_phonetic \
+#                         -local_phonetic $local_phonetic \
+#                         -user_id $user_id \
+#                         -def_locale $locale_b \
+#                         -def_list $definition
+#                    ]
+
+# 	if {![exists_and_not_null return_url]} {
+# 	#TODO: new_id is the wrong thing to redirect to, probably thanks
+#         # to cr dumbness, so this breaks
+# 	    set return_url [export_vars -base test-edit {{id $new_id}}]
+# 	#    set return_url "."
+#         }
+        
+#         ad_returnredirect $return_url
+# 	ad_script_abort
+        
+#     } -edit_data {
+
+# 	auth::require_login
+# 	vocab::test::edit \
+#             -id $id \
+#             -test $test \
+#             -locale $locale \
+#             -ipa_phonetic $ipa_phonetic \
+#             -local_phonetic $local_phonetic
+
+#     } -after_submit {
+
+# 	ad_returnredirect $return_url
+# 	ad_script_abort
+
+#     }
+
Index: openacs-4/contrib/packages/vocabulary/www/test-list.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/test-list.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/test-list.adp	23 Feb 2004 23:00:10 -0000	1.1
@@ -0,0 +1,6 @@
+<master src="master">
+  <property name="title">@page_title@</property>
+  <property name="context">@context@</property>
+  <property name="subnavbar_link">@subnavbar_link;noquote@</property>
+
+<include src="/packages/vocabulary/lib/test-list">
Index: openacs-4/contrib/packages/vocabulary/www/test-list.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/test-list.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/test-list.tcl	23 Feb 2004 23:00:10 -0000	1.1
@@ -0,0 +1,19 @@
+ad_page_contract {
+    
+    Test list
+    
+    @author Joel Aufrecht (joel@aufrecht.org)
+    @creation-date 2004-02-01
+    @cvs-id $Id: test-list.tcl,v 1.1 2004/02/23 23:00:10 joela Exp $
+    
+} {
+    {test_type ""}
+    {id ""}
+}
+
+set page_title "Test List"
+set context $page_title
+
+vocab::conn
+set subnavbar_link [vocab::subnav_prep -package_id $package_id]
+
Index: openacs-4/contrib/packages/vocabulary/www/test-train-add.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/test-train-add.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/test-train-add.tcl	23 Feb 2004 23:00:10 -0000	1.1
@@ -0,0 +1,23 @@
+ad_page_contract {
+    Add a word and its tests for a user.
+
+    @author Joel Aufrecht
+    @cvs-id $Id: test-train-add.tcl,v 1.1 2004/02/23 23:00:10 joela Exp $
+ 
+    @param id The id of the word to add.
+} {
+    id:integer
+    return_url
+}
+
+set user_id [auth::require_login]
+vocab::conn
+
+db_dml add_word_tests "
+    insert 
+      into vocab_user_test_map (user_id, test_id, date_added) 
+    values (:user_id, :id, current_timestamp)
+"
+
+ad_returnredirect $return_url
+ad_script_abort
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/www/test-train-delete.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/test-train-delete.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/test-train-delete.tcl	23 Feb 2004 23:00:10 -0000	1.1
@@ -0,0 +1,27 @@
+ad_page_contract {
+    Delete a word.
+
+    @author Joel Aufrecht
+    @cvs-id $Id: test-train-delete.tcl,v 1.1 2004/02/23 23:00:10 joela Exp $
+ 
+    @param item_id The item_id of the word to delete
+} {
+    id:integer
+    return_url
+}
+
+set user_id [auth::require_login]
+set package_id [ad_conn package_id]
+
+# TODO: suppress any active tests (but don't delete, or we will
+# have to delete any test results too)
+
+db_dml delete_user_test_mapping "
+    delete 
+      from vocab_user_test_map
+     where user_id = :user_id
+       and test_id = :id
+"
+
+ad_returnredirect $return_url
+ad_script_abort
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/www/test.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/test.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/test.adp	23 Feb 2004 23:00:10 -0000	1.1
@@ -0,0 +1,11 @@
+<master src="master">
+  <property name="title">@page_title@</property>
+  <property name="context">@context@</property>
+  <property name="subnavbar_link">@subnavbar_link;noquote@</property>
+
+<form method="post" action="@action_url@">
+<input type="hidden" name="repeat_url" value="@repeat_url@"/>
+<listtemplate name="test"></listtemplate>
+<div class="form-help-text"><%=[vocab::img::help]%> Leave questions blank to skip.</div>
+<center><p><input type="submit" value="Done"></p></center>
+</form>
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/www/test.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/test.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/test.tcl	23 Feb 2004 23:00:10 -0000	1.1
@@ -0,0 +1,122 @@
+ad_page_contract {
+    Framework page for showing a list of questions and collecting answers.  Handles specified tests as well choosing random questions if necessary.
+} {
+    {size:integer 10}
+    {test_type ""}
+    {test_id ""}
+}
+
+vocab::conn
+set subnavbar_link [vocab::subnav_prep -package_id $package_id]
+
+set page_title "Test"
+set action_url "test-2"
+set context $page_title
+set repeat_url [export_vars -base [ad_conn url] {size test_type test_id}]
+template::list::create \
+    -name test \
+    -multirow test \
+    -elements {
+        question {
+            label ""
+            display_template {
+                @test.question;noquote@
+            }
+        }
+        answer {
+            label ""
+            display_template {
+                <input type="hidden" name="test_id.@test.rownum@" value="@test.test_id@">
+                <input type="text" name="a.@test.rownum@" size="@test.answer_size@">
+            }
+        }
+    }
+
+# figure out what tests to do
+# this is pretty ad_hoc
+# probably a better thing is to do the figuring first, and then use a switch
+# to do the appropriate retrieval
+# or hide it all in a proc
+if { [string equal $test_type "sentence_group"] } {
+    # if we are told ahead of time that it's a sentence group, then it has its own list of tests
+    db_multirow -extend {question answer_size} test test_select "
+        select vts.subject_id as test_id
+          from vocab_test_subject vts
+         where vts.test_id = :test_id
+    " {
+        vocab::train::test::get -test_id $test_id -array test_array
+        set question $test_array(question)
+	set answer_size 40
+    }
+    
+} elseif { [exists_and_not_null test_id] } {
+    # if a specific id is requested, do it
+    db_multirow  -extend {question answer_size} test test_select "
+        select vt.id as test_id
+          from vocab_test vt
+         where vt.id = :test_id
+           and vt.package_id = :package_id
+           and vt.test_type != 'sentence_group'
+    " {
+        vocab::train::test::get -test_id $test_id -array test_array -target_locale $locale_b
+        set question $test_array(question)
+	set answer_size 15
+    }
+
+
+} else {
+    # just get ten random tests
+    # excluding sentence_group is a kludge
+    # if user has no tests, go random.
+
+    set user_has_tests [db_string user_has_tests_select "
+                          select count(*)
+                            from vocab_user_test_map vutm,
+                                 vocab_test vt
+                           where vutm.user_id = :user_id
+                             and vt.id = vutm.test_id
+                             and vt.package_id = :package_id"]
+
+    if { $user_has_tests > 0 } {
+        db_multirow  -extend {question answer_size} test test_select "
+        select vt.id as test_id,
+               random() as random
+          from vocab_user_test_map vutm,
+               vocab_test vt
+         where vutm.user_id = :user_id
+           and vt.id = vutm.test_id
+           and vt.package_id = :package_id
+           and vt.test_type != 'sentence_group'
+         order by random
+         limit $size
+    " {
+        vocab::train::test::get -test_id $test_id -array test_array -target_locale $locale_b
+        set question $test_array(question)
+	set answer_size 10
+    }
+    } else {
+    db_multirow  -extend {question answer_size} test test_select "
+        select vt.id as test_id,
+               vt.question,
+               random() as random
+          from vocab_test vt
+         where vt.package_id = :package_id
+           and vt.test_type != 'sentence_group'
+           and vt.locale_b = :locale_b
+         order by random
+         limit $size
+    " {
+        vocab::train::test::get -test_id $test_id -array test_array -target_locale $locale_b
+        set question $test_array(question)
+	set answer_size 10
+    } if_no_rows {
+        # maybe implement this?  Right now we don't check label_a, because the purpose
+        # is to show somebody a test and if we check locale_a and locale_b then, thanks
+        # to default locale values, we will usually be showing this message
+        set message "There are no tests from $label_a to $label_b.  Try changing your locale."
+    }
+
+
+    }
+    
+}
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/www/vocab.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/vocab.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/vocab.adp	23 Feb 2004 23:00:10 -0000	1.1
@@ -0,0 +1,15 @@
+<master src="master">
+  <property name="title">@title@</property>
+  <property name="context">@context@</property>
+  <property name="subnavbar_link">@subnavbar_link;noquote@</property>
+
+<table cellpadding="5">
+<tr>
+<td>
+<include src="/packages/vocabulary/lib/word-count">
+</td>
+<td>
+<include src="/packages/vocabulary/lib/sentence-count">
+</td>
+</tr>
+</table>
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/www/vocab.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/vocab.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/vocab.tcl	23 Feb 2004 23:00:10 -0000	1.1
@@ -0,0 +1,15 @@
+ad_page_contract {
+    
+    Front page for vocabulary package.
+    
+    @author Joel Aufrecht (joel@aufrecht.org)
+    @creation-date 2004-02-01
+    @cvs-id $Id: vocab.tcl,v 1.1 2004/02/23 23:00:10 joela Exp $
+    
+} {
+}
+
+set title "Words and Sentences"
+set context $title
+vocab::conn
+set subnavbar_link [vocab::subnav_prep -package_id $package_id]
Index: openacs-4/contrib/packages/vocabulary/www/word-delete.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/word-delete.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/word-delete.tcl	23 Feb 2004 23:00:10 -0000	1.1
@@ -0,0 +1,19 @@
+ad_page_contract {
+    Delete a word.
+
+    @author Joel Aufrecht
+    @cvs-id $Id: word-delete.tcl,v 1.1 2004/02/23 23:00:10 joela Exp $
+ 
+    @param id The id of the word to delete
+} {
+    id:integer
+    {return_url "word-list"}
+}
+
+auth::require_login
+
+vocab::word::delete \
+    -id $id
+
+ad_returnredirect $return_url
+ad_script_abort
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/www/word-edit.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/word-edit.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/word-edit.adp	23 Feb 2004 23:00:10 -0000	1.1
@@ -0,0 +1,40 @@
+<master src="master">
+  <property name="title">@page_title@</property>
+  <property name="context">@context@</property>
+  <property name="subnavbar_link">@subnavbar_link;noquote@</property>
+  <if @word@ not nil>
+    <div style="font-size:2em; text-align:center;">@word@</div>
+  </if>
+  <center>
+    <table border=1 cellpadding=5 style="margin: 0.5em; border: gray">
+      <tr>
+        <td valign="top">
+          <formtemplate id="word"></formtemplate>
+          <if @id@ not nil>
+            <p>
+              <include src="/packages/vocabulary/lib/definition-list"
+                word_id="@id@" return_url="@return_url@">
+            </p>
+          </if>
+          @form_help;noquote@
+          <p><a class="button" href="@new_word_url@">Add another word</a>
+        </td>
+        <if @phonetic_alphabet@ ne "none">
+          <td valign="top">
+            <include src="/packages/vocabulary/lib/phonetic-list"
+              phonetic_locale="@locale_a@"
+              return_url="@return_url@"
+              button_target="@button_target_locale@"
+              working_locale="@locale_b@">
+         </td>
+         </if>
+         <td valign="top">
+           <include src="/packages/vocabulary/lib/phonetic-list" 
+             phonetic_locale="" 
+             return_url="@return_url@" 
+             button_target="@button_target_ipa@"
+             working_locale="@locale_b@">
+         </td>
+      </tr>
+    </table>
+  </center>
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/www/word-edit.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/word-edit.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/word-edit.tcl	23 Feb 2004 23:00:10 -0000	1.1
@@ -0,0 +1,174 @@
+ad_page_contract {
+    
+    Form for viewing and editing individual words
+    
+    @author Joel Aufrecht (joel@aufrecht.org)
+    @creation-date 2004-02-01
+    @cvs-id $Id: word-edit.tcl,v 1.1 2004/02/23 23:00:10 joela Exp $
+    
+} {
+    id:integer,optional
+    {return_url ""}
+}
+
+if { ![exists_and_not_null return_url] } {
+    set return_url [export_vars -base "word-edit" {id}]
+}
+
+set page_title "Edit a word"
+set context [list [list word-list Words]]
+vocab::conn
+set subnavbar_link [vocab::subnav_prep -package_id $package_id]
+
+set button_target_ipa ""
+set button_target_locale ""
+set locale_a $locale_a
+
+#---------------------------------------------------------------------
+# Do special things in case we are reloading a submitted form
+#---------------------------------------------------------------------
+# catch missing local_phonetic when submitting from a form
+# where that field was hidden
+if {![exists_and_not_null local_phonetic] } {
+    set local_phonetic ""
+}
+# pre-query for the object's locale, which can override locale_a
+# yes, we're querying the database twice for the same thing
+# that's still faster than hacking ad_form
+if { [exists_and_not_null id]} {
+    set locale_a [db_string get_locale "
+                            select locale
+                              from vocab_word
+                             where id = :id
+" -default ""]
+}
+#---------------------------------------------------------------------
+
+set phonetic_alphabet [vocab::phonetic_alphabet_for_locale -locale $locale_a]
+set locale_options [vocab::locale_list -all all]
+set form_mode [ad_decode [ad_form_new_p -key id] 1 "edit" "display"]
+set new_word_url [export_vars -base word-edit]
+set form_help ""
+
+ad_form \
+    -name word \
+    -mode $form_mode \
+    -form {
+
+	{id:key}
+	{word:text 
+	    {label Word}
+	}
+	{locale:text(select)
+	    {label Locale}
+	    {options $locale_options}
+	}
+	{ipa_phonetic:text,optional
+	    {label "IPA"}
+            {help_text "Phonetic spelling in<br>International Phonetic Alphabet"}
+	}
+    }
+
+if { ![string equal $phonetic_alphabet none]} {
+    ad_form -extend -name word -form {
+	{local_phonetic:text,optional
+	    {label "[set phonetic_alphabet]"}
+            {help_text "Phonetic spelling in [set phonetic_alphabet]"}
+	}
+    }
+}
+
+if { [string equal $form_mode "edit"]} {
+    ad_form -extend -name word -form {
+	{definition:text,optional
+	    {label "Definition in [set label_b]"}
+            {help_text "One or more words,<br>separated by commas"}
+	}
+    }
+}
+
+ad_form -extend -name word \
+    -new_request {
+	set page_title "Add a Word"
+	lappend context $page_title
+	set button_target_ipa word.ipa_phonetic.value
+        
+        # this pre-sets the form locale
+        set locale $locale_a
+	if { [exists_and_not_null phonetic_alphabet]} {
+	    set button_target_locale word.local_phonetic.value
+        }
+
+        set form_help "<div class=\"form-help-text\">[vocab::img::help] If any definitions are included, tests are automatically created.</div>"
+
+         
+    } -edit_request {
+
+	vocab::word::get -id $id -array word_array -locale_b $locale_b
+        set word $word_array(word)
+        set locale $word_array(locale)
+        set ipa_phonetic $word_array(ipa_phonetic)
+        set local_phonetic $word_array(local_phonetic)
+
+	set page_title $word
+	lappend context $page_title
+
+        # this may be redundant with the prefetch
+	set locale_a $locale
+	set phonetic_alphabet [vocab::phonetic_alphabet_for_locale -locale $locale_a]
+
+        if { [string equal $form_mode edit] } {
+            set button_target_ipa word.ipa_phonetic.value
+            if { [exists_and_not_null phonetic_alphabet]} {
+                set button_target_locale word.local_phonetic.value
+            }
+        }
+
+	if {![exists_and_not_null return_url]} {
+	#TODO: new_id is the wrong thing to redirect to, probably thanks
+        # to cr dumbness, so this breaks
+	    set return_url [export_vars -base [ad_conn url] {id}]
+	#    set return_url "."
+        }
+
+    } -new_data {
+        
+	set user_id [auth::require_login]
+	set new_id [vocab::word::new \
+                        -package_id $package_id \
+                        -word $word \
+                        -locale $locale \
+                        -ipa_phonetic $ipa_phonetic \
+                        -local_phonetic $local_phonetic \
+                        -user_id $user_id \
+                        -def_locale $locale_b \
+                        -def_list $definition
+                   ]
+
+	if {![exists_and_not_null return_url]} {
+	#TODO: new_id is the wrong thing to redirect to, probably thanks
+        # to cr dumbness, so this breaks
+	    set return_url [export_vars -base word-edit {{id $new_id}}]
+	#    set return_url "."
+        }
+        
+        ad_returnredirect $return_url
+	ad_script_abort
+        
+    } -edit_data {
+
+	auth::require_login
+	vocab::word::edit \
+            -id $id \
+            -word $word \
+            -locale $locale \
+            -ipa_phonetic $ipa_phonetic \
+            -local_phonetic $local_phonetic
+
+    } -after_submit {
+
+	ad_returnredirect $return_url
+	ad_script_abort
+
+    }
+
Index: openacs-4/contrib/packages/vocabulary/www/word-list-add-2.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/word-list-add-2.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/word-list-add-2.tcl	23 Feb 2004 23:00:10 -0000	1.1
@@ -0,0 +1,42 @@
+ad_page_contract {
+    
+    Batch word add
+   
+    @author Joel Aufrecht (joel@aufrecht.org)
+    @creation-date 2004-02-01
+    @cvs-id $Id: word-list-add-2.tcl,v 1.1 2004/02/23 23:00:10 joela Exp $
+    
+} {
+    {return_url word-list}
+    locale_a:optional
+    locale_b:optional
+    word_list
+}
+
+set page_title "Add a list of words"
+set context $page_title
+vocab::conn
+set subnavbar_link [vocab::subnav_prep -package_id $package_id]
+
+set total_new_words [list]
+
+# split up each line
+while {[regexp {(.[^\n]+)} $word_list match_fodder row_csv] } {
+    # remove each row as it's handled
+    set remove_count [string length $row_csv]
+    set word_list [string range $word_list [expr $remove_count + 1] end]
+    set word_row [string trim [vocab::csv2list -str $row_csv]]
+    # make a word
+    set word_id [vocab::word::new \
+                     -package_id $package_id \
+                     -word [lindex $word_row 0] \
+                     -locale $locale_a \
+                     -user_id $user_id \
+                     -ipa_phonetic [lindex $word_row 2] \
+                     -local_phonetic [lindex $word_row 3] \
+                     -def_locale $locale_b \
+                     -def_list [lindex $word_row 1]]
+} 
+
+ad_returnredirect $return_url
+ad_script_abort
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/www/word-list-add.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/word-list-add.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/word-list-add.adp	23 Feb 2004 23:00:10 -0000	1.1
@@ -0,0 +1,6 @@
+<master src="master">
+  <property name="title">@page_title@</property>
+  <property name="context">@context@</property>
+  <property name="subnavbar_link">@subnavbar_link;noquote@</property>
+
+  <formtemplate id="word_list_add"></formtemplate>
Index: openacs-4/contrib/packages/vocabulary/www/word-list-add.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/word-list-add.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/word-list-add.tcl	23 Feb 2004 23:00:11 -0000	1.1
@@ -0,0 +1,44 @@
+ad_page_contract {
+    
+    Batch word add
+   
+    @author Joel Aufrecht (joel@aufrecht.org)
+    @creation-date 2004-02-01
+    @cvs-id $Id: word-list-add.tcl,v 1.1 2004/02/23 23:00:11 joela Exp $
+    
+} {
+}
+
+set page_title "Add a list of words"
+set context $page_title
+
+vocab::conn
+set subnavbar_link [vocab::subnav_prep -package_id $package_id]
+
+set locale_options [vocab::locale_list -all all]
+set return_url [export_vars -base word-list {{locale $locale_a}}]
+set action [export_vars -base word-list-add-2 {return_url}]
+ad_form -name word_list_add \
+    -action $action \
+    -form {
+        {
+            locale_a:text(select)
+	    {label "Word Locale"}
+	    {options $locale_options}
+            {value $locale_a}
+            {help_text "The locale of the words."}
+	}
+        {
+            locale_b:text(select)
+	    {label "Definition Locale"}
+	    {options $locale_options}
+            {value $locale_b}
+            {help_text "The locale of the definitions."}
+	}
+        {
+            word_list:text(textarea)
+            {label "CSV list of words."}
+            {html { rows 15 cols 60}}
+            {help_text {"word","definition","ipa_phonetic","local_phonetic"<br>Word is required; the rest are optional.}}
+        }
+    }
Index: openacs-4/contrib/packages/vocabulary/www/word-list-csv.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/word-list-csv.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/word-list-csv.tcl	23 Feb 2004 23:00:11 -0000	1.1
@@ -0,0 +1,44 @@
+ad_page_contract {
+    includelet for word list
+} {
+    locale_b:optional
+    locale:optional
+    orderby:optional
+}
+
+vocab::conn
+set subnavbar_link [vocab::subnav_prep -package_id $package_id]
+
+set locale_b_label [ad_locale_get_label $locale_b]
+
+template::list::create \
+    -name words \
+    -multirow words \
+    -elements {
+        word {}
+        locale {}
+        definition {}
+        ipa_phonetic {}
+        local_phonetic {}
+    }
+    
+
+db_multirow \
+    words word_select "
+        select vw.id,
+               vw.word,
+               vw.ipa_phonetic,
+               vw.local_phonetic,
+               vw.locale,
+               (select substring(definition,0,20)
+                  from vocab_definition
+                 where word_id = vw.id
+                   and locale = :locale_b
+                 limit 1) as definition
+          from vocab_word vw
+         where vw.package_id = :package_id
+	[template::list::filter_where_clauses -name words -and]
+	[template::list::orderby_clause -name words -orderby]
+    "
+
+template::list::write_csv -name words
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/www/word-list-export.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/word-list-export.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/word-list-export.tcl	23 Feb 2004 23:00:11 -0000	1.1
@@ -0,0 +1,46 @@
+ad_page_contract {
+    includelet for word list
+} {
+    locale_b:optional
+    locale:optional
+    orderby:optional
+}
+
+set locale_b_label [ad_locale_get_label $locale_b]
+
+template::list::create \
+    -name words \
+    -multirow words \
+    -elements {
+        word {
+            label "Word"
+        }
+        locale {
+	}
+        definition {
+            label "Meaning in [set locale_b_label]"
+        }
+        ipa_phonetic {}
+        local_phonetic
+    }
+    
+
+db_multirow \
+    words word_select "
+        select vw.id,
+               vw.word,
+               vw.ipa_phonetic,
+               vw.local_phonetic,
+               vw.locale,
+               (select substring(definition,0,20)
+                  from vocab_definition
+                 where word_id = vw.id
+                   and locale = :locale_b
+                 limit 1) as definition,
+          from vocab_word vw
+         where vw.package_id = :package_id
+	[template::list::filter_where_clauses -name words -and]
+	[template::list::orderby_clause -name words -orderby]
+    "
+
+template::list::write_csv -name words
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/www/word-list.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/word-list.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/word-list.adp	23 Feb 2004 23:00:11 -0000	1.1
@@ -0,0 +1,7 @@
+<master src="master">
+  <property name="title">@title@</property>
+  <property name="context">@context@</property>
+  <property name="subnavbar_link">@subnavbar_link;noquote@</property>
+
+<include src="/packages/vocabulary/lib/word-list" locale_b="@locale_b@" return_url="@return_url@">
+<p><a href="@word_list_csv_url@">Export</a> all the words currently on this page as a CSV
Index: openacs-4/contrib/packages/vocabulary/www/word-list.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/word-list.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/word-list.tcl	23 Feb 2004 23:00:11 -0000	1.1
@@ -0,0 +1,18 @@
+ad_page_contract {
+    
+    Word list.
+    
+    @author Joel Aufrecht (joel@aufrecht.org)
+    @creation-date 2004-02-01
+    @cvs-id $Id: word-list.tcl,v 1.1 2004/02/23 23:00:11 joela Exp $
+    
+} {
+    locale:optional
+    orderby:optional
+}
+set title "Words"
+set context [list $title]
+vocab::conn
+set subnavbar_link [vocab::subnav_prep -package_id $package_id]
+set return_url "[ad_conn url]?[ad_conn query]"
+set word_list_csv_url [export_vars -base "word-list-csv" {locale orderby}]
Index: openacs-4/contrib/packages/vocabulary/www/word-train-add.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/word-train-add.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/word-train-add.tcl	23 Feb 2004 23:00:11 -0000	1.1
@@ -0,0 +1,36 @@
+ad_page_contract {
+    Add a word and its tests for a user.
+
+    @author Joel Aufrecht
+    @cvs-id $Id: word-train-add.tcl,v 1.1 2004/02/23 23:00:11 joela Exp $
+ 
+    @param id The id of the word to add.
+} {
+    id:integer
+    return_url
+}
+
+set user_id [auth::require_login]
+vocab::conn
+
+# Put the word on the user's list
+db_dml add_word_to_list "
+    insert into vocab_word_list 
+           (user_id, word_id, date_added, package_id, locale) 
+    values (:user_id, :id, current_timestamp, :package_id, :locale_b)
+"
+
+# Assign all locale-appropriate tests for this word to the user
+db_dml add_word_tests "
+    insert 
+      into vocab_user_test_map (user_id, test_id, date_added) 
+    select :user_id,
+           id,
+           current_timestamp
+      from vocab_test
+     where test_subject = :id
+       and locale_b = :locale_b
+"
+
+ad_returnredirect $return_url
+ad_script_abort
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/www/word-train-delete.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/word-train-delete.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/word-train-delete.tcl	23 Feb 2004 23:00:11 -0000	1.1
@@ -0,0 +1,27 @@
+ad_page_contract {
+    Delete a word.
+
+    @author Joel Aufrecht
+    @cvs-id $Id: word-train-delete.tcl,v 1.1 2004/02/23 23:00:11 joela Exp $
+ 
+    @param item_id The item_id of the word to delete
+} {
+    id:integer
+    return_url
+}
+
+set user_id [auth::require_login]
+set package_id [ad_conn package_id]
+db_dml delete_word_from_list "
+    delete 
+      from vocab_word_list
+     where user_id = :user_id
+       and package_id = :package_id
+       and word_id = :id
+"
+
+# TODO: suppress any active tests (but don't delete, or we will
+# have to delete any test results too
+
+ad_returnredirect $return_url
+ad_script_abort
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/www/word-train-list.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/word-train-list.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/word-train-list.adp	23 Feb 2004 23:00:11 -0000	1.1
@@ -0,0 +1,7 @@
+<master src="master">
+  <property name="title">@title@</property>
+  <property name="context">@context@</property>
+  <property name="subnavbar_link">@subnavbar_link;noquote@</property>
+
+  <include src="/packages/vocabulary/lib/word-train-list">
+
Index: openacs-4/contrib/packages/vocabulary/www/word-train-list.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/word-train-list.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/word-train-list.tcl	23 Feb 2004 23:00:11 -0000	1.1
@@ -0,0 +1,15 @@
+ad_page_contract {
+    
+    Word list
+    
+    @author Joel Aufrecht (joel@aufrecht.org)
+    @creation-date 2004-02-01
+    @cvs-id $Id: word-train-list.tcl,v 1.1 2004/02/23 23:00:11 joela Exp $
+    
+} {
+}
+
+set title "Word List"
+set context $title
+vocab::conn
+set subnavbar_link [vocab::subnav_prep -package_id $package_id]
Index: openacs-4/contrib/packages/vocabulary/www/your-test-list.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/your-test-list.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/your-test-list.adp	23 Feb 2004 23:00:11 -0000	1.1
@@ -0,0 +1,6 @@
+<master src="master">
+  <property name="title">@page_title@</property>
+  <property name="context">@context@</property>
+  <property name="subnavbar_link">@subnavbar_link;noquote@</property>
+
+<include src="/packages/vocabulary/lib/test-user-list">
Index: openacs-4/contrib/packages/vocabulary/www/your-test-list.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/your-test-list.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/your-test-list.tcl	23 Feb 2004 23:00:11 -0000	1.1
@@ -0,0 +1,19 @@
+ad_page_contract {
+    
+    Test list
+    
+    @author Joel Aufrecht (joel@aufrecht.org)
+    @creation-date 2004-02-01
+    @cvs-id $Id: your-test-list.tcl,v 1.1 2004/02/23 23:00:11 joela Exp $
+    
+} {
+    {test_type ""}
+    {id ""}
+}
+
+set page_title "Your Tests"
+set context $page_title
+
+vocab::conn
+set subnavbar_link [vocab::subnav_prep -package_id $package_id]
+
Index: openacs-4/contrib/packages/vocabulary/www/admin/index.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/admin/index.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/admin/index.adp	23 Feb 2004 23:00:11 -0000	1.1
@@ -0,0 +1,10 @@
+<master src="/packages/vocabulary/www/master">
+  <property name="title">@title;noquote@</property>
+  <property name="context">@context;noquote@</property>
+  <property name="subnavbar_link">@subnavbar_link;noquote@</property>
+
+<ul class="action-links">
+  <li><a href="@parameters_url@" title="Set parameters" class="action_link">Set parameters</a></li>
+<li><a href="@category_map_url@"
+      class="action_link">Site-Wide Categories</a>
+</ul>
\ No newline at end of file
Index: openacs-4/contrib/packages/vocabulary/www/admin/index.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/admin/index.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/admin/index.tcl	23 Feb 2004 23:00:11 -0000	1.1
@@ -0,0 +1,19 @@
+ad_page_contract {} {
+} -properties {
+    context_bar
+}
+
+set title "Administration"
+set context [list]
+
+vocab::conn
+set subnavbar_link [vocab::subnav_prep -package_id $package_id]
+set admin_p [ad_require_permission $package_id admin]
+
+set parameters_url [export_vars -base "/shared/parameters" {
+  package_id { return_url [ad_return_url] }
+}]
+
+set category_map_url [export_vars -base \
+    "[site_node::get_package_url -package_key categories]cadmin/one-object" \
+        { { object_id $package_id } }]
Index: openacs-4/contrib/packages/vocabulary/www/doc/index.adp
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/doc/index.adp,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/doc/index.adp	23 Feb 2004 23:00:11 -0000	1.1
@@ -0,0 +1,216 @@
+<master src="/packages/vocabulary/www/master">
+  <property name="title">@page_title@</property>
+  <property name="context">@context@</property>
+  <property name="subnavbar_link">@subnavbar_link;noquote@</property>
+
+<p><a href="resources">Resources</a>
+<p><a href="design">Design Notes</a>
+
+<h2>Release Notes</h2>
+<ul>
+<li>This is version 0.10.  It's a "technology preview" because the
+      package is under active development and not fully tested.  The
+      API and data model are reasonably stable but may change.
+<li>I use a native UNICODE postgresql database.  I put the line
+      <code>encoding system utf-8</code> into
+      <code>serverroot/tcl/zz-postload.tcl</code> but I don't know if
+      it made any difference.
+</ul>
+<h2>Feature list</h2>
+Implemented features are <strike>overstruck</strike>.  Current priorities are <strong>in bold face</strong>.
+
+<ol style="color:#666666">
+
+<li>when creating a word, make ortho tests in two directions
+<li>make the list of tests prettier
+</ol>
+
+<ul>
+<li>Bugs:
+<ol>
+<li>Can be given word tests for words without definitions; these are
+          impossible to pass
+<li>Random testing will not test you on sentence groups
+<li>Ordering of test-2 doesn't match test.
+<li><strike>word tests don't respect testing locale</strike>
+<li><strong>Lots of imported pinyin still has numbers</strong>
+<li><strong>lots of imported definitions have curly brackets</strong>
+<li><strike>random testing doesn't do anything interesting if you haven't
+              added anything to your lists; instead it should just
+              grab something, anything.</strike>
+<li><strike>your scores should instead be a list of all tests you either have
+              results in or have put on your list; ie, merge tests and
+                your tests.</strike>
+<li><strike>can't delete definitions; get error on edit</strike>
+<li><strike>Deleting words fails if definitions are present - should cascade</strike>
+</ol>
+<li>Store words
+<ol>
+<li><strike>store words</strike>
+<li><strike>store definitions</strike> - support both links and new
+                    text as definitions
+<li><strike>store phonetic spellings</strike>
+<li>store grammar relationships between words (is the plural of, etc)
+<li>standard category trees (parts of speech, etc)
+<li>Store licensing information for all content (public domain, GPL,
+                            user-only) - via category?
+<li>Store information about frequency of appearance of words
+<li>Some prioritization of words, so that some are shown less
+                                frequently when you get them right a lot
+<li>stroke order (via picture?)
+</ol>
+<li>Store Sentences
+<ol>
+<li><strike>Basic sentence storage</strike>
+<li>categorize sentences
+<li><strike>store sentences in groups</strike> (maybe category is enough?)
+<li>store information about which words are in the sentences
+<li><strike>store groups of sentences</strike> and put a useful label
+                        on the group
+</ol>
+<li>Scoring
+<ol>
+<li>Consistent rules for storing scores - ie, all scores are 0-100, so
+                    getting a word right is 100, getting 3 of 5
+                    sentences is 60.
+<li>When highlighting errors, do forwards and backwards string match
+                                        to isolate the problem area.
+</ol>
+<li>Testing
+<ol>
+<li><strike>handle skipped questions</strike>
+<li>fix detection of empty answer keys in test-2.tcl
+<li>handle a test with no q's
+<li><strike>Fix bad answers (my answer is correct, edit anwser) for
+                                all</strike> for just you
+<li>Show last 5 results and allow filtering out of such words in test
+<li>add other types of tests:
+<ol>
+<li>full sentences
+<li>reverse direction for words (from local def to foreign word)
+<li>phonetic-based
+<li>sound-based tests
+</ol>
+<li>Better determination of what to test - frequency ranking of tests,
+                                or lesson-based testing (from categories?)
+<li>"you've gotten this right 5 times in a row; only show it rarely in
+                                        the future? y/n
+<li><strike>Use a t/f field in tests so that they can be taken off the list
+without losing test results - mooted</strike>
+</ol>
+<li>Data
+<ol>
+<li>input the full IPA and example words in English, other languages
+                          (54 IPA characters now included in install)
+<li>Input Dania to the live data, including example words
+<li><strike>Input the first Chinese words</strike> - first 3000 or 6000
+<li><strong>input some words and definitions from a non-english
+                                  language</strong>
+<li>Input some chinese 2-character words
+<li>Input my Danish lessons (copyright issues - need to restrict access)
+</ol>
+<li>Import/Export capability
+<ol>
+<li>CSV export of <strike>words</strike>, sentences, phonetic alphabets
+<li>Import a CSV file
+<li>CSV import of <strike>words</strike>, sentences, phonetic
+                alphabets
+<li>export to file - ie, handle mime type correctly
+<li>find and implement standards.  IMS?  XML?  <strong>Dict</strong>!  Moby.
+<li>Import/export of tests?  SCORM?
+<li>automatic lookup of external web services
+<li>Live outgoing feed, RSS etc
+<li>Framework for syncronizing new data
+<ol>
+<li>automatic matching on words instead of creating new words
+<li>confirmation/conflict resolution pages
+</ol>
+</ol>
+<li><strike>Auto-generate examples by looking into phonetic
+                  spelling</strike> okay, now do two examples
+<li>Store sounds
+<ol>
+<li>pronunciations of words
+<li>pronunciations of sentences
+<li>provide simple online editing tools (trim .2 sec from front or
+                                                                      back, etc) so that people can improve their own uploaded samples to match the others
+<li>audio tests: see a word, upload your pronunciation, grading by 1)
+                                                                        grading yourself after hearing your upload sandwiched between native speaker examples 2) asking for someone to grade you. 
+<li>round-trip audio test: you are given a text.  You record yourself
+                                                                          and upload it.  Someone else transcribes your recording and you can compare that to the original text.
+</ol>
+<li>Printable test pages (flashcard mode; test mode)
+<li>Utilities
+<ol>
+<li>Show tooltip/link for every foreign word on the page
+<li>"another useful thing: a bookmarklet: so that while I'm reading a
+chinese page and I run into an unknown word I select it and hit the
+bookmarklet and I get both the dictionary entry plus the word gets
+added to my list. that way I can be sure that whenever I look up a
+word it will also get added to my list"
+<li>scan a specified text and determine how many of the words I
+already know
+</ol>
+<li><strike>Store and allow changing current working locale</strike>
+<li><strike>Add a wizard for directly adding word+def to word list</strike>
+<li>Navigation & Interface
+<ol>
+<li><strike>Use subnavbar to show locale settings and subglobal
+            nav</strike>
+<li><strike>write a flow map for the whole thing</strike>
+<li>Do the UI improvements from my paper notes
+<li><strike>Put "Add another word" link on word-edit</strike>
+<li>Consolidate Words, Sentences, Locales into hierarchy, with Locale
+                  list, then per-locale page, then sentences & words
+
+<li><strike>prettier formatting for the white pseudo-button</strike>
+</ol>
+<li>Coding Hygiene & Platform
+<ol>
+<li><strike>set and retain the working locales</strike>
+<li><strike>move everything into procs</strike>
+
+<li>Add UI for controlling sort order
+<li><strike>write a data dictionary/diagram</strike>
+<li><strike>Merge vocabulary and vocabulary-test</strike>
+<li><strike>redo subnav prep as vocab::conn</strike>
+<li><strike>implement the flow map in the new subnavbar</strike>
+<li><strike>Think hard about data model for tests and sentences: put
+                            sentence hint in test, not sentence?
+                            YES. should tests have user_ids?  NO.</strike>
+<li><strike>Use subnavbar to show locale settings and subglobal
+nav</strike>
+<li>Put docs into Docbook
+<li>use pretty urls
+<li>Write test cases for all of API
+<li>Paginate all the lists
+<li>Eliminate all of the group count views in favor of paginated lists
+that show group count in filter blocks
+<li>move all data into content repository
+<li>Performance Tuning
+<ol>
+<li>cache the phonetic chart
+</ol>
+</ol>
+</ul>
+<h2>Refactoring list</h2>
+<ul>
+<li>Make all hyperlinks (export_urls) calculate base url with
+package_awareness
+<li>Enable sort/filter of multiple includes on the same page
+<li><strike>Make vocabulary-trainer use the same subnav structure as vocab</strike>
+<li>Move locale information into real table (extension of ad_locales,
+with phonetic alphabet name)
+<li>Implement permissions
+<ol>
+<li>Set up at least two roles, student and editor
+<li>Enforce roles in all procs and in UI
+<li><strike>capture a definition during word creation</strike>
+<li><strike>Revise front page into link/portlet thingie</strike>
+
+
+<li><strike>provide word data via API call</strike>
+<li><strike>Don't show button in view mode of word-edit</strike>
+</ol>
+
+</ul>
Index: openacs-4/contrib/packages/vocabulary/www/doc/index.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/doc/index.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/contrib/packages/vocabulary/www/doc/index.tcl	23 Feb 2004 23:00:11 -0000	1.1
@@ -0,0 +1,15 @@
+ad_page_contract {
+    
+    Front page for vocabulary docs
+    
+    @author Joel Aufrecht (joel@aufrecht.org)
+    @creation-date 2004-02-01
+    @cvs-id $Id: index.tcl,v 1.1 2004/02/23 23:00:11 joela Exp $
+    
+} {
+}
+
+set page_title "Documentation"
+set context [list $page_title]
+vocab::conn
+set subnavbar_link [vocab::subnav_prep -package_id $package_id]
Index: openacs-4/contrib/packages/vocabulary/www/doc/vocab-data-model.dia
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/doc/vocab-data-model.dia,v
diff -u
Binary files differ
Index: openacs-4/contrib/packages/vocabulary/www/doc/vocab-data-model.png
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/doc/vocab-data-model.png,v
diff -u
Binary files differ
Index: openacs-4/contrib/packages/vocabulary/www/doc/vocab-flow-map.dia
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/doc/vocab-flow-map.dia,v
diff -u
Binary files differ
Index: openacs-4/contrib/packages/vocabulary/www/doc/vocab-flow-map.png
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/vocabulary/www/doc/vocab-flow-map.png,v
diff -u
Binary files differ