whimsical-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ru...@apache.org
Subject [whimsy] branch master updated: move secmail to workbench
Date Fri, 21 Apr 2017 21:20:58 GMT
This is an automated email from the ASF dual-hosted git repository.

rubys pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/whimsy.git

The following commit(s) were added to refs/heads/master by this push:
       new  84aaa54   move secmail to workbench
84aaa54 is described below

commit 84aaa54ec98334ab5f80a5c3c3edb55957d1ec02
Author: Sam Ruby <rubys@intertwingly.net>
AuthorDate: Fri Apr 21 17:20:47 2017 -0400

    move secmail to workbench
---
 www/index.html                                     |    1 -
 www/secmail/Gemfile                                |   27 -
 www/secmail/README                                 |   85 --
 www/secmail/public/spinner.gif                     |  Bin 673 -> 0 bytes
 www/{secmail => secretary/workbench}/.gitignore    |    0
 www/secretary/workbench/Gemfile                    |   22 +-
 www/secretary/workbench/HOWTO.html                 |  179 ---
 www/secretary/workbench/README                     |   96 +-
 www/{secmail => secretary/workbench}/Rakefile      |    0
 www/secretary/workbench/ccla.erb                   |   13 -
 www/{secmail => secretary/workbench}/config.rb     |    0
 www/{secmail => secretary/workbench}/config.ru     |    0
 www/{secmail => secretary/workbench}/deliver.rb    |    0
 www/secretary/workbench/file.cgi                   | 1138 --------------------
 www/secretary/workbench/grant.erb                  |   13 -
 www/{secmail => secretary/workbench}/helpers.rb    |    0
 www/secretary/workbench/icla.erb                   |   19 -
 www/secretary/workbench/incomplete.erb             |   16 -
 www/secretary/workbench/index.html                 |   10 -
 www/secretary/workbench/jquery-1.7.2.min.js        |    4 -
 www/secretary/workbench/jquery.timeago.js          |  152 ---
 www/secretary/workbench/local_paths.rb             |   40 -
 www/secretary/workbench/local_paths.yml            |    6 -
 www/secretary/workbench/mem.erb                    |   15 -
 .../workbench}/models/attachment.rb                |    0
 .../workbench}/models/events.rb                    |    0
 .../workbench}/models/mailbox.rb                   |    0
 .../workbench}/models/message.rb                   |    0
 .../workbench}/models/safetemp.rb                  |    0
 www/secretary/workbench/nda.erb                    |   13 -
 www/{secmail => secretary/workbench}/parsemail.rb  |    0
 .../workbench}/personalize.rb                      |    0
 .../workbench}/public/HOWTO.html                   |    7 +-
 .../workbench}/public/assets/bootstrap-min.css     |    0
 .../workbench}/public/assets/bootstrap-min.js      |    0
 .../workbench}/public/assets/jquery-min.js         |    0
 .../workbench}/public/assets/react-min.js          |    0
 .../workbench}/public/fetch.js                     |    0
 .../workbench}/public/secmail.css                  |    0
 www/secretary/workbench/{ => public}/spinner.gif   |  Bin
 .../workbench}/public/tasklist.js                  |    0
 www/secretary/workbench/publickey.erb              |   16 -
 www/secretary/workbench/secmail.rb                 |   52 -
 www/{secmail => secretary/workbench}/server.rb     |    0
 www/{secmail => secretary/workbench}/tasks.rb      |    0
 .../workbench}/templates/acreq.erb                 |    0
 .../workbench}/templates/ccla.erb                  |    0
 .../workbench}/templates/grant.erb                 |    0
 .../templates/icla-account-requested.erb           |    0
 .../workbench}/templates/icla-pmc-notified.erb     |    0
 .../workbench}/templates/icla.erb                  |    0
 .../workbench}/templates/incomplete.erb            |    0
 .../workbench}/templates/mem.erb                   |    0
 .../workbench}/templates/nda.erb                   |    0
 .../workbench}/templates/pubkey.erb                |    0
 .../workbench}/templates/unsigned.erb              |    0
 .../workbench}/tmp/.gitignore                      |    0
 www/secretary/workbench/unsigned.erb               |   15 -
 www/secretary/workbench/upload.cgi                 |   48 -
 .../workbench}/views/actions/burst.json.rb         |    0
 .../workbench}/views/actions/ccla.json.rb          |    0
 .../workbench}/views/actions/check-id.json.rb      |    0
 .../workbench}/views/actions/check-mail.json.rb    |    0
 .../views/actions/check-signature.json.rb          |    0
 .../views/actions/delete-attachment.json.rb        |    0
 .../workbench}/views/actions/drop.json.rb          |    0
 .../workbench}/views/actions/forward.json.rb       |    0
 .../workbench}/views/actions/grant.json.rb         |    0
 .../workbench}/views/actions/icla.json.rb          |    0
 .../workbench}/views/actions/incomplete.json.rb    |    0
 .../workbench}/views/actions/memapp.json.rb        |    0
 .../workbench}/views/actions/pdfize.json.rb        |    0
 .../workbench}/views/actions/pubkey.json.rb        |    0
 .../views/actions/rotate-attachment.json.rb        |    0
 .../workbench}/views/actions/unsigned.json.rb      |    0
 .../workbench}/views/actions/update-mail.json.rb   |    0
 .../workbench}/views/app.js.rb                     |    0
 .../workbench}/views/asciize.js.rb                 |    0
 .../workbench}/views/body.html.rb                  |    0
 .../workbench}/views/check-signature.js.rb         |    0
 .../workbench}/views/context-menu.js.rb            |    0
 .../workbench}/views/danger.html.rb                |    0
 .../workbench}/views/forms/ccla.js.rb              |    0
 .../workbench}/views/forms/forward.js.rb           |    0
 .../workbench}/views/forms/grant.js.rb             |    0
 .../workbench}/views/forms/icla.js.rb              |    0
 .../workbench}/views/forms/memapp.js.rb            |    0
 .../workbench}/views/forms/nda.js.rb               |    0
 .../workbench}/views/headers.html.rb               |    0
 .../workbench}/views/http.js.rb                    |    0
 .../workbench}/views/index.html.rb                 |    0
 .../workbench}/views/index.js.rb                   |    0
 .../workbench}/views/index.json.rb                 |    0
 .../workbench}/views/memapp.json.rb                |    0
 .../workbench}/views/message.html.rb               |    0
 .../workbench}/views/parts.html.rb                 |    0
 .../workbench}/views/parts.js.rb                   |    0
 .../workbench}/views/status.js.rb                  |    0
 .../workbench}/views/tasklist.html.rb              |    0
 www/secretary/workbench/worklist.cgi               |  658 -----------
 www/secretary/workbench/worklist.css               |   23 -
 www/secretary/workbench/worklist.js                |  228 ----
 102 files changed, 98 insertions(+), 2798 deletions(-)

diff --git a/www/index.html b/www/index.html
index 9591b66..7f1e888 100644
--- a/www/index.html
+++ b/www/index.html
@@ -189,7 +189,6 @@
                 <li><a href="secretary/icla-lint">Lint test for iclas.txt</a></li>
                 <li><a href="secretary/public-names">Public names: LDAP vs icla.txt</a></li>
                 <li><a href="secretary/response-time">Response time test</a></li>
-                <li><a href="secmail/">Secretary Mail</a> (experimental)</li>
               </ul>
             </div>
           </div>
diff --git a/www/secmail/Gemfile b/www/secmail/Gemfile
deleted file mode 100644
index ec4b3eb..0000000
--- a/www/secmail/Gemfile
+++ /dev/null
@@ -1,27 +0,0 @@
-source 'https://rubygems.org'
-
-root = '../../..'
-version_file = File.expand_path("#{root}/asf.version", __FILE__)
-if File.exist? version_file
-  # for deployment and local testing
-  asf_version = File.read(version_file).chomp
-  gem 'whimsy-asf', asf_version, path: File.expand_path(root, __FILE__)
-else
-  # for docker purposes (atleast for now)
-  gem 'whimsy-asf'
-end
-
-gem 'mail'
-gem 'rake'
-gem 'zip'
-gem 'sinatra', '~> 1.4'
-gem 'sanitize'
-gem 'wunderbar', '~> 1.0.27'
-gem 'ruby2js', '~> 2.0.12'
-gem 'execjs'
-gem 'listen', ('~> 3.0.7' if RUBY_VERSION =~ /^2\.[01]/)
-gem 'escape'
-
-group :demo do
-  gem 'puma'
-end
diff --git a/www/secmail/README b/www/secmail/README
deleted file mode 100644
index 14ec74e..0000000
--- a/www/secmail/README
+++ /dev/null
@@ -1,85 +0,0 @@
-This directory contains a script that fetches and parsed secretary emails
-and a server that will enable exploration of those emails.
-
-
-Usage:
-
-  rake fetch
-  rake server
-
-Notes:
-
-  First fetch and parse will take approximately an hour, even with a relatively
-  fast machine and internet connection.  Subsequent fetches will take as
-  little as ten seconds or less.  (For the impatient: replace 'fetch' with
-  'fetch1' and you will only get the latest month.  At some later point,
-  running 'rake fetch' will fetch the remaining months as well as any new
-  emails that have arrived in the current month).
-
-  Secretary email archive currently requires about approximately 11 Gigabytes.
-
-  Some functions will require installations of gpg, imageMagick and pdftk.
-
-  OS X El Capitan users may want to look at:
-    http://stackoverflow.com/a/33248310
-
-Overview of files:
-
-  Gemfile:            Ruby configuration (installation of gems)
-  Rakefile:           Command line configuration (like 'make')
-  config.rb:          Customizations
-  config.ru:          Rack (webserver) configuration
-  mailbox.rb:         Encapsulate interface to wb server
-  officers-secretary: local copy of mailboxes and indexes (in YAML format)
-  parsemail.rb:       Fetch and parse emails
-  server.rb:          Web interface to emails
-  views:              HTML templates (in Wunderbar format)
-
-Overview of control flow:
-
-  server.rb:          Matches HTTP requests to methods and paths, and runs
-                      the associated code.  This code will either return a
-                      result directly (rare) or invoke a view using a method
-                      name starting with an underscore.  For more
-                      information, see:
-
-                      http://www.sinatrarb.com/documentation.html
-                      https://github.com/rubys/wunderbar/#readme
-
-  views:              Files in the views directory have two extensions, the
-                      first identifies the target type (html, json, js), the
-                      second indicates the language of the view (rb).
-
-    html views:       Method names that start with an underscore generate HTML.
-                      This HTML may pull in scripts, stylesheets, and have
-                      inline code that renders other views.  Views in the
-                      actions subdirectory produce responses to HTTP post
-                      requests.
-
-    js views:         This code is converted from Ruby to JavaScript.
-                      This conversion is aware of React.js and will perform
-                      additional React.js mappings when it encounters classes
-                      that derive from "React".  See
-
-                      https://facebook.github.io/react/docs/component-specs.html
-                      https://github.com/rubys/ruby2js#readme
-
-                      Note: in this application app.js.rb pulls in all of the
-                      other javascript files and returns the result as a
-                      single file.
-
-    json views:       The last statement identifies an object (typically a
-                      hash or array) that will be converted to JSON and sent
-                      back as a response.
-
-Model:
-
-  mailbox.rb:         read-only interface to a mailbox files found in
-                      officers-secretary as well as read/write interface
-                      to accompanying YAML file.
-
-  message.rb:         representation of an individual message, backed by
-                      both the mbox file and the YAML file.
-
-  attachment.rb:      representation of an individual attachment, backed by
-                      both the mbox file and the YAML file.
diff --git a/www/secmail/public/spinner.gif b/www/secmail/public/spinner.gif
deleted file mode 100644
index d0bce15..0000000
Binary files a/www/secmail/public/spinner.gif and /dev/null differ
diff --git a/www/secmail/.gitignore b/www/secretary/workbench/.gitignore
similarity index 100%
rename from www/secmail/.gitignore
rename to www/secretary/workbench/.gitignore
diff --git a/www/secretary/workbench/Gemfile b/www/secretary/workbench/Gemfile
index 2924c4b..ec4b3eb 100644
--- a/www/secretary/workbench/Gemfile
+++ b/www/secretary/workbench/Gemfile
@@ -1,15 +1,27 @@
 source 'https://rubygems.org'
 
-root = '../../../..'
+root = '../../..'
 version_file = File.expand_path("#{root}/asf.version", __FILE__)
 if File.exist? version_file
   # for deployment and local testing
   asf_version = File.read(version_file).chomp
   gem 'whimsy-asf', asf_version, path: File.expand_path(root, __FILE__)
+else
+  # for docker purposes (atleast for now)
+  gem 'whimsy-asf'
 end
 
-gem 'mime-types', ('~> 2.99' if RUBY_VERSION < '2')
-
-gem 'wunderbar'
-gem 'escape'
 gem 'mail'
+gem 'rake'
+gem 'zip'
+gem 'sinatra', '~> 1.4'
+gem 'sanitize'
+gem 'wunderbar', '~> 1.0.27'
+gem 'ruby2js', '~> 2.0.12'
+gem 'execjs'
+gem 'listen', ('~> 3.0.7' if RUBY_VERSION =~ /^2\.[01]/)
+gem 'escape'
+
+group :demo do
+  gem 'puma'
+end
diff --git a/www/secretary/workbench/HOWTO.html b/www/secretary/workbench/HOWTO.html
deleted file mode 100644
index 8c1304e..0000000
--- a/www/secretary/workbench/HOWTO.html
+++ /dev/null
@@ -1,179 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-<HTML>
-<HEAD>
-	<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=utf-8">
-	<TITLE></TITLE>
-	<META NAME="GENERATOR" CONTENT="OpenOffice.org 3.4.1  (Unix)">
-	<META NAME="AUTHOR" CONTENT="Craig Russell">
-	<META NAME="CREATED" CONTENT="20110814;13503500">
-	<META NAME="CHANGEDBY" CONTENT="Craig Russell">
-	<META NAME="CHANGED" CONTENT="20130406;14553000">
-	<META NAME="CHANGEDBY" CONTENT="Craig Russell">
-	<META NAME="CHANGEDBY" CONTENT="Craig Russell">
-	<STYLE TYPE="text/css">
-	<!--
-		@page { margin: 0.79in }
-		P { margin-bottom: 0.08in }
-		A:link { so-language: zxx }
-	-->
-	</STYLE>
-</HEAD>
-<BODY LANG="en-US" DIR="LTR">
-<P STYLE="margin-bottom: 0in"><FONT FACE="Times New Roman, serif"><FONT SIZE=3>This
-tool helps file documents received via fax or email.</FONT></FONT></P>
-<P STYLE="margin-bottom: 0in"><BR>
-</P>
-<OL>
-	<LI><P STYLE="margin-bottom: 0in"><FONT FACE="Times New Roman, serif"><FONT SIZE=3>Documents
-	to be filed appear in the Work List area. </FONT></FONT>
-	</P>
-	<OL TYPE=A>
-		<LI><P STYLE="margin-bottom: 0in"><FONT COLOR="#000000"><FONT FACE="Times New Roman, serif"><FONT SIZE=3>Documents
-		that are received via fax appear as .pdf files with an eFax prefix.
-		These documents can be processed directly. There is no metadata
-		associated with these documents, so all information must be entered
-		manually.</FONT></FONT></FONT></P>
-		<LI><P STYLE="margin-bottom: 0in"><FONT COLOR="#000000"><FONT FACE="Times New Roman, serif"><FONT SIZE=3>Documents
-		that are received via email are in one of two forms. Metadata
-		associated with the documents includes the sender's email address
-		and name. This metadata will populate certain forms for specific
-		document types.</FONT></FONT></FONT></P>
-		<OL TYPE=i>
-			<LI><P STYLE="margin-bottom: 0in"><FONT COLOR="#000000"><FONT FACE="Times New Roman, serif"><FONT SIZE=3>If
-			an email is received with a single document to be processed that
-			document will appear by itself. </FONT></FONT></FONT>
-			</P>
-			<LI><P STYLE="margin-bottom: 0in"><FONT COLOR="#000000"><FONT FACE="Times New Roman, serif"><FONT SIZE=3>If
-			an email is received with multiple documents, all of the documents
-			will be put into a single directory and the directory appears in
-			the work list.</FONT></FONT></FONT></P>
-		</OL>
-	</OL>
-	<LI><P STYLE="margin-bottom: 0in"><FONT COLOR="#000000"><FONT FACE="Times New Roman, serif"><FONT SIZE=3>To
-	convert documents to a form that can be processed, select the
-	directory from the Work List. The contents of the directory will be
-	displayed on the view port. </FONT></FONT></FONT>
-	</P>
-	<OL TYPE=A>
-		<LI><P STYLE="margin-bottom: 0in"><FONT COLOR="#000000"><FONT FACE="Times New Roman, serif"><FONT SIZE=3>If
-		the documents are a file and a signature (e.g. icla.txt and
-		icla.txt.asc) the tool will attempt to verify the document via the
-		command &quot;gpg --verify icla.txt.asc&quot;. The results of the
-		verification are displayed in the view port along with the list of
-		documents. If the document verifies correctly, it can be processed
-		as if it were a document. </FONT></FONT></FONT>
-		</P>
-		<OL TYPE=i>
-			<LI><P STYLE="margin-bottom: 0in"><FONT COLOR="#000000"><FONT FACE="Times New Roman, serif"><FONT SIZE=3>If
-			the document does not verify because the public key is not
-			available, try to download the key from a public key server via
-			the command &quot;gpg --recv-keys &lt;pub-key&gt;&quot;. After the
-			key is downloaded, you can try again.</FONT></FONT></FONT></P>
-			<LI><P STYLE="margin-bottom: 0in"><FONT COLOR="#000000"><FONT FACE="Times New Roman, serif"><FONT SIZE=3>If
-			the document does not verify because the signature is BAD, contact
-			the sender and attempt to get the document in a different mode.</FONT></FONT></FONT></P>
-		</OL>
-		<LI><P STYLE="margin-bottom: 0in"><FONT COLOR="#000000"><FONT FACE="Times New Roman, serif"><FONT SIZE=3>If
-		the documents are a collection of documents, they can be turned
-		into individual documents via the Staple command in the view port. </FONT></FONT></FONT>
-		</P>
-		<OL TYPE=i>
-			<LI><P STYLE="margin-bottom: 0in"><FONT COLOR="#000000"><FONT FACE="Times New Roman, serif"><FONT SIZE=3>If
-			the documents comprise a single document, e.g. icla1.pdf,
-			icla2.pdf, select both documents and press the Staple key. Both
-			documents will be combined into a single .pdf document and
-			displayed in the Work List. This technique currently works for
-			.pdf and .jpg files.</FONT></FONT></FONT></P>
-			<LI><P STYLE="margin-bottom: 0in"><FONT COLOR="#000000"><FONT FACE="Times New Roman, serif"><FONT SIZE=3>If
-			the documents comprise multiple independent documents, select a
-			single document and press Staple. The single document will be
-			copied into a new document with the same metadata and placed into
-			the Work List. This technique currently works for .pdf and .jpg
-			files.</FONT></FONT></FONT></P>
-			<LI><P STYLE="margin-bottom: 0in"><FONT COLOR="#000000"><FONT FACE="Times New Roman, serif"><FONT SIZE=3>For
-			each document in the collection, after stapling the document is
-			ready for processing.</FONT></FONT></FONT></P>
-			<LI><P STYLE="margin-bottom: 0in"><FONT COLOR="#000000"><FONT FACE="Times New Roman, serif"><FONT SIZE=3>After
-			all documents have been processed, check to make sure the
-			directory has been deleted by using the &quot;svn status&quot;
-			command. [The directory will not appear in the Work List if it is
-			empty.] If not, remove it manually using the &quot;svn rm&quot;
-			command.</FONT></FONT></FONT></P>
-		</OL>
-	</OL>
-	<LI><P STYLE="margin-bottom: 0in"><FONT COLOR="#000000"><FONT FACE="Times New Roman, serif"><FONT SIZE=3>To
-	process a document, select it from the Work List. If it is
-	displayable, the document will appear in the view port. All
-	documents must be complete and legible. Multipage documents must
-	have all pages included (specifically, not just the first and last
-	page) in the same transmission. </FONT></FONT></FONT>
-	</P>
-	<OL TYPE=A>
-		<LI><P STYLE="margin-bottom: 0in"><FONT COLOR="#000000"><FONT FACE="Times New Roman, serif"><FONT SIZE=3>ICLAs:
-		Required fields are full name, email address, signature, and date.
-		If there is already an existing ICLA for the same name, you must
-		establish whether the existing document is for the same person. </FONT></FONT></FONT>
-		</P>
-		<OL TYPE=i>
-			<LI><P STYLE="margin-bottom: 0in"><FONT COLOR="#000000"><FONT FACE="Times New Roman, serif"><FONT SIZE=3>If
-			the existing document is for the same person, process the new ICLA
-			with a different file name (e.g. existing-name2.pdf) and before
-			completing the process, create a directory (e.g. existing-name/)
-			and move both existing-name.pdf and existing-name2.pdf into the
-			new directory.</FONT></FONT></FONT></P>
-			<LI><P STYLE="margin-bottom: 0in"><FONT COLOR="#000000"><FONT FACE="Times New Roman, serif"><FONT SIZE=3>If
-			the existing document is for a different person, contact the
-			submitter and see if there is an additional (middle) name that can
-			be used in the &quot;Full Name&quot; section. If not, see if there
-			is a title that can be used to distinguish the entries.</FONT></FONT></FONT></P>
-			<LI><P STYLE="margin-bottom: 0in"><FONT COLOR="#000000"><FONT FACE="Times New Roman, serif"><FONT SIZE=3>Try
-			to determine if the ICLA is from a person who should be given
-			commit access. Read the original email containing the document,
-			look at the &quot;notify PMC&quot; field on the form, and search
-			the email records to determine if the ICLA is in response to an
-			invitation from a PMC or PPMC. If there is a valid invitation from
-			a PMC, select the url of the invitation so it can be pasted into
-			the ICLA form below the &quot;file&quot; button.</FONT></FONT></FONT></P>
-		</OL>
-		<LI><P STYLE="margin-bottom: 0in"><FONT COLOR="#000000"><FONT FACE="Times New Roman, serif"><FONT SIZE=3>If
-		the acknowledgement email to the ICLA submitter bounces, try to
-		resolve the issue. Common reasons for bounced emails are typos due
-		to illegible ICLAs or ambiguous special symbols, such as &quot;-&quot;
-		for &quot;_&quot;, &quot;0&quot; for &quot;o&quot;. Note that email
-		addresses are case-insensitive. If the proper email address cannot
-		be obtained, manually edit the foundation/officers/iclas.txt file
-		and change the incorrect email address. The address can be changed,
-		for example, from &quot;<A HREF="mailto:wrong.email@company.com">wrong.email@company.com</A>&quot;
-		to &quot;Bounced Email&lt; <A HREF="mailto:wrong.email@company.com">wrong.email@company.com</A>&gt;&quot;.</FONT></FONT></FONT></P>
-		<LI><P STYLE="margin-bottom: 0in"><FONT COLOR="#000000"><FONT FACE="Times New Roman, serif"><FONT SIZE=3>CCLAs
-		must be signed by an officer of the corporation authorized to enter
-		into binding contracts. Required fields are corporation name,
-		contact, and email address. </FONT></FONT></FONT>
-		</P>
-		<LI><P STYLE="margin-bottom: 0in"><FONT COLOR="#000000"><FONT FACE="Times New Roman, serif"><FONT SIZE=3>NDAs
-		require the apache id.</FONT></FONT></FONT></P>
-	</OL>
-	<LI><P STYLE="margin-bottom: 0in"><FONT COLOR="#000000"><FONT FACE="Times New Roman, serif"><FONT SIZE=3>If
-	there are duplicate documents to be processed, after processing the
-	best document, remove the duplicates before committing. Choose
-	&quot;other&quot; &quot;junk&quot; from the menu. After removing all
-	duplicates, commit the batch.</FONT></FONT></FONT></P>
-	<LI><P STYLE="margin-bottom: 0in"><FONT COLOR="#000000"><FONT FACE="Times New Roman, serif"><FONT SIZE=3>After
-	processing an ICLA, if the sender should have an account created,
-	select &quot;New Account&quot; from the menu. The last ICLA should
-	appear in the &quot;ASF New Account Request&quot; form. In
-	&quot;Comments&quot; indicate &quot;original committer&quot; if the
-	ICLA is from a new podling. Otherwise, leave it blank.</FONT></FONT></FONT></P>
-	<LI><P STYLE="margin-bottom: 0in"><FONT COLOR="#000000"><FONT FACE="Times New Roman, serif"><FONT SIZE=3>Forms
-	received by other than the above process should be added to the
-	documents/received directory and added via &quot;svn add&quot;. The
-	document will then appear in the work list to be processed as above.
-	Documents that are not in the proper format should be converted to
-	.pdf before adding them.</FONT></FONT></FONT></P>
-</OL>
-<P STYLE="margin-bottom: 0in"><BR>
-</P>
-<P STYLE="margin-bottom: 0in"><BR>
-</P>
-</BODY>
-</HTML>
\ No newline at end of file
diff --git a/www/secretary/workbench/README b/www/secretary/workbench/README
index 89db1ff..14ec74e 100644
--- a/www/secretary/workbench/README
+++ b/www/secretary/workbench/README
@@ -1,27 +1,85 @@
-This tool help file documents received via fax or email.
+This directory contains a script that fetches and parsed secretary emails
+and a server that will enable exploration of those emails.
 
-See HOWTO.html for usage.
 
-General design of the tool:
+Usage:
 
-*) configuration is done using local_paths.yml.  Format is a series of
-   names followed by locations.  Most are directories where svn checkouts
-   reside.  'mail' is a path to a ruby script that sets Mail.defaults as
-   well as @from and @sig.
+  rake fetch
+  rake server
 
-*) index.html splits the window into two panes using old-school frames.
+Notes:
 
-*) worklist.cgi, worklist.css, and worklist.js are primarily responsible
-   for the left pane.  This is mostly an HTML forms processing, with a lot
-   of JavaScript to assist.
+  First fetch and parse will take approximately an hour, even with a relatively
+  fast machine and internet connection.  Subsequent fetches will take as
+  little as ten seconds or less.  (For the impatient: replace 'fetch' with
+  'fetch1' and you will only get the latest month.  At some later point,
+  running 'rake fetch' will fetch the remaining months as well as any new
+  emails that have arrived in the current month).
 
-*) file.cgi is responsible for the right pane.  This is where most of the
-   server logic resides, and mostly involves invoking underlying system
-   commands (e.g., svn, pdftk, convert, gpg) and returning the results as
-   HTML.
+  Secretary email archive currently requires about approximately 11 Gigabytes.
 
-*) ccla.erb, grant.erb, icla.erb, mem.erb, and nda.erb are mail templates
-   of confirmation replies sent back when the document is processed.
+  Some functions will require installations of gpg, imageMagick and pdftk.
 
-*) The tool assumes that the url /members/received is a symlink to a checkout
-   of https://svn.apache.org/repos/private/documents/received
+  OS X El Capitan users may want to look at:
+    http://stackoverflow.com/a/33248310
+
+Overview of files:
+
+  Gemfile:            Ruby configuration (installation of gems)
+  Rakefile:           Command line configuration (like 'make')
+  config.rb:          Customizations
+  config.ru:          Rack (webserver) configuration
+  mailbox.rb:         Encapsulate interface to wb server
+  officers-secretary: local copy of mailboxes and indexes (in YAML format)
+  parsemail.rb:       Fetch and parse emails
+  server.rb:          Web interface to emails
+  views:              HTML templates (in Wunderbar format)
+
+Overview of control flow:
+
+  server.rb:          Matches HTTP requests to methods and paths, and runs
+                      the associated code.  This code will either return a
+                      result directly (rare) or invoke a view using a method
+                      name starting with an underscore.  For more
+                      information, see:
+
+                      http://www.sinatrarb.com/documentation.html
+                      https://github.com/rubys/wunderbar/#readme
+
+  views:              Files in the views directory have two extensions, the
+                      first identifies the target type (html, json, js), the
+                      second indicates the language of the view (rb).
+
+    html views:       Method names that start with an underscore generate HTML.
+                      This HTML may pull in scripts, stylesheets, and have
+                      inline code that renders other views.  Views in the
+                      actions subdirectory produce responses to HTTP post
+                      requests.
+
+    js views:         This code is converted from Ruby to JavaScript.
+                      This conversion is aware of React.js and will perform
+                      additional React.js mappings when it encounters classes
+                      that derive from "React".  See
+
+                      https://facebook.github.io/react/docs/component-specs.html
+                      https://github.com/rubys/ruby2js#readme
+
+                      Note: in this application app.js.rb pulls in all of the
+                      other javascript files and returns the result as a
+                      single file.
+
+    json views:       The last statement identifies an object (typically a
+                      hash or array) that will be converted to JSON and sent
+                      back as a response.
+
+Model:
+
+  mailbox.rb:         read-only interface to a mailbox files found in
+                      officers-secretary as well as read/write interface
+                      to accompanying YAML file.
+
+  message.rb:         representation of an individual message, backed by
+                      both the mbox file and the YAML file.
+
+  attachment.rb:      representation of an individual attachment, backed by
+                      both the mbox file and the YAML file.
diff --git a/www/secmail/Rakefile b/www/secretary/workbench/Rakefile
similarity index 100%
rename from www/secmail/Rakefile
rename to www/secretary/workbench/Rakefile
diff --git a/www/secretary/workbench/ccla.erb b/www/secretary/workbench/ccla.erb
deleted file mode 100644
index fc031dd..0000000
--- a/www/secretary/workbench/ccla.erb
+++ /dev/null
@@ -1,13 +0,0 @@
-to:   <%= contact.inspect %> <<%= cemail %>>
-from: <%= from %>
-cc:   secretary@apache.org
-bcc:  <%= bcc %>
-subject: Your CCLA sent to Apache Secretary
-
-Dear <%= contact %>,
-
-This message acknowledges receipt of the following document, which has been filed in the Apache Software Foundation records:
-
-  <%= commit_message %>
-
-<%= sig %>
diff --git a/www/secmail/config.rb b/www/secretary/workbench/config.rb
similarity index 100%
rename from www/secmail/config.rb
rename to www/secretary/workbench/config.rb
diff --git a/www/secmail/config.ru b/www/secretary/workbench/config.ru
similarity index 100%
rename from www/secmail/config.ru
rename to www/secretary/workbench/config.ru
diff --git a/www/secmail/deliver.rb b/www/secretary/workbench/deliver.rb
similarity index 100%
rename from www/secmail/deliver.rb
rename to www/secretary/workbench/deliver.rb
diff --git a/www/secretary/workbench/file.cgi b/www/secretary/workbench/file.cgi
deleted file mode 100755
index f24ebf8..0000000
--- a/www/secretary/workbench/file.cgi
+++ /dev/null
@@ -1,1138 +0,0 @@
-#!/usr/bin/env ruby
-$LOAD_PATH.unshift File.realpath(File.expand_path('../../../../lib', __FILE__))
-
-require 'wunderbar'
-require 'open3'
-require './local_paths'
-require 'fileutils'
-require 'ostruct'
-require 'escape'
-require 'time'
-require 'whimsy/asf'
-
-ENV['LANG'] = 'en_US.UTF-8'
-
-ENV['GNUPGHOME'] = '/srv/gpg' if Dir.exist?('/srv/gpg')
-
-def html_fragment(&block)
-  x = Wunderbar::HtmlMarkup.new({})
-  x.instance_eval(&block)
-  x._.target!
-end
-
-OLDPODLINGS = [
-  'allura',
-  'ambari',
-  'blur',
-  'celix',
-  'chukwa',
-  'deltaspike',
-  'devicemap',
-  'drill',
-  'droids',
-  'hcatalog',
-  'jspwiki',
-  'kafka',
-  'kalumet',
-  'mesos',
-  'npanday',
-  'odf',
-  'onami',
-  's4',
-  'tashi',
-  'vxquery',
-  'wave'
-]
-
-def update_pending fields, dest
-  # start with the fields provided
-  fields = Hash[fields]
-
-  # reject blank fields, normalize the rest
-  fields.reject! {|k,v| v == [""]}
-  fields.each_key {|k| fields[k]=fields[k].join(' ')}
-
-  # add properties from svn
-  at = svn_at(dest)
-  at += '/*' if File.directory?(dest) and `svn proplist #{dest}#{at}`.empty?
-  `svn proplist #{dest}#{at}`.scan(/  \w+:[-\w]+/).each do |prop|
-    prop.untaint if prop.strip =~ /^\w+(:\w+)/
-    value = `svn propget #{prop} #{dest}#{at}`.chomp
-    value.gsub!(/\\x[0-9a-fA-F][0-9a-fA-F]/) {|c| [c[2..3].to_i(16)].pack('C')}
-    value.gsub!(/\\[0-7][0-7][0-7]/) {|c| [c[1..3].to_i(8)].pack('C')}
-    fields[prop.strip] = value
-  end
-
-  # copy email field
-  fields['email'] ||= fields['gemail'] || fields['cemail'] ||
-                      fields['nemail'] || fields['memail'] ||
-                      fields['iemail'] || fields['uemail'] || fields['pemail']
-  fields.delete('email') unless fields['email']
-  
-  # Concatenate the fields to the pending list and write to disk
-  pending = YAML.load(open(PENDING_YML)) rescue []
-  pending << fields
-  open(PENDING_YML, 'w') {|file| file.write pending.to_yaml}
-end
-
-class Wunderbar::XmlMarkup
-  def move source, dest
-    if not Dir.chdir(RECEIVED) {Dir['*']}.include? source.chomp('/')
-      tag! :pre, "svn mv #{source.inspect} #{dest}", class: '_stdin'
-      tag! :pre, "File #{source} doesn't exist.", class: '_stderr'
-      return
-    end
-
-    source = File.expand_path(source, RECEIVED).untaint
-    source += svn_at(source)
-
-    if File.exist?(dest) and !File.directory?(dest)
-      # Since svn error messages aren't as helpful as they could be here,
-      # let's improve on it... by pretending to run the command and then
-      # producing a better error message.
-      tag! :pre, "svn mv #{source.inspect} #{dest}", class: '_stdin'
-      tag! :pre, "File #{dest} already exists.", class: '_stderr'
-    else
-      if (`svn --version --quiet`.chomp.split('.') <=> %w(1 5)) >= 1
-        if source.start_with? Dir.pwd + '/'
-          source = source[Dir.pwd.length+1..-1]
-        end
-        self.system "svn mv #{source.inspect} #{dest}"
-      else
-        if `svn st #{source.inspect}` =~ /^A/
-          self.system "cp #{source.inspect} #{dest}"
-          self.system "svn add #{dest}"
-          `svn proplist #{source.inspect}`.scan(/  \w+:[-\w]+/).each do |prop|
-            prop.untaint if prop.strip =~ /^\w+(:\w+)/
-            value = `svn propget #{prop.strip} #{source.inspect}`.chomp
-            self.system "svn propset #{prop.strip} #{value.inspect} #{dest}"
-          end
-          self.system "svn revert #{source.inspect}"
-        elsif `svn st #{source.inspect}` !~ /^D/
-          self.system "svn mv --force #{source.inspect} #{dest}"
-        end
-      end
-      self.system "svn rm #{dest}/Thumbs.db" if File.exist?("#{dest}/Thumbs.db")
-    end
-  end
-end
-
-def check
-  email = {}
-  prev_name = nil
-  output = []
-
-  open("#{OFFICERS}/iclas.txt").each do |line|
-    next unless line =~ /^\w.*?:(.*?):(.*?):(.*?)(:(.*))?\n/
-    name = $1
- 
-    if ! $3.index('@')
-      output << [ 'email', "#{$1}: #{$3}" ]
-    elsif ! $4 or ! $4.index('CLA')
-      if not %w(:President :Treasurer).include? $4.split(';').first
-        output << [ 'nocla', "#$2: #$4" ]
-      end
-    elsif email[$3.downcase]
-      output << [ 'dupemail', "#{$3}: #{name} and #{email[$3.downcase]}" ]
-    end
- 
-    email[$3.downcase] = name
-
-    name.split.each do |word|
-      next if word.length == 1 and word !~ /\w/
-      next if word =~ /^\W/
-      next if %w(van von da de del der den dos i tot la).include? word
-      output << ['case', name] if word !~ /[A-Z][a-z]*/
-    end
-
-    sort_name = ASF::ICLA.lname(line)
-    if prev_name and prev_name > sort_name
-      output << [ 'order', "#{prev_name} > #{sort_name}" ]
-    end
-    prev_name = sort_name
-  end
-
-  html_fragment do
-    if output.empty?
-      _p 'No icla.txt issues.'
-    else
-      _h3 'icla.txt issues'
-      _table border: 1, cellpadding: 10, cellspacing: 0 do
-        _thead do
-          _th 'issue'
-          _th 'name'
-        end
-        output.each do |type, message|
-          _tr do
-            _td type
-            _td message
-          end
-        end
-      end
-    end
-  end
-end
-
-def svn_info(source)
-  source.untaint if Dir.chdir(RECEIVED) {Dir['*']}.include? source.chomp('/')
-  source = File.join(RECEIVED, source)
-  source += svn_at(source)
-  info = {
-    'from' =>  `svn propget email:name #{source}`.chomp,
-    'email' => `svn propget email:addr #{source}`.chomp
-  }
-
-  if info['from'].empty? and info['email'].empty?
-    log=`svn log -l 9 #{source}`
-    from=log.scan(/\nFrom: (.*)/).flatten.first
-
-    if from and from !~ /"eFax"/
-      email = from.gsub(/.*<(.*)>$/, '\1')
-      info['email'] = email if email.include?('@')
-    
-      from.gsub! /\s<.*>$/, ''
-      from.gsub! /^"(.*)"$/, '\1'
-      info['from'] = from unless from.include?('@')
-    end
-  end
-
-  info.each do |name, value| 
-    value.gsub!(/\\x[0-9a-fA-F][0-9a-fA-F]/) {|c| [c[2..3].to_i(16)].pack('C')}
-    value.force_encoding('utf-8')
-    value.force_encoding('iso-8859-1') unless value.valid_encoding?
-  end
-
-  info
-end
-
-def send_email(target, message)
-  pending = YAML.load(open(PENDING_YML))
-
-  require_relative 'secmail'
-  require 'erb'
-
-  mails = []
-  pending.each do |pending_hash|
-    next unless pending_hash['email'] == target
-
-    vars = OpenStruct.new(Hash[pending_hash.map {|k,v| 
-      [k.gsub(/\W/,'_'), v.dup.untaint]
-    }])
-    vars.commit_message = message
-
-    # collapse pmc and podling variable names
-    vars.pmc ||= vars.cpmc || vars.gpmc || vars.ipmc || vars.upmc || vars.ppmc
-    vars.podling ||= vars.cpodling || vars.gpodling || vars.ipodling || vars.upodling || vars.ppodling
-
-    # send email, if template exists
-    template = vars.doctype + '.erb'
-    template.taint unless template =~ /^\w[.\w]+$/
-    if File.exist?(template)
-      # prepare to send mail
-      ASF::Mail.configure
-
-      # extract fields from the Mail defaults
-      Mail.defaults do
-        vars.sig  = instance_eval {@sig.gsub(/^ +/,'').strip}
-        vars.from = instance_eval {@from}
-        vars.bcc  = instance_eval {@bcc}
-      end
-
-      # expand template
-      def vars.render(template)
-        ERB.new(template).result(binding)
-      end
-      message = vars.render(open(template).read.untaint)
-      headers = message.slice!(/\A(\w+: .*\r?\n)*(\r?\n)*/)
-
-      mail = Mail.new do
-        # apply headers
-        headers.scan(/(\w+):[ \t]*(.*)/).each do |name, value|
-          send name, value.untaint unless value.empty?
-        end
-
-        body message
-
-        # is this a reply?
-        if vars.email_id
-          in_reply_to vars.email_id
-          references  vars.email_id
-
-          # override subject?
-          if vars.email_subject and !vars.email_subject.empty?
-            begin
-              if vars.email_subject =~ /^re:\s/i
-                subject vars.email_subject
-              else
-                subject 'Re: ' + vars.email_subject
-              end
-            rescue Encoding::UndefinedConversionError
-              # some error in the subject; use the subject in the .erb file
-            end
-          end
-        end
-      end
-
-      # get the list of cc's as an array
-      cc = mail.cc.to_a
-
-      # eliminate the legal-archive from the cc list
-      cc.reject! {|addr| addr =~ /\blegal-archive@apache\.org\b/}
-
-      # add additional cc if email:addr != email
-      if vars.email_addr and !vars.email_addr.include?(mail.to.to_s)
-        if vars.email_name
-          cc << "#{vars.email_name.inspect} <#{vars.email_addr}>"
-        else
-          cc << vars.email_addr
-        end
-      end
-
-      # add original cc list
-      cc << vars.email_cc if vars.email_cc
-
-      # calculate podling list name
-      if vars.podling and vars.podling =~ /^\w[-\w]+$/
-        if OLDPODLINGS.include?(vars.podling) 
-          vars.podlinglist = "#{vars.podling}-private@incubator.apache.org"
-        else
-          vars.podlinglist = "private@#{vars.podling}.incubator.apache.org"
-        end
-      end
-
-      # add pmc and podling lists, if supplied
-      cc << "private@#{vars.pmc}.apache.org" if vars.pmc
-      cc << "#{vars.podlinglist}" if vars.podlinglist
-      cc << "private@incubator.apache.org" if vars.podling and not vars.pmc
-
-      # replace the list of cc's
-      mail.cc = cc.uniq.join(', ')
-
-      # update bcc
-      if vars.email_bcc and not vars.email_bcc.empty?
-        bcc = mail.bcc.to_s.split(/,\s*/) + vars.email_bcc.to_s.split(/,\s*/)
-        mail.bcc = bcc.uniq.join(', ').untaint
-      end
-
-      # for debugging purposes
-      mails << [vars.email, mail.to_s]
-
-      # ship it!
-      mail.deliver!
-
-      completed = YAML.load(open(COMPLETED_YML)) rescue []
-      completed << pending_hash
-      open(COMPLETED_YML, 'w') {|file| file.write completed.pop(10).to_yaml}
-
-      # clean up pending list
-      pending.delete pending_hash
-
-      if pending.empty?
-        FileUtils.rm_f PENDING_YML
-      else
-        open(PENDING_YML, 'w') {|file| file.write pending.to_yaml}
-      end
-    end
-  end
-
-  html_fragment do
-    mails.each do |dest, mail|
-      _h2 "email #{dest}"
-      _pre.email mail
-    end
-  end
-end
-
-def committable
-  files = %W( #{FOUNDATION}/Correspondence/JCP/tck-nda-list.txt )
-  if defined?(MEETING)
-    files += %W(#{MEETING}/memapp-received.txt #{FOUNDATION}/members.txt)
-    files << MEETING
-    files << SUBREQ
-  end
-  files += [DOCUMENTS, OFFICERS]
-end
-
-_json do
-  if @cmd == 'svninfo'
-    _! svn_info(@source)
-  elsif @cmd == 'icla.txt issues'
-    _html check
-  elsif @cmd =~ /email (.*)/
-    _html send_email $1, @message
-  elsif @cmd =~ /svn (update|revert -R|cleanup)/ and committable.include? @file
-    op, file = $1.split(' '), @file
-    op << ['--username', $USER, '--password', $PASSWORD] if $PASSWORD
-    _html html_fragment {
-      _.system [ 'svn', *op, file ]
-    }
-  elsif @cmd =~ /svn commit/ and committable.include? @file
-    message, file = @message, @file
-    _html html_fragment {
-      _.system [
-        'svn', 'commit', '-m', message, '--no-auth-cache',
-        (['--username', $USER, '--password', $PASSWORD] if $PASSWORD),
-        file
-      ]
-    }
-  elsif @cmd =~ /ezmlm-sub/
-    message, list, email, availid = @message, @list, @email, @availid
-    _html html_fragment {
-      user = ASF::Person.find(availid)
-      vars = {
-        version: 3, # This must match http://s.apache.org/008
-        availid: availid,
-        addr: email,
-        listkey: list,
-        member_p: true,
-        chair_p: ASF.pmc_chairs.include?(user),
-      }
-
-      fn = "#{availid}-members-#{Time.now.strftime '%Y%m%d-%H%M%S-%L'}.json"
-      fn.untaint if availid =~ /^\w[-.\w]+$/
-
-      Dir.chdir SUBREQ do
-        File.open(fn, 'w') {|file| file.write JSON.pretty_generate(vars) + "\n"}
-        _.system [ 'svn', 'add', fn ]
-        _.system [
-          'svn', 'commit', '-m', message, '--no-auth-cache',
-          (['--username', $USER, '--password', $PASSWORD] if $PASSWORD),
-          fn
-        ]
-      end
-    }
-  elsif @cmd =~ /modify_unix_group/
-    cmd, group, availid = @cmd, @group, @availid
-    _html html_fragment {
-      _pre cmd, class: '_stdin'
-      ldap = ASF.init_ldap(true)
-      ldap.bind("uid=#{$USER},ou=people,dc=apache,dc=org", $PASSWORD)
-
-      ldap.modify "cn=#{group},ou=groups,dc=apache,dc=org",
-        [LDAP.mod(LDAP::LDAP_MOD_ADD, 'memberUid', [availid])]
-
-      _pre "add member: #{ldap.err2string(ldap.err)} (#{ldap.err})",
-        class: (ldap.err == 0 ? '_stdout' : '_stderr')
-
-      ldap.unbind
-
-    }
-  else
-    cmd = @cmd
-    _html html_fragment { 
-      _pre cmd, class: '_stdin'
-      _pre 'Unauthorized command', class: '_stderr'
-    }
-  end
-end
-
-DESTINATION = {
-  "operations" => "to_operations",
-  "dup" => "deadletter/dup",
-  "incomplete" => "deadletter/incomplete",
-  "unsigned" => "deadletter/unsigned"
-}
-
-line = nil
-
-_html do
-  _head_ do
-    _title 'File Document'
-
-    if ! %w{check update commit view danger}.include?(@action.to_s.downcase)
-      _script 'parent.frames[0].location.reload()'
-    end
-
-    _style %{
-      html {background-color: #F8F8FF}
-      pre {font-weight: bold; margin: 0}
-      pre._stdin, pre.todo {color: #C000C0; margin-top: 1em}
-      .todo {opacity: 0.2}
-      pre._stdout {color: #000}
-      pre._hilite {color: #000; background-color: #FF0}
-      pre._stderr {color: #F00}
-      pre.email {background-color: #BDF; padding: 1em 3em}
-      pre.email {border-radius: 1em}
-      .traceback {background-color:#ff0; margin: 1em 0; padding: 1em;
-        border: 2px solid}
-      #notice {color: green}
-      .collision {background-color: #ee82ee}
-    }
-  end
-
-  _body? do
-
-    activity_log = nil
-    File.open "#{RECEIVED}/activity.yml", File::RDWR|File::CREAT, 0644 do |file|
-      file.flock File::LOCK_EX
-      activity_log = YAML.load(file.read) || []
-      activity_log.unshift({'USER' => $USER, 'time' => Time.now.utc}.
-        merge(Hash[params.map {|key,value| [key,value.first]}]))
-      activity_log.delete_at(1) if activity_log.length > 1 and
-        activity_log[1]['USER'] == $USER and 
-        activity_log[1]['action'] == 'update'
-      file.rewind
-      file.write YAML.dump(activity_log[0...5])
-      file.flush
-      file.truncate file.pos
-    end
-
-    filename = [@filename, @cfilename, @gfilename, @mfilename, @nfilename].
-      find {|name| name and not name.empty?}
-    filename.untaint if filename and filename =~ /^[-.\w]+/
-    doctype = (@doctype == 'mem' ? 'member_apps' : @doctype.to_s+'s')
-    doctype.untaint if doctype =~ /^\w+$/
-    dest = File.join(DOCUMENTS, doctype, filename.to_s)
-    stem = ";#{filename.sub(/\.\w+$/,'').split('/').first}" if filename
-    alax = false
-    if @source and Dir.chdir(RECEIVED) {Dir['*']}.include? @source.chomp('/')
-      @source.untaint
-    end
-
-    unless %w(clr rubys jcarman sanders mnour).include? $USER
-      @action = 'welcome' unless @action == 'view'
-    end
-
-    case (@action || @doctype).to_s.downcase
-    when 'welcome'
-      _h1 "Welcome!"
-      _p "This tools is for the Secretarial's team use only."
-      _p %{
-        Feel free to look around, but none of your actions will cause any
-        files to be moved, updated, or any emails to be sent.
-      }
-
-    when 'icla'
-      if @replaces != ''
-        remove_id, remove_email = @replaces.strip.split(':',2)
-      else
-        remove_id, remove_email = 'notinavail', nil
-      end
-
-      insert = [
-        remove_id, 
-        @realname.strip, 
-        @pubname.strip, 
-        @email.strip, 
-        "Signed CLA#{stem}\n"
-      ].join(':')
-
-      Dir.chdir(OFFICERS) do
-        input = open('iclas.txt') {|file| file.to_a}
-        open('iclas.txt','w') do |file|
-          input.each do |line|
-            if insert and ASF::ICLA.lname(line) >= ASF::ICLA.lname(insert)
-              if insert.split(':',2).last != line.split(':',2).last
-                file.print insert
-              end
-              insert = nil
-            end
-            fields = line.split(':')
-            next if fields[0] == remove_id and fields[3] == remove_email
-            file.print line
-          end
-          file.print insert if insert
-        end
-
-        _h1 @pubname
-        if @source=~/[^\x00-\x7F]/ and RUBY_PLATFORM=~/darwin/i
-          require 'unicode'
-          @source = Unicode.normalize_KC(@source)
-        end
-        _.move @source, dest
-        _.system "svn diff iclas.txt", hilite: @pubname
-      end
-
-      update_pending params, dest
-
-    when 'grant'
-      insert = "#{@from.strip}" +
-        "\n  file: #{dest.split('/').last}" +
-        "\n  for: #{@description.strip.gsub(/\r?\n\s*/,"\n       ")}"
-
-      Dir.chdir(OFFICERS) do
-        input = open('grants.txt') {|file| file.read}
-        marker = "\n# registering.  documents on way to Secretary.\n"
-        input = input.split(marker).insert(1,"\n#{insert}\n",marker)
-        open('grants.txt','w') do |file|
-          file.write(input.join)
-        end
-
-        _h1 "Grant"
-        _.move @source, dest
-        _.system "svn diff grants.txt", hilite: insert.split("\n")
-      end
-
-      update_pending params, dest
-
-    when 'ccla'
-      insert = "notinavail:" + @company.strip
-       
-      unless @contact.empty?
-        insert += " - #{@contact.strip}"
-      end
-
-      insert += ":#{@cemail.strip}:Signed Corp CLA"
-
-      unless @employees.empty?
-        insert += " for #{@employees.strip.gsub(/\s*\n\s*/, ', ')}"
-      end
-
-      unless @product.empty?
-        insert += " for #{@product.strip}"
-      end
-
-      Dir.chdir(OFFICERS) do
-        open('cclas.txt','a') {|file| file.write(insert+"\n")}
-
-        _h1 @pubname
-        _.move @source, dest
-        _.system "svn diff cclas.txt", hilite: insert
-      end
-
-      update_pending params, dest
-
-    when 'nda'
-      @realname ||= @nname
-
-      _h1 "NDA for #{@realname}"
-      _.move @source, dest
-
-      Dir.chdir(FOUNDATION) do
-        ndalist = "Correspondence/JCP/tck-nda-list.txt"
-        _.system "svn update #{ndalist}"
-        text = open(ndalist).read
-        open(ndalist, 'w') do |fh|
-          fh.write(text)
-          line = "#{@nname.ljust(20)} #{@nid.ljust(13)} "
-          line += Date.today.strftime("%Y/%m/%d    ")
-          line += `id -un`.chomp.ljust(10) + ' No TCK access yet'
-          fh.write("#{line}\n")
-        end
-        _.system "svn diff #{ndalist}", hilite: @nid
-      end
-
-      update_pending params, dest
-
-    when 'incomplete'
-      dest = "#{RECEIVED}/deadletter/incomplete/#{File.basename(@source)}"
-
-      Dir.chdir(RECEIVED) do
-        @realname ||= @iname
-        _h1 "Incomplete document received from #{@iname}"
-        _.move @source, dest
-      end
-
-      update_pending params, dest
-
-    when 'unsigned'
-      dest = "#{RECEIVED}/deadletter/unsigned/#{File.basename(@source)}"
-
-      Dir.chdir(RECEIVED) do
-        @realname ||= @uname
-        _h1 "Unsigned document received from #{@uname}"
-        _.move @source, dest
-      end
-
-      update_pending params, dest
-
-    when 'publickey'
-      @realname ||= @iname
-
-      _h1 "Public key not found for #{@pname}"
-
-      update_pending params, dest
-
-    when 'mem'
-      @realname ||= @mfname
-      dest.untaint if dest =~ /^[-.\w]+$/
-
-      _h1 "Membership Application for #{@realname}"
-      _.move @source, dest
-
-      if defined?(MEETING)
-        _.system "svn update #{MEETING}"
-        received = open("#{MEETING}/memapp-received.txt").read
-        begin
-          received[/(no )\s+\w+\s+\w+\s+#{@mavailid}/,1] = 'yes'
-          received[/(no )\s+\w+\s+#{@mavailid}/,1] = 'yes'
-          received[/(no )\s+#{@mavailid}/,1] = 'yes'
-        rescue
-          _pre.stderr $!
-        end
-        open("#{MEETING}/memapp-received.txt", 'w') do |fh| 
-          fh.write(received)
-        end
-        _.system "svn diff #{MEETING}/memapp-received.txt", hilite: @mavailid
-      end
-
-      _.system "svn update #{FOUNDATION}/members.txt"
-      pattern = /^Active.*?^=+\n+(.*?)^Emeritus/m
-      members_txt = open("#{FOUNDATION}/members.txt").read
-      data = members_txt.scan(pattern).flatten.first
-      members = data.split(/^\s+\*\)\s+/)
-      members.shift
-
-      members.push [
-        "#{@mfname}",
-        "#{@maddr.gsub(/^/,'    ').gsub(/\r/,'')}",
-        ("    #{@mcountry}"     unless @mcountry.empty?),
-        "    Email: #{@memail}",
-        ("      Tel: #{@mtele}" unless @mtele.empty?),
-        ("      Fax: #{@mfax}"  unless @mfax.empty?),
-        " Forms on File: ASF Membership Application",
-        " Avail ID: #{@mavailid}"
-      ].compact.join("\n") + "\n"
-
-      members_txt[pattern,1] = " *) " + members.join("\n *) ")
-      members_txt[/We now number (\d+) active members\./,1] = 
-        members.length.to_s
-      File.write("#{FOUNDATION}/members.txt", ASF::Member.sort(members_txt))
-
-      _.system "svn diff #{FOUNDATION}/members.txt"
-
-      update_pending params, dest
-
-    when 'staple'
-      _h1 'Staple'
-      Dir.chdir(RECEIVED) do
-        @source.sub! /\/$/, ''
-        @source.untaint if Dir['*'].include? @source
-        selected = params.keys.grep(/include\d+/)
-        selected.map! {|key| "#{@source}/#{params[key].first}"}
-        selected=Dir["#{@source}/*"] if selected.empty?
-        cleanup = []
-
-        # convert to pdf, if necessary
-        sources = []
-        selected.sort.each do |file|
-          file.untaint if Dir["#{@source}/*"].include? file
-          ext = file.split('.').last
-          if ext.downcase == 'pdf'
-            sources << file
-          else
-            cleanup << file.sub(Regexp.new(ext+'$'),'pdf').sub(' ', '_')
-            sources << cleanup.last
-            _.system(['convert', file, cleanup.last])
-          end
-        end
-
-        dest,i = @source,0
-        dest = @source + (i+=1).to_s while File.exist?("#{dest}.pdf")
-        at = svn_at(@source)
-
-        # concatenate sources
-        if sources.length > 1
-          _.system "pdftk #{sources.sort.join(' ')} cat output #{dest}.pdf"
-          _.system "svn add #{dest}.pdf#{at}"
-        elsif selected.first =~ /\.pdf$/i
-          _.system "svn mv #{sources.first}#{at} #{dest}.pdf"
-        else
-          _.system "mv #{cleanup.shift} #{dest}.pdf"
-          _.system "svn add #{dest}.pdf#{at}"
-        end
-
-        # copy properties
-        sfx = at
-        `svn proplist #{@source}#{sfx}`.scan(/  \w+:[-\w]+/).each do |prop|
-          prop.untaint if prop.strip =~ /^\w+(:\w+)/
-          value = `svn propget #{prop} #{@source}#{sfx}`.chomp
-          _.system(['svn', 'propset', prop.strip, value, "#{dest}.pdf#{at}"])
-        end
-        _.system "svn propset svn:mime-type application/pdf #{dest}.pdf#{at}"
-
-        # remove temporary file and source directory
-        _.system "rm #{cleanup.join(' ')}" unless cleanup.empty?
-        if not (Dir["#{@source}/*"]-selected).empty?
-          selected.each do |file| 
-            if `svn st #{file}` !~ /^D/ and File.exist? file
-              _.system "svn rm --force #{file}" 
-            end
-          end
-          if Dir["#{@source}/*"].empty?
-            _.system "svn remove --force #{@source}#{at}"
-          end
-        elsif `svn st #{@source}#{at}` !~ /^D/ and File.exist? @source
-          _.system "svn remove --force #{@source}#{at}"
-        end
-      end
-
-    when 'cleanup'
-      _h1 'Revert all and cleanup'
-
-      committable.each do |file|
-        status = `svn status #{file}`
-        unless status.empty?
-          status.scan(/^[?A]\s*\+?\s*(.*)/).flatten.each do |uncommitted|
-            _.system ['rm', '-rf', uncommitted]
-          end
-          if status =~ /^\w/
-            _pre.todo "svn revert -R #{file}", 'data-file' => file
-          end
-        end
-
-        if File.directory? file
-           _pre.todo "svn cleanup #{file}", 'data-file' => file
-        end
-      end
-
-      if File.exist?(PENDING_YML)
-        _.system "rm #{PENDING_YML}"
-      end
-
-      ajax=true
-
-    when 'commit'
-      _h1 'Commit'
-      log = Escape.shell_single_word(@message)
-      committable.each do |file|
-        unless `svn status #{file}`.empty?
-          _pre.todo "svn commit -m #{log} #{file}",
-            'data-message' => @message, 'data-file' => file
-        end
-      end
-
-      if File.exist?(PENDING_YML)
-        pending = YAML.load(open(PENDING_YML))
-
-        pending.each do |vars|
-          if vars['memail'] and vars['mfilename']
-            _pre.todo "ezmlm-sub lists/apache.org/members/ #{vars['memail']}",
-              'data-list' => 'members', 'data-email' => vars['memail'],
-              'data-availid' => vars['mavailid'], 'data-message' => @message
-          end
-
-          if vars['mavailid'] and vars['mfilename']
-            _pre.todo "modify_unix_group.pl member --add=#{vars['mavailid']}",
-              'data-group' => 'member', 'data-availid' => vars['mavailid']
-          end
-
-          _h2.todo "email #{vars['email']}", 'data-message' => @message
-        end
-      end
-
-      ajax = true
-
-    when 'other'
-      at = svn_at(@source)
-      Dir.chdir(RECEIVED) do
-        if @dest == 'burst'
-          _h1 'Burst'
-          dest = @source.sub(/\.\w+$/,'')
-
-          _.system "mkdir #{dest}"
-          _.system "pdftk #{@source} burst output #{dest}/%02d.pdf"
-          _.system "svn add #{dest}#{at}"
-          # copy properties
-          `svn proplist #{@source}#{at}`.scan(/ \w+:[-\w]+/).each do |prop|
-             prop.untaint if prop.strip =~ /^\w+(:\w+)/
-             next if prop.strip == 'svn:mime-type'
-             value = `svn propget #{prop} #{@source}#{at}`.chomp
-             _.system ['svn', 'propset', prop.strip, value, dest+at]
-          end
-          _.system "rm doc_data.txt" if File.exist? 'doc_data.txt'
-          _.system "svn rm #{@source}#{at}"
-        elsif @dest == 'flip'
-          _h1 'Flip'
-          _.system "pdftk #{@source} cat 1-endSouth output #{@source}.tmp"
-          _.system "mv #{@source}.tmp #{@source}"
-        elsif @dest == 'restore'
-          _h1 'Restore'
-          _.system "pdftk #{@source} cat 1-endNorth output #{@source}.tmp"
-          _.system "mv #{@source}.tmp #{@source}"
-        elsif @dest == 'rotate right'
-          _h1 'Rotate Right'
-          _.system "pdftk #{@source} cat 1-endEast output #{@source}.tmp"
-          _.system "mv #{@source}.tmp #{@source}"
-        elsif @dest == 'rotate left'
-          _h1 'Rotate Left'
-          _.system "pdftk #{@source} cat 1-endWest output #{@source}.tmp"
-          _.system "mv #{@source}.tmp #{@source}"
-        elsif @dest == 'junk'
-          _.system(['svn', 'rm', '--force', "#{@source}#{at}"])
-        elsif DESTINATION.include? @dest
-          _.move @source, DESTINATION[@dest]
-        else
-          _pre.stderr "Unknown destination: #{@dest}"
-        end
-      end
-
-    when 'update'
-      _h1 'Update'
-      _pre.todo "svn update #{OFFICERS}",  'data-file' => OFFICERS
-      _pre.todo "svn update #{DOCUMENTS}", 'data-file' => DOCUMENTS
-      if defined? MEETING
-        _pre.todo "svn update #{MEETING}", 'data-file' => MEETING
-        _pre.todo "svn update #{SUBREQ}", 'data-file' => SUBREQ
-      end
-      _h3.todo 'icla.txt issues'
-      ajax = true
-      cleanup = Dir["#{DOCUMENTS}/members/received/*"].map(&:untaint).
-        select {|name| File.directory?(name) and Dir["#{name}/*"].empty?}.
-        reject {|name| name =~ /\/to_\w+$/}
-      unless cleanup.empty?
-        _h2 'Empty directories'
-        _ul do
-          cleanup.each { |name| _li name }
-        end
-      end
-
-      _h2 'Recent Activity'
-      _table border: 1, cellpadding: 5, cellspacing: 0 do
-        _thead_ do
-          _tr do
-            _th 'Time'
-            _th 'User'
-            _th 'Action'
-          end
-        end
-
-        cleanup = {
-          icla: %w( realname email ),
-          ccla: %w( cemail contact ),
-          nda: %w( nname nemail nid ),
-          incomplete: %w( iname iemail ),
-          unsigned: %w( uname uemail ),
-          publickey: %w( pname pemail ),
-          mem: %w( memail ),
-          grant: %w( gname gemail ),
-        } 
-
-        _tbody do
-          activity_log[1..-1].each do |entry|
-            collision = (entry['USER'] != $USER)
-            collision &&= (Time.now-entry['time'] < 600)
-
-            keep = cleanup[entry['doctype']] || []
-            (cleanup.values.flatten - keep).each { |var| entry.delete(var) }
-
-            _tr_ class: ('collision' if collision) do
-              _td! do
-                 time = entry.delete('time')
-                 _time time, datetime: time.utc.iso8601
-              end
-              _td entry.delete('USER')
-
-              entry.delete_if {|name, value| value.empty?}
-              title = entry.map {|n, v| "#{n}: #{v.inspect}"}.join(', ')
-
-              name = entry['action'] || entry['doctype']
-              name = entry['dest'] if name == 'other'
-              _td name.to_s.downcase, title: title
-            end
-          end
-        end
-      end
-
-    when 'view'
-      files = nil
-      Dir.chdir(RECEIVED) do
-	if Dir['*'].include? @dir.chomp('/')
-	  files = Dir["#{@dir.untaint.chomp('/')}/*"].map(&:untaint).sort
-        else
-	  files = []
-        end
-
-	if files.length == 2
-	  verify = nil
-
-	  if files.first.end_with? '.sig' or files.first.end_with? '.asc'
-	    unless files.last.end_with? '.sig' or files.last.end_with? '.asc'
-	      verify = files
-            end
-	  elsif files.last.end_with? '.sig' or files.last.end_with? '.asc'
-	    verify = files.reverse
-	  end
-
-	  if verify
-	    stderr2out = { class: {stderr: '_stdout'} }
-	    _.system ['gpg', '--verify', *verify], stderr2out
-            if _.target!.include? "gpg: Can't check signature:"
-              keyid = _.target![/[RD]SA key ID (\w+)/,1]
-              if keyid
-	        _.system ['gpg', '--keyserver', 'pgpkeys.mit.edu',
-		  '--recv-keys', keyid], stderr2out
-	        _.system ['gpg', '--verify', *verify], stderr2out
-              end
-            end
-	  end
-	end
-      end
-
-      _form.buttons target: 'viewport', action: 'file.cgi', method: 'post' do
-        _ul style: 'list-style: none; padding: 0' do
-          files.each_with_index do |line,i|
-            file = line.split('/').last
-            _li do
-              _input type: :checkbox, name: "include#{i}", value: file
-              if %w(jpg).include?(file.split('.').last)
-                _img src: "/members/received/#{@dir}/#{file}"
-              else
-                _a file, href: "/members/received/#{@dir}/#{file}"
-              end
-            end
-          end
-        end
-
-        _input name: 'source', id: 'source', type: 'hidden', value: @dir
-
-        if files.length > 0
-          _input type: 'submit', name: 'action', value: 'Staple'
-        else
-          file = "#{RECEIVED}/#{@dir}"
-	  stderr2out = { class: {stderr: '_stdout'} }
-	  _.system ['gpg', '--verify', file], stderr2out
-          if _.target!.include? "gpg: Can't check signature: public key not found"
-            keyid = _.target![/[RD]SA key ID (\w+)/,1]
-            if keyid
-	      _.system ['gpg', '--keyserver', 'pgpkeys.mit.edu',
-		'--recv-keys', keyid], stderr2out
-	      _.system ['gpg', '--verify', file], stderr2out
-            end
-          end
-          open(file) {|fh| _pre fh.read} 
-        end
-
-        _script src: "jquery-1.7.2.min.js"
-        _script %{
-          // first, check all of the checkboxes
-          $('input[type=checkbox]').attr('checked', 'checked');
-
-          // on click, unclick all if all are checked.
-          $('input[type=checkbox]').mousedown(function() {
-            if (!$('input[type=checkbox]:not(:checked)').length) {
-              $('input[type=checkbox]').removeAttr('checked');
-            }
-          });
-        }
-      end
-
-    when 'cancel', 'check'
-      _h1 @action
-      check
-  
-    when 'edit cc'
-      _h1 @action
-
-      if File.exist?(PENDING_YML)
-        pending = YAML.load(open(PENDING_YML))
-
-        if @email
-          pending.each do |vars|
-            if vars['email'] == @email
-              vars['email:cc'] = @cc.strip.gsub(/\r?\n/, ", ") 
-              vars['email:bcc'] = @bcc.strip.gsub(/\r?\n/, ", ") 
-              vars.delete('email:bcc') if vars['email:bcc'].empty?
-
-              open(PENDING_YML, 'w') {|file| file.write pending.to_yaml}
-              _p.notice! "cc list for #{@email} updated"
-            end
-          end
-        end
-
-        pending.each do |vars|
-          vars = OpenStruct.new(Hash[vars.map {|k,v| [k.gsub(/\W/,'_'),v]}])
-          _h2 "email #{vars.email}"
-          _form do
-            _input name: 'email', value: vars.email, type: 'hidden'
-            _table do
-              _tr do
-                _td 'cc:'
-                _td do
-                  _textarea vars.email_cc.to_s.gsub(/,\s+/,"\n"),
-                    name: 'cc', cols: 60
-                end
-              end
-              _tr do
-                _td 'bcc:'
-                _td do
-                  _textarea vars.email_bcc.to_s.gsub(/,\s+/,"\n"),
-                    name: 'bcc', cols: 60
-                end
-              end
-            end
-            _input type: 'hidden', name: 'action', value: @action
-            _input type: 'submit', value: 'Update'
-          end
-        end
-      end
-  
-    when 'danger'
-      _h2 'Potentially dangerous content'
-      _a @link, href: '/members/received/' + @link
-
-    else
-      _h2 'Unsupported action'
-      _table border: 1, cellpadding: 10, cellspacing: 0 do
-        params.sort.each do |key, value|
-          _tr do
-            _td key
-            _td value
-          end
-        end
-      end
-    end
-
-    if ajax
-      _script src: 'jquery-1.7.2.min.js'
-
-      if @action == 'update'
-        _script src: 'jquery.timeago.js'
-        _script "$('time').timeago()"
-      end
-
-      _script %{
-        function execute_todos() {
-          var todo = $('.todo:first');
-          if (todo.length == 1) {
-            // add a spinner
-            var spinner = $('<img src="spinner.gif"/>');
-            todo.after(spinner);
-
-            // params = cmd plus all of the data-* attributes
-            var params = {cmd: todo.text()};
-            for (var i=0; i<todo[0].attributes.length; i++) {
-              var attr = todo[0].attributes[i];
-              if (attr.name.match("^data-")) {
-                params[attr.name.substr(5)] = attr.value;
-              }
-            }
-
-            // issue request
-            $.post(#{ENV['SCRIPT_NAME'].inspect}, params, function(response) {
-              var replacement = $(response.html);
-              if (replacement.length > 0) todo.replaceWith(replacement);
-              if (replacement.filter('._stderr,._traceback').length > 0) {
-                if (!confirm("Error detected.  Continue?")) return;
-              }
-              execute_todos();
-            }, 'json').done(function(data, textStatus, jqXHR) {
-              if (data.toString() == '') {
-                var replacement = $(
-                  '<pre class="_stdin">' + params.cmd + '</pre>' +
-                  '<pre class="_stderr"><em>no response</em></pre>'
-                );
-                todo.replaceWith(replacement);
-                alert("Error detected.  Processing terminated.");
-              }
-            }).fail(function(jqXHR, textStatus, errorThrown) {
-              if (errorThrown == '') errorThrown = 'no response';
-              var replacement = $(
-                '<pre class="_stdin">' + params.cmd + '</pre>' +
-                '<pre class="_stderr">' + textStatus+': '+errorThrown + '</pre>'
-              );
-              todo.replaceWith(replacement);
-              if (!confirm("Error detected.  Continue?")) return;
-              execute_todos();
-            }).always(function() {
-              spinner.remove();
-            });
-          } else {
-            parent.frames[0].location.reload();
-          }
-        }
-        execute_todos();
-      }
-    end
-  end
-end
diff --git a/www/secretary/workbench/grant.erb b/www/secretary/workbench/grant.erb
deleted file mode 100644
index 4f91d0f..0000000
--- a/www/secretary/workbench/grant.erb
+++ /dev/null
@@ -1,13 +0,0 @@
-to:   <%= gname.inspect %> <<%= gemail %>>
-from: <%= from %>
-cc:   secretary@apache.org
-bcc:  <%= bcc %>
-subject: Your Grant sent to Apache Secretary
-
-Dear <%= gname %>,
-
-This message acknowledges receipt of the following document, which has been filed in the Apache Software Foundation records:
-
-  <%= commit_message %>
-
-<%= sig %>
diff --git a/www/secmail/helpers.rb b/www/secretary/workbench/helpers.rb
similarity index 100%
rename from www/secmail/helpers.rb
rename to www/secretary/workbench/helpers.rb
diff --git a/www/secretary/workbench/icla.erb b/www/secretary/workbench/icla.erb
deleted file mode 100644
index 772da2e..0000000
--- a/www/secretary/workbench/icla.erb
+++ /dev/null
@@ -1,19 +0,0 @@
-to:   <%= pubname.inspect %> <<%= email %>>
-from: <%= from %>
-cc:   secretary@apache.org
-bcc:  <%= bcc %>
-subject: Your ICLA sent to Apache Secretary
-
-Dear <%= pubname %>,
-
-This message acknowledges receipt of your ICLA, which has been filed in the Apache Software Foundation records.
-
-If you have been invited as a committer, please advise the project PMC that your ICLA has been filed.
-
-If you have not been invited, please refer to http://www.apache.org/foundation/how-it-works.html#developers
-for more information about roles at Apache.
-
-Warm Regards,
-
-<%= sig %>
-
diff --git a/www/secretary/workbench/incomplete.erb b/www/secretary/workbench/incomplete.erb
deleted file mode 100644
index 9bcb677..0000000
--- a/www/secretary/workbench/incomplete.erb
+++ /dev/null
@@ -1,16 +0,0 @@
-to:   <%= realname.inspect %> <<%= nemail %>>
-from: <%= from %>
-cc:   secretary@apache.org
-bcc:  <%= bcc %>
-subject: Unsigned Document
-
-Dear <%= realname %>,
-
-We received this document but it appears to be incomplete.
-Please fill all required fields and resubmit all pages
-to secretary@apache.org
-http://www.apache.org/licenses/#submitting
-
-Warm Regards,
-
-<%= sig %>
diff --git a/www/secretary/workbench/index.html b/www/secretary/workbench/index.html
deleted file mode 100644
index f82a156..0000000
--- a/www/secretary/workbench/index.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<!DOCTYPE html>
-<html xmlns="http://www.w3.org/1999/xhtml">
-  <head>
-    <title>ASF Secretary-Assistant Workbench</title>
-  </head>
-  <frameset cols="20%, 60%">
-    <frame src="worklist.cgi"/>
-    <frame name="viewport"/>
-  </frameset>
-</html>
diff --git a/www/secretary/workbench/jquery-1.7.2.min.js b/www/secretary/workbench/jquery-1.7.2.min.js
deleted file mode 100644
index 16ad06c..0000000
--- a/www/secretary/workbench/jquery-1.7.2.min.js
+++ /dev/null
@@ -1,4 +0,0 @@
-/*! jQuery v1.7.2 jquery.com | jquery.org/license */
-(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"<!doctype html>":"")+"<html><body>"),cl.close();d=cl.createElement(a),cl.bod [...]
-a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){ [...]
-.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagNa [...]
\ No newline at end of file
diff --git a/www/secretary/workbench/jquery.timeago.js b/www/secretary/workbench/jquery.timeago.js
deleted file mode 100644
index 1417747..0000000
--- a/www/secretary/workbench/jquery.timeago.js
+++ /dev/null
@@ -1,152 +0,0 @@
-/**
- * Timeago is a jQuery plugin that makes it easy to support automatically
- * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
- *
- * @name timeago
- * @version 0.11.3
- * @requires jQuery v1.2.3+
- * @author Ryan McGeary
- * @license MIT License - http://www.opensource.org/licenses/mit-license.php
- *
- * For usage and examples, visit:
- * http://timeago.yarp.com/
- *
- * Copyright (c) 2008-2012, Ryan McGeary (ryan -[at]- mcgeary [*dot*] org)
- */
-(function($) {
-  $.timeago = function(timestamp) {
-    if (timestamp instanceof Date) {
-      return inWords(timestamp);
-    } else if (typeof timestamp === "string") {
-      return inWords($.timeago.parse(timestamp));
-    } else if (typeof timestamp === "number") {
-      return inWords(new Date(timestamp));
-    } else {
-      return inWords($.timeago.datetime(timestamp));
-    }
-  };
-  var $t = $.timeago;
-
-  $.extend($.timeago, {
-    settings: {
-      refreshMillis: 60000,
-      allowFuture: false,
-      strings: {
-        prefixAgo: null,
-        prefixFromNow: null,
-        suffixAgo: "ago",
-        suffixFromNow: "from now",
-        seconds: "less than a minute",
-        minute: "about a minute",
-        minutes: "%d minutes",
-        hour: "about an hour",
-        hours: "about %d hours",
-        day: "a day",
-        days: "%d days",
-        month: "about a month",
-        months: "%d months",
-        year: "about a year",
-        years: "%d years",
-        wordSeparator: " ",
-        numbers: []
-      }
-    },
-    inWords: function(distanceMillis) {
-      var $l = this.settings.strings;
-      var prefix = $l.prefixAgo;
-      var suffix = $l.suffixAgo;
-      if (this.settings.allowFuture) {
-        if (distanceMillis < 0) {
-          prefix = $l.prefixFromNow;
-          suffix = $l.suffixFromNow;
-        }
-      }
-
-      var seconds = Math.abs(distanceMillis) / 1000;
-      var minutes = seconds / 60;
-      var hours = minutes / 60;
-      var days = hours / 24;
-      var years = days / 365;
-
-      function substitute(stringOrFunction, number) {
-        var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction;
-        var value = ($l.numbers && $l.numbers[number]) || number;
-        return string.replace(/%d/i, value);
-      }
-
-      var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
-        seconds < 90 && substitute($l.minute, 1) ||
-        minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
-        minutes < 90 && substitute($l.hour, 1) ||
-        hours < 24 && substitute($l.hours, Math.round(hours)) ||
-        hours < 42 && substitute($l.day, 1) ||
-        days < 30 && substitute($l.days, Math.round(days)) ||
-        days < 45 && substitute($l.month, 1) ||
-        days < 365 && substitute($l.months, Math.round(days / 30)) ||
-        years < 1.5 && substitute($l.year, 1) ||
-        substitute($l.years, Math.round(years));
-
-      var separator = $l.wordSeparator === undefined ?  " " : $l.wordSeparator;
-      return $.trim([prefix, words, suffix].join(separator));
-    },
-    parse: function(iso8601) {
-      var s = $.trim(iso8601);
-      s = s.replace(/\.\d\d\d+/,""); // remove milliseconds
-      s = s.replace(/-/,"/").replace(/-/,"/");
-      s = s.replace(/T/," ").replace(/Z/," UTC");
-      s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400
-      return new Date(s);
-    },
-    datetime: function(elem) {
-      var iso8601 = $t.isTime(elem) ? $(elem).attr("datetime") : $(elem).attr("title");
-      return $t.parse(iso8601);
-    },
-    isTime: function(elem) {
-      // jQuery's `is()` doesn't play well with HTML5 in IE
-      return $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time");
-    }
-  });
-
-  $.fn.timeago = function() {
-    var self = this;
-    self.each(refresh);
-
-    var $s = $t.settings;
-    if ($s.refreshMillis > 0) {
-      setInterval(function() { self.each(refresh); }, $s.refreshMillis);
-    }
-    return self;
-  };
-
-  function refresh() {
-    var data = prepareData(this);
-    if (!isNaN(data.datetime)) {
-      $(this).text(inWords(data.datetime));
-    }
-    return this;
-  }
-
-  function prepareData(element) {
-    element = $(element);
-    if (!element.data("timeago")) {
-      element.data("timeago", { datetime: $t.datetime(element) });
-      var text = $.trim(element.text());
-      if (text.length > 0 && !($t.isTime(element) && element.attr("title"))) {
-        element.attr("title", text);
-      }
-    }
-    return element.data("timeago");
-  }
-
-  function inWords(date) {
-    return $t.inWords(distance(date));
-  }
-
-  function distance(date) {
-    return (new Date().getTime() - date.getTime());
-  }
-
-  // fix for IE6 suckage
-  document.createElement("abbr");
-  document.createElement("time");
-}(jQuery));
diff --git a/www/secretary/workbench/local_paths.rb b/www/secretary/workbench/local_paths.rb
deleted file mode 100644
index c081a17..0000000
--- a/www/secretary/workbench/local_paths.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-require 'fileutils'
-
-# attempt to determine where 'HOME' is
-unless ENV['HOME']
-  ENV['HOME'] = $1 if ENV['SCRIPT_FILENAME'] =~ /(.*?)\/public_html\//
-  ENV['HOME'] = $1 if ENV['SCRIPT_FILENAME'] =~ /(.*?)\/Sites\//
-end
-
-# override home for whimsy
-ENV['HOME'] = '/var/www' if `hostname`.chomp == 'whimsy-vm3'
-
-# look for ~/.secassist or local_paths.yml (in that order)
-config = File.expand_path('~/.secassist')
-config = 'local_paths.yml' unless File.exist?(config)
-
-# set constants based on the configuration file
-require 'yaml'
-YAML.load(open(config).read).each do |key, value|
-  FileUtils.mkdir_p value unless File.exist? value
-
-  Object.const_set key.upcase,
-    File.realpath(File.expand_path(value).untaint).untaint
-end
-
-# pending file
-PENDING_YML = File.join(RECEIVED, 'pending.yml')
-COMPLETED_YML = File.join(RECEIVED, 'completed.yml')
-
-# svn >= 1.5 requires a trailing '@' if the file name contains an '@'
-# http://stackoverflow.com/questions/1985203/why-subversion-skips-files-which-contain-the-symbol
-def svn_at name
-  svn_version = `svn --version --quiet`.chomp.split('.').map {|s| s.to_i}
-  if (svn_version <=> [1,6,4]) < 1
-    ''
-  elsif name.include? '@'
-    '@'
-  else
-    ''
-  end
-end
diff --git a/www/secretary/workbench/local_paths.yml b/www/secretary/workbench/local_paths.yml
deleted file mode 100644
index 4b08b9c..0000000
--- a/www/secretary/workbench/local_paths.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-meeting:    /srv/secretary/workbench/foundation/Meetings/20170328
-foundation: /srv/secretary/workbench/foundation
-officers:   /srv/secretary/workbench/foundation/officers
-documents:  /srv/secretary/workbench/documents
-received:   /srv/secretary/workbench/documents/received
-subreq:     /srv/secretary/workbench/subreq
diff --git a/www/secretary/workbench/mem.erb b/www/secretary/workbench/mem.erb
deleted file mode 100644
index 1844415..0000000
--- a/www/secretary/workbench/mem.erb
+++ /dev/null
@@ -1,15 +0,0 @@
-to:   <%= realname.inspect %> <<%= memail %>>
-from: <%= from %>
-cc:   secretary@apache.org
-bcc:  <%= bcc %>
-subject: Your ASF Membership Application
-
-Dear <%= realname %>,
-
-This message acknowledges receipt of your membership application, which has been filed in the Apache Software Foundation records.
-
-You now have access to the foundation repository and will be subscribed to the members@apache.org mailing list shortly.
-
-Warm Regards,
-
-<%= sig %>
diff --git a/www/secmail/models/attachment.rb b/www/secretary/workbench/models/attachment.rb
similarity index 100%
rename from www/secmail/models/attachment.rb
rename to www/secretary/workbench/models/attachment.rb
diff --git a/www/secmail/models/events.rb b/www/secretary/workbench/models/events.rb
similarity index 100%
rename from www/secmail/models/events.rb
rename to www/secretary/workbench/models/events.rb
diff --git a/www/secmail/models/mailbox.rb b/www/secretary/workbench/models/mailbox.rb
similarity index 100%
rename from www/secmail/models/mailbox.rb
rename to www/secretary/workbench/models/mailbox.rb
diff --git a/www/secmail/models/message.rb b/www/secretary/workbench/models/message.rb
similarity index 100%
rename from www/secmail/models/message.rb
rename to www/secretary/workbench/models/message.rb
diff --git a/www/secmail/models/safetemp.rb b/www/secretary/workbench/models/safetemp.rb
similarity index 100%
rename from www/secmail/models/safetemp.rb
rename to www/secretary/workbench/models/safetemp.rb
diff --git a/www/secretary/workbench/nda.erb b/www/secretary/workbench/nda.erb
deleted file mode 100644
index 5d57bac..0000000
--- a/www/secretary/workbench/nda.erb
+++ /dev/null
@@ -1,13 +0,0 @@
-to:   <%= realname.inspect %> <<%= nemail %>>
-from: <%= from %>
-cc:   secretary@apache.org
-bcc:  <%= bcc %>
-subject: Your JCP NDA
-
-Dear <%= realname %>,
-
-This message acknowledges receipt of your NDA, which has been filed in the Apache Software Foundation records.
-
-Warm Regards,
-
-<%= sig %>
diff --git a/www/secmail/parsemail.rb b/www/secretary/workbench/parsemail.rb
similarity index 100%
rename from www/secmail/parsemail.rb
rename to www/secretary/workbench/parsemail.rb
diff --git a/www/secmail/personalize.rb b/www/secretary/workbench/personalize.rb
similarity index 100%
rename from www/secmail/personalize.rb
rename to www/secretary/workbench/personalize.rb
diff --git a/www/secmail/public/HOWTO.html b/www/secretary/workbench/public/HOWTO.html
similarity index 94%
rename from www/secmail/public/HOWTO.html
rename to www/secretary/workbench/public/HOWTO.html
index 6f2f0b8..fc01a2d 100644
--- a/www/secmail/public/HOWTO.html
+++ b/www/secretary/workbench/public/HOWTO.html
@@ -101,10 +101,11 @@
     <a href="https://gitbox.apache.org/repos/asf?p=whimsy.git">ASF</a> and
     <a href="https://github.com/apache/whimsy/">github</a>.</p>
 
-    <p>The secmail application can be found in the
-    <a href="https://github.com/apache/whimsy/tree/master/www/secmail">www/secmail</a>
+    <p>The secretary workbench application can be found in the
+    <a
+href="https://github.com/apache/whimsy/tree/master/www/secretary/workbench">www/secretary/workbench</a>
     subdirectory</a>.
 
-    <p>The source to this page is in secmail/public/HOWTO.html</p>
+    <p>The source to this page is in secretary/workbench/public/HOWTO.html</p>
   </body>
 </html>
diff --git a/www/secmail/public/assets/bootstrap-min.css b/www/secretary/workbench/public/assets/bootstrap-min.css
similarity index 100%
rename from www/secmail/public/assets/bootstrap-min.css
rename to www/secretary/workbench/public/assets/bootstrap-min.css
diff --git a/www/secmail/public/assets/bootstrap-min.js b/www/secretary/workbench/public/assets/bootstrap-min.js
similarity index 100%
rename from www/secmail/public/assets/bootstrap-min.js
rename to www/secretary/workbench/public/assets/bootstrap-min.js
diff --git a/www/secmail/public/assets/jquery-min.js b/www/secretary/workbench/public/assets/jquery-min.js
similarity index 100%
rename from www/secmail/public/assets/jquery-min.js
rename to www/secretary/workbench/public/assets/jquery-min.js
diff --git a/www/secmail/public/assets/react-min.js b/www/secretary/workbench/public/assets/react-min.js
similarity index 100%
rename from www/secmail/public/assets/react-min.js
rename to www/secretary/workbench/public/assets/react-min.js
diff --git a/www/secmail/public/fetch.js b/www/secretary/workbench/public/fetch.js
similarity index 100%
rename from www/secmail/public/fetch.js
rename to www/secretary/workbench/public/fetch.js
diff --git a/www/secmail/public/secmail.css b/www/secretary/workbench/public/secmail.css
similarity index 100%
rename from www/secmail/public/secmail.css
rename to www/secretary/workbench/public/secmail.css
diff --git a/www/secretary/workbench/spinner.gif b/www/secretary/workbench/public/spinner.gif
similarity index 100%
rename from www/secretary/workbench/spinner.gif
rename to www/secretary/workbench/public/spinner.gif
diff --git a/www/secmail/public/tasklist.js b/www/secretary/workbench/public/tasklist.js
similarity index 100%
rename from www/secmail/public/tasklist.js
rename to www/secretary/workbench/public/tasklist.js
diff --git a/www/secretary/workbench/publickey.erb b/www/secretary/workbench/publickey.erb
deleted file mode 100644
index 8ab0090..0000000
--- a/www/secretary/workbench/publickey.erb
+++ /dev/null
@@ -1,16 +0,0 @@
-to:   <%= realname.inspect %> <<%= pemail %>>
-from: <%= from %>
-cc:   secretary@apache.org
-bcc:  <%= bcc %>
-subject: Missing public key
-
-Dear <%= realname %>,
-
-We received this document but cannot verify your signature without
-your public key. Please upload your public key to pgpkeys.mit.edu.
-
-http://www.apache.org/licenses/#submitting
-
-Warm Regards,
-
-<%= sig %>
diff --git a/www/secretary/workbench/secmail.rb b/www/secretary/workbench/secmail.rb
deleted file mode 100644
index 83f2a26..0000000
--- a/www/secretary/workbench/secmail.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-require 'mail'
-
-Mail.defaults do
-  if $USER == 'clr'
-
-    @from = 'Craig L Russell <secretary@apache.org>'
-    @sig = %{
-      -- Craig L Russell
-      Secretary, Apache Software Foundation
-    }
-
-  elsif $USER == 'jcarman'
-
-    @from = 'James Carman <jcarman@apache.org>'
-    @sig = %{
-      -- James Carman
-      Apache Software Foundation Secretarial Team
-    }
-
-  elsif $USER == 'rubys'
-
-    @from = 'Sam Ruby <rubys@apache.org>'
-    @sig = %{
-      -- Sam Ruby
-      Apache Software Foundation Secretarial Team
-    }
-
-  elsif $USER == 'jim'
-
-    @from = 'Jim Jagielski <jim@apache.org>'
-    @sig = %{
-      -- Jim Jagielski
-      Apache Software Foundation Secretarial Team
-    }
-
-  elsif $USER == 'sanders'
-
-    @from = 'Scott Sander <sanders@apache.org>'
-    @sig = %{
-      -- Scott Sander
-      Apache Software Foundation Secretarial Team
-    }
-
-  elsif $USER == 'mnour'
-
-    @from = 'Mohammad Nour El-Din <mnour@apache.org>'
-    @sig = %{
-      -- Mohammad Nour El-Din
-      Apache Software Foundation Secretarial Team
-    }
-  end
-end
diff --git a/www/secmail/server.rb b/www/secretary/workbench/server.rb
similarity index 100%
rename from www/secmail/server.rb
rename to www/secretary/workbench/server.rb
diff --git a/www/secmail/tasks.rb b/www/secretary/workbench/tasks.rb
similarity index 100%
rename from www/secmail/tasks.rb
rename to www/secretary/workbench/tasks.rb
diff --git a/www/secmail/templates/acreq.erb b/www/secretary/workbench/templates/acreq.erb
similarity index 100%
rename from www/secmail/templates/acreq.erb
rename to www/secretary/workbench/templates/acreq.erb
diff --git a/www/secmail/templates/ccla.erb b/www/secretary/workbench/templates/ccla.erb
similarity index 100%
rename from www/secmail/templates/ccla.erb
rename to www/secretary/workbench/templates/ccla.erb
diff --git a/www/secmail/templates/grant.erb b/www/secretary/workbench/templates/grant.erb
similarity index 100%
rename from www/secmail/templates/grant.erb
rename to www/secretary/workbench/templates/grant.erb
diff --git a/www/secmail/templates/icla-account-requested.erb b/www/secretary/workbench/templates/icla-account-requested.erb
similarity index 100%
rename from www/secmail/templates/icla-account-requested.erb
rename to www/secretary/workbench/templates/icla-account-requested.erb
diff --git a/www/secmail/templates/icla-pmc-notified.erb b/www/secretary/workbench/templates/icla-pmc-notified.erb
similarity index 100%
rename from www/secmail/templates/icla-pmc-notified.erb
rename to www/secretary/workbench/templates/icla-pmc-notified.erb
diff --git a/www/secmail/templates/icla.erb b/www/secretary/workbench/templates/icla.erb
similarity index 100%
rename from www/secmail/templates/icla.erb
rename to www/secretary/workbench/templates/icla.erb
diff --git a/www/secmail/templates/incomplete.erb b/www/secretary/workbench/templates/incomplete.erb
similarity index 100%
rename from www/secmail/templates/incomplete.erb
rename to www/secretary/workbench/templates/incomplete.erb
diff --git a/www/secmail/templates/mem.erb b/www/secretary/workbench/templates/mem.erb
similarity index 100%
rename from www/secmail/templates/mem.erb
rename to www/secretary/workbench/templates/mem.erb
diff --git a/www/secmail/templates/nda.erb b/www/secretary/workbench/templates/nda.erb
similarity index 100%
rename from www/secmail/templates/nda.erb
rename to www/secretary/workbench/templates/nda.erb
diff --git a/www/secmail/templates/pubkey.erb b/www/secretary/workbench/templates/pubkey.erb
similarity index 100%
rename from www/secmail/templates/pubkey.erb
rename to www/secretary/workbench/templates/pubkey.erb
diff --git a/www/secmail/templates/unsigned.erb b/www/secretary/workbench/templates/unsigned.erb
similarity index 100%
rename from www/secmail/templates/unsigned.erb
rename to www/secretary/workbench/templates/unsigned.erb
diff --git a/www/secmail/tmp/.gitignore b/www/secretary/workbench/tmp/.gitignore
similarity index 100%
rename from www/secmail/tmp/.gitignore
rename to www/secretary/workbench/tmp/.gitignore
diff --git a/www/secretary/workbench/unsigned.erb b/www/secretary/workbench/unsigned.erb
deleted file mode 100644
index 191e578..0000000
--- a/www/secretary/workbench/unsigned.erb
+++ /dev/null
@@ -1,15 +0,0 @@
-to:   <%= realname.inspect %> <<%= nemail %>>
-from: <%= from %>
-cc:   secretary@apache.org
-bcc:  <%= bcc %>
-subject: Unsigned Document
-
-Dear <%= realname %>,
-
-We received this document but it appears to be unsigned.
-Please sign and resubmit to secretary@apache.org
-http://www.apache.org/licenses/#submitting
-
-Warm Regards,
-
-<%= sig %>
diff --git a/www/secretary/workbench/upload.cgi b/www/secretary/workbench/upload.cgi
deleted file mode 100755
index f35c1a2..0000000
--- a/www/secretary/workbench/upload.cgi
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/usr/bin/env ruby
-require 'wunderbar'
-require 'mail'
-
-_html do
-  _head do
-    _title 'Upload an email'
-    _style %{
-      pre {font-weight: bold; margin: 0} 
-      pre._stdin  {color: #C000C0; margin-top: 1em} 
-      pre._stdout {color: #000} 
-      pre._stderr {color: #F00} 
-    }
-  end
-
-  _body do
-    _h2 'Upload an email'
-    _form method: 'post', enctype: 'multipart/form-data' do
-      _input type: 'file', name: 'file'
-      _input type: 'submit', value: 'send'
-    end
-
-    if @file
-      Dir.chdir('/var/tools/secretary/secmail') do
-        # write email to mailbox
-        upload = @file.read
-        mail = Mail.new {from upload[/^From: (.*?)[\r\n]/i, 1]}
-        time = Time.now.asctime
-        File.open('mailbox', 'w') do |file|
-          file.write "From #{mail.from.first}  #{time}\r\n"
-          file.write upload
-        end
-
-        # allow emails previously processed to be processed again
-        Dir['tally/*'].each {|file| File.unlink file.untaint}
-
-        _.system 'python secmail.py' 
-      end
-
-      Dir.chdir('/var/tools/secretary/documents') do
-        # update received
-        _.system 'svn update received' 
-      end
-
-      _script 'parent.frames[0].location.reload()'
-    end
-  end
-end
diff --git a/www/secmail/views/actions/burst.json.rb b/www/secretary/workbench/views/actions/burst.json.rb
similarity index 100%
rename from www/secmail/views/actions/burst.json.rb
rename to www/secretary/workbench/views/actions/burst.json.rb
diff --git a/www/secmail/views/actions/ccla.json.rb b/www/secretary/workbench/views/actions/ccla.json.rb
similarity index 100%
rename from www/secmail/views/actions/ccla.json.rb
rename to www/secretary/workbench/views/actions/ccla.json.rb
diff --git a/www/secmail/views/actions/check-id.json.rb b/www/secretary/workbench/views/actions/check-id.json.rb
similarity index 100%
rename from www/secmail/views/actions/check-id.json.rb
rename to www/secretary/workbench/views/actions/check-id.json.rb
diff --git a/www/secmail/views/actions/check-mail.json.rb b/www/secretary/workbench/views/actions/check-mail.json.rb
similarity index 100%
rename from www/secmail/views/actions/check-mail.json.rb
rename to www/secretary/workbench/views/actions/check-mail.json.rb
diff --git a/www/secmail/views/actions/check-signature.json.rb b/www/secretary/workbench/views/actions/check-signature.json.rb
similarity index 100%
rename from www/secmail/views/actions/check-signature.json.rb
rename to www/secretary/workbench/views/actions/check-signature.json.rb
diff --git a/www/secmail/views/actions/delete-attachment.json.rb b/www/secretary/workbench/views/actions/delete-attachment.json.rb
similarity index 100%
rename from www/secmail/views/actions/delete-attachment.json.rb
rename to www/secretary/workbench/views/actions/delete-attachment.json.rb
diff --git a/www/secmail/views/actions/drop.json.rb b/www/secretary/workbench/views/actions/drop.json.rb
similarity index 100%
rename from www/secmail/views/actions/drop.json.rb
rename to www/secretary/workbench/views/actions/drop.json.rb
diff --git a/www/secmail/views/actions/forward.json.rb b/www/secretary/workbench/views/actions/forward.json.rb
similarity index 100%
rename from www/secmail/views/actions/forward.json.rb
rename to www/secretary/workbench/views/actions/forward.json.rb
diff --git a/www/secmail/views/actions/grant.json.rb b/www/secretary/workbench/views/actions/grant.json.rb
similarity index 100%
rename from www/secmail/views/actions/grant.json.rb
rename to www/secretary/workbench/views/actions/grant.json.rb
diff --git a/www/secmail/views/actions/icla.json.rb b/www/secretary/workbench/views/actions/icla.json.rb
similarity index 100%
rename from www/secmail/views/actions/icla.json.rb
rename to www/secretary/workbench/views/actions/icla.json.rb
diff --git a/www/secmail/views/actions/incomplete.json.rb b/www/secretary/workbench/views/actions/incomplete.json.rb
similarity index 100%
rename from www/secmail/views/actions/incomplete.json.rb
rename to www/secretary/workbench/views/actions/incomplete.json.rb
diff --git a/www/secmail/views/actions/memapp.json.rb b/www/secretary/workbench/views/actions/memapp.json.rb
similarity index 100%
rename from www/secmail/views/actions/memapp.json.rb
rename to www/secretary/workbench/views/actions/memapp.json.rb
diff --git a/www/secmail/views/actions/pdfize.json.rb b/www/secretary/workbench/views/actions/pdfize.json.rb
similarity index 100%
rename from www/secmail/views/actions/pdfize.json.rb
rename to www/secretary/workbench/views/actions/pdfize.json.rb
diff --git a/www/secmail/views/actions/pubkey.json.rb b/www/secretary/workbench/views/actions/pubkey.json.rb
similarity index 100%
rename from www/secmail/views/actions/pubkey.json.rb
rename to www/secretary/workbench/views/actions/pubkey.json.rb
diff --git a/www/secmail/views/actions/rotate-attachment.json.rb b/www/secretary/workbench/views/actions/rotate-attachment.json.rb
similarity index 100%
rename from www/secmail/views/actions/rotate-attachment.json.rb
rename to www/secretary/workbench/views/actions/rotate-attachment.json.rb
diff --git a/www/secmail/views/actions/unsigned.json.rb b/www/secretary/workbench/views/actions/unsigned.json.rb
similarity index 100%
rename from www/secmail/views/actions/unsigned.json.rb
rename to www/secretary/workbench/views/actions/unsigned.json.rb
diff --git a/www/secmail/views/actions/update-mail.json.rb b/www/secretary/workbench/views/actions/update-mail.json.rb
similarity index 100%
rename from www/secmail/views/actions/update-mail.json.rb
rename to www/secretary/workbench/views/actions/update-mail.json.rb
diff --git a/www/secmail/views/app.js.rb b/www/secretary/workbench/views/app.js.rb
similarity index 100%
rename from www/secmail/views/app.js.rb
rename to www/secretary/workbench/views/app.js.rb
diff --git a/www/secmail/views/asciize.js.rb b/www/secretary/workbench/views/asciize.js.rb
similarity index 100%
rename from www/secmail/views/asciize.js.rb
rename to www/secretary/workbench/views/asciize.js.rb
diff --git a/www/secmail/views/body.html.rb b/www/secretary/workbench/views/body.html.rb
similarity index 100%
rename from www/secmail/views/body.html.rb
rename to www/secretary/workbench/views/body.html.rb
diff --git a/www/secmail/views/check-signature.js.rb b/www/secretary/workbench/views/check-signature.js.rb
similarity index 100%
rename from www/secmail/views/check-signature.js.rb
rename to www/secretary/workbench/views/check-signature.js.rb
diff --git a/www/secmail/views/context-menu.js.rb b/www/secretary/workbench/views/context-menu.js.rb
similarity index 100%
rename from www/secmail/views/context-menu.js.rb
rename to www/secretary/workbench/views/context-menu.js.rb
diff --git a/www/secmail/views/danger.html.rb b/www/secretary/workbench/views/danger.html.rb
similarity index 100%
rename from www/secmail/views/danger.html.rb
rename to www/secretary/workbench/views/danger.html.rb
diff --git a/www/secmail/views/forms/ccla.js.rb b/www/secretary/workbench/views/forms/ccla.js.rb
similarity index 100%
rename from www/secmail/views/forms/ccla.js.rb
rename to www/secretary/workbench/views/forms/ccla.js.rb
diff --git a/www/secmail/views/forms/forward.js.rb b/www/secretary/workbench/views/forms/forward.js.rb
similarity index 100%
rename from www/secmail/views/forms/forward.js.rb
rename to www/secretary/workbench/views/forms/forward.js.rb
diff --git a/www/secmail/views/forms/grant.js.rb b/www/secretary/workbench/views/forms/grant.js.rb
similarity index 100%
rename from www/secmail/views/forms/grant.js.rb
rename to www/secretary/workbench/views/forms/grant.js.rb
diff --git a/www/secmail/views/forms/icla.js.rb b/www/secretary/workbench/views/forms/icla.js.rb
similarity index 100%
rename from www/secmail/views/forms/icla.js.rb
rename to www/secretary/workbench/views/forms/icla.js.rb
diff --git a/www/secmail/views/forms/memapp.js.rb b/www/secretary/workbench/views/forms/memapp.js.rb
similarity index 100%
rename from www/secmail/views/forms/memapp.js.rb
rename to www/secretary/workbench/views/forms/memapp.js.rb
diff --git a/www/secmail/views/forms/nda.js.rb b/www/secretary/workbench/views/forms/nda.js.rb
similarity index 100%
rename from www/secmail/views/forms/nda.js.rb
rename to www/secretary/workbench/views/forms/nda.js.rb
diff --git a/www/secmail/views/headers.html.rb b/www/secretary/workbench/views/headers.html.rb
similarity index 100%
rename from www/secmail/views/headers.html.rb
rename to www/secretary/workbench/views/headers.html.rb
diff --git a/www/secmail/views/http.js.rb b/www/secretary/workbench/views/http.js.rb
similarity index 100%
rename from www/secmail/views/http.js.rb
rename to www/secretary/workbench/views/http.js.rb
diff --git a/www/secmail/views/index.html.rb b/www/secretary/workbench/views/index.html.rb
similarity index 100%
rename from www/secmail/views/index.html.rb
rename to www/secretary/workbench/views/index.html.rb
diff --git a/www/secmail/views/index.js.rb b/www/secretary/workbench/views/index.js.rb
similarity index 100%
rename from www/secmail/views/index.js.rb
rename to www/secretary/workbench/views/index.js.rb
diff --git a/www/secmail/views/index.json.rb b/www/secretary/workbench/views/index.json.rb
similarity index 100%
rename from www/secmail/views/index.json.rb
rename to www/secretary/workbench/views/index.json.rb
diff --git a/www/secmail/views/memapp.json.rb b/www/secretary/workbench/views/memapp.json.rb
similarity index 100%
rename from www/secmail/views/memapp.json.rb
rename to www/secretary/workbench/views/memapp.json.rb
diff --git a/www/secmail/views/message.html.rb b/www/secretary/workbench/views/message.html.rb
similarity index 100%
rename from www/secmail/views/message.html.rb
rename to www/secretary/workbench/views/message.html.rb
diff --git a/www/secmail/views/parts.html.rb b/www/secretary/workbench/views/parts.html.rb
similarity index 100%
rename from www/secmail/views/parts.html.rb
rename to www/secretary/workbench/views/parts.html.rb
diff --git a/www/secmail/views/parts.js.rb b/www/secretary/workbench/views/parts.js.rb
similarity index 100%
rename from www/secmail/views/parts.js.rb
rename to www/secretary/workbench/views/parts.js.rb
diff --git a/www/secmail/views/status.js.rb b/www/secretary/workbench/views/status.js.rb
similarity index 100%
rename from www/secmail/views/status.js.rb
rename to www/secretary/workbench/views/status.js.rb
diff --git a/www/secmail/views/tasklist.html.rb b/www/secretary/workbench/views/tasklist.html.rb
similarity index 100%
rename from www/secmail/views/tasklist.html.rb
rename to www/secretary/workbench/views/tasklist.html.rb
diff --git a/www/secretary/workbench/worklist.cgi b/www/secretary/workbench/worklist.cgi
deleted file mode 100755
index aff7923..0000000
--- a/www/secretary/workbench/worklist.cgi
+++ /dev/null
@@ -1,658 +0,0 @@
-#!/usr/bin/env ruby
-require 'wunderbar'
-require 'yaml'
-
-DOCTYPES = %w{icla grant ccla nda other incomplete unsigned publickey}
-
-_html do
-  _head_ do
-    _title 'worklist'
-    _link rel: 'stylesheet', type: "text/css", href: "worklist.css"
-    _script src: "jquery-1.7.2.min.js"
-    _script src: 'worklist.js'
-  end
-
-  _body? do
-
-    _pre do
-      _ `date` + "\n"
-    end
-
-    if `which svn`.empty?
-      _h2_.warn 'Unable to locate svn'
-      _p 'Search PATH used:'
-      _ul do
-        ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
-          _li { _code path }
-        end
-      end
-    end
-
-    begin
-      begin
-        require './local_paths'
-        DOCTYPES.insert(-1, 'mem') if defined?(MEETING)
-      rescue
-        _h2_ 'Syntax error in local_paths.yml'
-        raise 
-      end
-
-      files = []
-      ENV['LANG']||="en_US.UTF-8"
-      if defined?(MEETING)
-        IO.popen("svn st #{FOUNDATION}/members.txt").each do |line|
-          files << line.sub(FOUNDATION, 'foundation')
-        end
-        IO.popen("svn st #{MEETING}").each do |line|
-          files << line.sub(MEETING, 'meeting')
-        end
-      end
-      IO.popen("svn st #{OFFICERS}").each do |line|
-        files << line.sub(OFFICERS, 'officers')
-      end
-      IO.popen("svn st #{DOCUMENTS}").each do |line| 
-        files << line.sub(DOCUMENTS, 'documents')
-      end
-
-      message = 'spam'
-
-      if File.exist? PENDING_YML
-        pending = YAML.load(open(PENDING_YML))
-        if pending.size == 1
-          pending = pending.first
-          if pending['doctype'] == 'icla'
-            message = "ICLA from #{pending['realname']}"
-          elsif pending['doctype'] == 'ccla'
-            message = "CCLA from #{pending['company']}"
-
-            unless pending['employees'].nil? or pending['employees'].empty?
-              message += " for "
-              message += pending['employees'].strip.gsub(/\s*\n\s*/, ', ')
-            end
-          elsif pending['doctype'] == 'grant'
-            message = "Grant from #{pending['from']}"
-          elsif pending['doctype'] == 'mem'
-            message = "Membership Application from #{pending['realname']}"
-          elsif pending['doctype'] == 'nda'
-            message = "NDA for #{pending['realname']}"
-          elsif pending['doctype'] == 'incomplete'
-            message = "Incomplete document received from #{pending['iname']}"
-          elsif pending['doctype'] == 'unsigned'
-            message = "Unsigned document received from #{pending['uname']}"
-          elsif pending['doctype'] == 'publickey'
-            message = "Public key not found for #{pending['pname']}"
-          end
-        end
-      end
-
-      files.reject! {|f| f=~ /\/(activity|pending|completed)\.yml\s*$/}
-      unless files.empty? and !File.exist?(PENDING_YML)
-        _h2_ 'Pending'
-        files.each {|line| _pre line.strip.sub(/\s+/,' ')}
-        pending = YAML.load(open(PENDING_YML)) rescue []
-        pending.each {|vars| _pre "@ #{vars['email']}" if vars['email']}
-        _form.buttons target: 'viewport', action: 'file.cgi', method: 'post' do
-          _input name: 'message', id: 'message', type: 'hidden',
-            'data-value' => message
-          _input type: 'submit', name: 'action', value: 'Edit CC'
-          _input type: 'submit', name: 'action', value: 'Cleanup'
-          _input type: 'submit', name: 'action', value: 'Commit'
-        end
-      end
-
-      files = []
-      Dir.chdir("#{RECEIVED}") do
-        Dir["*"].sort_by {|name| File.stat(name.untaint).mtime}.each do |file|
-          next if %w{README deadletter archives}.include? file
-          next if %w{pending.yml completed.yml activity.yml}.include? file
-          next if file =~ /^to_\w+$/
-          if File.directory? file
-            next if Dir.entries(file).reject {|name| name=~/^\./}.empty?
-          end
-          File.chmod 0644, file if file =~ /\.pdf$/
-          files << file
-        end
-      end
-
-      _h2_ 'Work List'
-      if files.empty?
-        _p.worklist! { _i 'Empty' }
-      else
-        _ul.worklist! do
-          files.each do |file|
-            _li do
-              ondisk = File.join(RECEIVED,file)
-              file += '/' if File.directory? ondisk
-              _a file, 'data-mtime' => (File.stat(ondisk).mtime.to_i rescue nil)
-            end
-          end
-        end
-      end
-
-      _div_.classify! do
-        _h2_ 'Classification'
-
-        _form.doctype! target: 'viewport', action: 'file.cgi', method: 'post' do
-          _table_ do
-            _tr do
-              DOCTYPES[0..4].each do |doctype|
-                _td align: 'center' do
-                  _input type: 'radio', name: 'doctype', value: doctype
-                end
-              end
-            end
-            _tr do
-              DOCTYPES[0..4].each do |doctype|
-                _td doctype, align: 'center'
-              end
-            end
-          end
-
-          _table_ do
-            _tr do
-              DOCTYPES[5..-1].each do |doctype|
-                _td align: 'center' do
-                  _input type: 'radio', name: 'doctype', value: doctype
-                end
-              end
-            end
-            _tr do
-              DOCTYPES[5..-1].each do |doctype|
-                _td doctype, align: 'center'
-              end
-            end
-          end
-
-          _input name: 'source', id: 'source', type: 'hidden'
-
-          _table_ id: 'icla-form' do
-            _tr do
-              _td.label 'Real Name'
-              _td.input do
-                _input name: 'realname', id: 'realname', type: 'text'
-              end
-            end
-            _tr do
-              _td.label 'Public Name'
-              _td.input do
-                _input name: 'pubname', id: 'pubname', type: 'text'
-              end
-            end
-            _tr do
-              _td.label 'E-mail'
-              _td do 
-                _input name: 'email', id: 'email', type: 'email'
-              end
-            end
-            _tr do
-              _td.label 'File Name'
-              _td.input do
-                _input name: 'filename', id: 'filename', type: 'text'
-              end
-            end
-          end
-
-          _div_ id: 'nda-form' do
-            _table do
-              _tr do
-                _td do
-                  _label 'Name', for: 'nname'
-                end
-                _td do
-                  _input type: :text, name: 'nname', id: 'nname'
-                end
-              end
-
-              _tr do
-                _td do
-                  _label 'ASF ID', for: 'nid'
-                end
-                _td do
-                  _input type: :text, name: 'nid', id: 'nid'
-                end
-              end
-
-              _tr do
-                _td.label 'EMail'
-                _td.input do
-                  _input name: 'nemail', id: 'nemail', type: 'email'
-                end
-              end
-
-              _tr do
-                _td.label 'File Name'
-                _td.input do
-                  _input name: 'nfilename', id: 'nfilename', type: 'text'
-                end
-              end
-            end
-          end
-
-          _div_ id: 'incomplete-form' do
-            _table do
-              _tr do
-                _td do
-                  _label 'Name', for: 'iname'
-                end
-                _td do
-                  _input type: :text, name: 'iname', id: 'iname'
-                end
-              end
-
-              _tr do
-                _td.label 'EMail'
-                _td.input do
-                  _input name: 'iemail', id: 'iemail', type: 'email'
-                end
-              end
-            end
-          end
-
-          _div_ id: 'unsigned-form' do
-            _table do
-              _tr do
-                _td do
-                  _label 'Name', for: 'uname'
-                end
-                _td do
-                  _input type: :text, name: 'uname', id: 'uname'
-                end
-              end
-
-              _tr do
-                _td.label 'EMail'
-                _td.input do
-                  _input name: 'uemail', id: 'uemail', type: 'email'
-                end
-              end
-
-            end
-          end
-
-          _div_ id: 'publickey-form' do
-            _table do
-              _tr do
-                _td do
-                  _label 'Name', for: 'pname'
-                end
-                _td do
-                  _input type: :text, name: 'pname', id: 'pname'
-                end
-              end
-
-              _tr do
-                _td.label 'EMail'
-                _td.input do
-                  _input name: 'pemail', id: 'pemail', type: 'email'
-                end
-              end
-            end
-          end
-
-          _div id: 'mem-form' do
-            if defined?(MEETING)
-              received = open("#{MEETING}/memapp-received.txt").read
-            else
-              received = ''
-            end
-
-            _table do
-              _tr do
-                _td do
-                  _label 'Public Name', for: 'mpname'
-                end
-                _td do
-                  _select id: 'mavailid', name: 'mavailid' do
-                    pattern = /^\w+\s+(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s+(.*)\n/
-                    _option value: '', selected: true
-                    options = []
-                    received.scan(pattern) do |apply, mail, karma, id, name|
-                      next unless apply=='no'
-                      options << [ name.strip, id ]
-                    end
-                    options.sort.each do |name, id|
-                      _option name, value: id
-                    end
-                  end
-                end
-              end
-        
-              _tr do
-                _td do
-                  _label 'Full Name', for: 'mfname'
-                end
-                _td do
-                  _input type: :text, name: 'mfname', id: 'mfname'
-                end
-              end
-        
-              _tr do
-                _td do
-                  _label 'Address', for: 'maddr'
-                end
-                _td do
-                  _textarea rows: 5, name: 'maddr', id: 'maddr'
-                end
-              end
-                
-              _tr do
-                _td do
-                  _label 'Country', for: 'mcountry'
-                end
-                _td do
-                  _input type: :text, name: 'mcountry', id: 'mcountry'
-                end
-              end
-                
-              _tr do
-                _td do
-                  _label 'Telephone', for: 'mtele'
-                end
-                _td do
-                  _input type: :text, name: 'mtele', id: 'mtele'
-                end
-              end
-                
-              _tr do
-                _td do
-                  _label 'Fax', for: 'mfax'
-                end
-                _td do
-                  _input type: :text, name: 'mfax', id: 'mfax'
-                end
-              end
-                
-              _tr do
-                _td do
-                  _label 'E-Mail', for: 'memail'
-                end
-                _td do
-                  _input type: :email, name: 'memail', id: 'memail'
-                end
-              end
-                
-              _tr do
-                _td do
-                  _label 'File Name', for: 'mfilename'
-                end
-                _td do
-                  _input type: :text, name: 'mfilename', id: 'mfilename'
-                end
-              end
-            end
-          end
-
-          _div_ id: 'grant-form' do
-            _table do
-              _tr do
-                _td.label 'From'
-                _td do
-                  _input name: 'from', type: 'text'
-                end
-              end
-              _tr do
-                _td.label 'For'
-                _td do
-                  _textarea name: 'description', rows: 5
-                end
-              end
-              _tr do
-                _td.label 'Signed By'
-                _td do
-                  _input name: 'gname', id: 'gname', type: 'text'
-                end
-              end
-              _tr do
-                _td.label 'E-mail'
-                _td do
-                  _input name: 'gemail', id: 'gemail', type: 'email'
-                end
-              end
-              _tr do
-                _td.label 'File Name'
-                _td.input do
-                  _input name: 'gfilename', type: 'text'
-                end
-              end
-            end
-          end
-
-          _table_ id: 'ccla-form' do
-            _tr do
-              _td.label 'Corporation'
-              _td.input do
-                _input name: 'company', id: 'company', type: 'text'
-              end
-            end
-            _tr do
-              _td.label 'Product'
-              _td.input do
-                _input name: 'product', id: 'product', type: 'text'
-              end
-            end
-            _tr do
-              _td.label 'Contact'
-              _td do
-                _input name: 'contact', id: 'contact', type: 'text'
-              end
-            end
-            _tr do
-              _td.label 'E-mail'
-              _td do
-                _input name: 'cemail', id: 'cemail', type: 'email'
-              end
-            end
-            _tr do
-              _td.label 'Employees'
-              _td { _textarea name: 'employees', rows: 5 }
-            end
-            _tr do
-              _td.label 'File Name'
-              _td.input do
-                _input name: 'cfilename', type: 'text'
-              end
-            end
-          end
-
-          _input name: 'replaces', id: 'replaces', type: 'hidden'
-
-          _div_.buttons!.buttons do
-            _input type: 'submit', value: 'File'
-            _input type: 'submit', name: 'action', value: 'Cancel'
-          end
-
-          _div_.buckets!.buttons do
-            _fieldset do
-              _legend 'Do:'
-              _input type: 'submit', name: 'dest', value: 'burst'
-              _input type: 'submit', name: 'dest', value: 'flip'
-              _input type: 'submit', name: 'dest', value: 'restore'
-              _input type: 'submit', name: 'dest', value: 'rotate right'
-              _input type: 'submit', name: 'dest', value: 'rotate left'
-            end
-            _fieldset do
-              _legend 'File:'
-              _input type: 'submit', name: 'dest', value: 'operations'
-              _input type: 'submit', name: 'dest', value: 'dup'
-              _input type: 'submit', name: 'dest', value: 'junk'
-              _input type: 'submit', name: 'dest', value: 'incomplete'
-              _input type: 'submit', name: 'dest', value: 'unsigned'
-            end
-          end
-
-          _table id: 'icla2-form' do
-            _tr do
-              _td.label 'User ID'
-              _td.input do
-                _input name: 'user', id: 'user', type: 'text'
-              end
-            end
-            _tr do
-              _td.label 'PMC'
-              _td.input do
-                _input name: 'pmc', id: 'pmc', type: 'text'
-              end
-            end
-            _tr do
-              _td.label 'Podling'
-              _td.input do
-                _input name: 'podling', id: 'podling', type: 'text'
-              end
-            end
-            _tr do
-              _td.label 'Vote Link'
-              _td.input do
-                _input name: 'votelink', id: 'votelink', type: 'text'
-              end
-            end
-          end
-
-          _table id: 'grant2-form' do
-            _tr do
-              _td.label 'PMC'
-              _td.input do
-                _input name: 'ggmc', id: 'gpmc', type: 'text'
-              end
-            end
-            _tr do
-              _td.label 'Podling'
-              _td.input do
-                _input name: 'gpodling', id: 'gpodling', type: 'text'
-              end
-            end
-          end
-
-          _table id: 'ccla2-form' do
-            _tr do
-              _td.label 'PMC'
-              _td.input do
-                _input name: 'cpmc', id: 'cpmc', type: 'text'
-              end
-            end
-            _tr do
-              _td.label 'Podling'
-              _td.input do
-                _input name: 'cpodling', id: 'cpodling', type: 'text'
-              end
-            end
-          end
-
-          _table id: 'incomplete2-form' do
-            _tr do
-              _td.label 'PMC'
-              _td.input do
-                _input name: 'ipmc', id: 'ipmc', type: 'text'
-              end
-            end
-            _tr do
-              _td.label 'Podling'
-              _td.input do
-                _input name: 'ipodling', id: 'ipodling', type: 'text'
-              end
-            end
-          end
-
-          _table id: 'unsigned2-form' do
-            _tr do
-              _td.label 'PMC'
-              _td.input do
-                _input name: 'upmc', id: 'upmc', type: 'text'
-              end
-            end
-            _tr do
-              _td.label 'Podling'
-              _td.input do
-                _input name: 'upodling', id: 'upodling', type: 'text'
-              end
-            end
-          end
-          _table id: 'publickey2-form' do
-            _tr do
-              _td.label 'PMC'
-              _td.input do
-                _input name: 'ppmc', id: 'ppmc', type: 'text'
-              end
-            end
-            _tr do
-              _td.label 'Podling'
-              _td.input do
-                _input name: 'ipodling', id: 'ipodling', type: 'text'
-              end
-            end
-          end
-        end
-      end
-
-      _h2_ 'Links'
-      _ul do
-        _li do
-          _a 'Response time', target: 'viewport',
-            href: 'https://whimsy.apache.org/secretary/response-time'
-        end
-        _li do
-          _a 'Mail Search', href: 'https://mail-search.apache.org/',
-            target: 'viewport'
-        end
-        _li do
-          query = ''
-
-          if File.exist? COMPLETED_YML
-            last = YAML.load(File.read COMPLETED_YML).last
-            params = {}
-            %w{email user pmc podling votelink}.each do |name|
-              params[name] = last[name] if last[name]
-            end
-            unless params.empty?
-              params['iclas'] = '1'
-              query = '?' + params.
-                map {|name,value| "#{name}=#{CGI.escape value}"}.join('&')
-            end
-          end
-
-          _a 'New Account', target: 'viewport', href: '/officers/acreq' + query
-        end
-        _li do
-          _a 'Committers by id', target: 'viewport',
-            href: 'http://people.apache.org/committer-index.html'
-        end
-        _li do
-          _a 'Documents', target: 'viewport',
-            href: 'https://svn.apache.org/repos/private/documents/'
-        end
-        _li do
-          _a 'ICLA lint', target: 'viewport',
-            href: 'https://whimsy.apache.org/secretary/icla-lint'
-        end
-        _li do
-          _a 'Public names', target: 'viewport',
-            href: 'https://whimsy.apache.org/secretary/public-names'
-        end
-        _li do
-          _a 'Board subscriptions', target: 'viewport',
-            href: 'https://whimsy.apache.org/board/subscriptions/'
-        end
-        _li do
-          _a 'Mail aliases', target: 'viewport',
-            href: 'https://id.apache.org/info/MailAlias.txt'
-        end
-        _li do
-          _a 'Member list', target: 'viewport',
-            href: 'https://svn.apache.org/repos/private/foundation/members.txt'
-        end
-        _li do
-          _a 'How to use this tool', href: 'HOWTO.html',
-            target: 'viewport'
-        end
-        _li do
-          _a 'Show pending.yml', href: 'RECEIVED/pending.yml',
-            target: 'viewport'
-        end
-
-        if File.exist? '/var/tools/secretary/secmail'
-          _li {_p {_hr}}
-          _li {_a 'Upload email', href: 'upload', target: 'viewport'}
-        end
-      end
-    end
-  end
-end
diff --git a/www/secretary/workbench/worklist.css b/www/secretary/workbench/worklist.css
deleted file mode 100644
index 041c4a8..0000000
--- a/www/secretary/workbench/worklist.css
+++ /dev/null
@@ -1,23 +0,0 @@
-html {background-color: #EEE}
-input[type="email"],input[type="text"],select {width: 100%}
-textarea {width: 100%}
-p#worklist {margin-left: 2em}
-#classify {display: none}
-#doctype > table:first-child {margin-bottom: 1em}
-table {width: 100%}
-td.label {width:10em;height:2.5em}
-td.input {width:100em}
-h2 {background: #303284; color:#FFF; border-radius: 1em; padding: .25em 1em}
-h2.warn {background:yellow; color:black}
-input[type="submit"].button {width: 5em} 
-input[type="submit"].bucket {width: 3.5em} 
-input[type="submit"] {border-radius: 2em}
-input.loading {background-color: #EF8}
-ul {list-style:none; padding-left:0}
-li {margin: 0.5em 2em 0 1em; padding-left: 1em; border-radius: 1em}
-li:hover {background-color: #F2EEB2}
-li.selected {background-color: #FE0}
-.buttons, .buckets {text-align:center; margin-bottom:0.5em}
-pre {margin:0 0 0 2em}
-.traceback {background-color:#ff0; border: 2px solid}
-.traceback {margin: 1em 0; padding: 1em}
diff --git a/www/secretary/workbench/worklist.js b/www/secretary/workbench/worklist.js
deleted file mode 100644
index ec9d49f..0000000
--- a/www/secretary/workbench/worklist.js
+++ /dev/null
@@ -1,228 +0,0 @@
-// prime the second frame
-if (parent.frames[1].location == 'about:blank') {
-  parent.frames[1].location.href = "file.cgi?action=update"
-}
-
-// Map non-ASCII characters to lower case ASCII
-function asciize(name) {
-  if (name.match(/[^\x00-\x7F]/)) {
-    // digraphs.  May be culturally sensitive
-    name=name.replace(/\u00df/g,'ss');
-    name=name.replace(/\u00e4|a\u0308/g,'ae');
-    name=name.replace(/\u00e5|a\u030a/g,'aa');
-    name=name.replace(/\u00e6/g,'ae');
-    name=name.replace(/\u00f1|n\u0303/g,'ny');
-    name=name.replace(/\u00f6|o\u0308/g,'oe');
-    name=name.replace(/\u00fc|u\u0308/g,'ue');
-
-    // latin 1
-    name=name.replace(/[\u00e0-\u00e5]/g,'a');
-    name=name.replace(/\u00e7/g,'c');
-    name=name.replace(/[\u00e8-\u00eb]/g,'e');
-    name=name.replace(/[\u00ec-\u00ef]/g,'i');
-    name=name.replace(/[\u00f2-\u00f6]|\u00f8/g,'o');
-    name=name.replace(/[\u00f9-\u00fc]/g,'u');
-    name=name.replace(/[\u00fd\u00ff]/g,'y');
-
-    // Latin Extended-A
-    name=name.replace(/[\u0100-\u0105]/g,'a');
-    name=name.replace(/[\u0106-\u010d]/g,'c');
-    name=name.replace(/[\u010e-\u0111]/g,'d');
-    name=name.replace(/[\u0112-\u011b]/g,'e');
-    name=name.replace(/[\u011c-\u0123]/g,'g');
-    name=name.replace(/[\u0124-\u0127]/g,'h');
-    name=name.replace(/[\u0128-\u0131]/g,'i');
-    name=name.replace(/[\u0132-\u0133]/g,'ij');
-    name=name.replace(/[\u0134-\u0135]/g,'j');
-    name=name.replace(/[\u0136-\u0138]/g,'k');
-    name=name.replace(/[\u0139-\u0142]/g,'l');
-    name=name.replace(/[\u0143-\u014b]/g,'n');
-    name=name.replace(/[\u014C-\u0151]/g,'o');
-    name=name.replace(/[\u0152-\u0153]/g,'oe');
-    name=name.replace(/[\u0154-\u0159]/g,'r');
-    name=name.replace(/[\u015a-\u0162]/g,'s');
-    name=name.replace(/[\u0162-\u0167]/g,'t');
-    name=name.replace(/[\u0168-\u0173]/g,'u');
-    name=name.replace(/[\u0174-\u0175]/g,'w');
-    name=name.replace(/[\u0176-\u0178]/g,'y');
-    name=name.replace(/[\u0179-\u017e]/g,'z');
-
-    // denormalized diacritics
-    name=name.replace(/[\u0300-\u036f]/g,'');
-  }
-
-  return name.trim().replace(/[^\w]+/g,'-');
-}
-
-// Generate file name from real name (icla)
-function generateFileName(selection) {
-  var value = asciize($('#realname').val());
-  var source = $('#source').val();
-  if (source.indexOf('.')>0) {
-    if (source.indexOf('@')<0 || source.match(/\.pdf$/)) {
-      value+=source.replace(/.*\./,'.');
-    }
-  }
-  return value.replace(/-+/g, '-').toLowerCase();
-}
-
-$(document).ready(function() {
-  // member autofill
-  $('#mavailid').change(function() {
-    var selected = $('#mavailid :selected'); 
-    if (!$('#memail').val()) $("#memail").val($(this).val()+"@apache.org");
-    $("#email").val($(this).val()+"@apache.org");
-    $("#mfname").val(selected.text());
-    $("#realname").val(selected.text());
-    $("#mfilename").val(generateFileName());
-    $("#maddr").focus();
-  });
-
-  // File selection
-  $('#worklist a').click(function() {
-    var link = $(this).text();
-    var directory = link.match("/$");
-    $("*[id$='-form']").hide();
-    $("#buttons").hide();
-    $("#buckets").hide();
-    $("#doctype")[0].reset();
-    $("#classify").show();
-    $("#source").val(link);
-
-    $("li:has(a)").removeClass('selected');
-    if (directory) {
-      $("li:has(a:contains('" + link + "/'))").addClass('selected');
-    } else {
-      $("li:has(a:contains('" + link + "'))").addClass('selected');
-    }
-
-    if (directory || link.match(/pgp\.txt$/)) {
-      parent.frames[1].location.href = 'file.cgi?action=view&dir=' +
-        encodeURIComponent(link);
-    } else if (link.match(/\.(pdf|txt)$/)) {
-      var href = link;
-      if ($(this).attr('data-mtime')) href += '?' + $(this).attr('data-mtime');
-      parent.frames[1].location.href = '/members/received/' + href;
-    } else  {
-      parent.frames[1].location.href = 'file.cgi?action=danger&link=' +
-        encodeURIComponent(link);
-    }
-
-    if (!link.match(/^eFax-\d+\.pdf$/)) {
-      $("#icla-form input").addClass("loading");
-      $("#ccla-form input").addClass("loading");
-      $("#grant-form input").addClass("loading");
-      $.post('file.cgi', {cmd: 'svninfo', source: link}, function(info) {
-        if (!$('#realname').val()) $('#realname').val(info.from);
-        if (!$('#nname').val())    $('#nname').val(info.from);
-        if (!$('#iname').val())    $('#iname').val(info.from);
-        if (!$('#uname').val())    $('#uname').val(info.from);
-        if (!$('#pname').val())    $('#pname').val(info.from);
-        if (!$('#contact').val())  $('#contact').val(info.from);
-        if (!$('#gname').val())    $('#gname').val(info.from);
-        if (!$('#email').val())    $('#email').val(info.email);
-        if (!$('#cemail').val())   $('#cemail').val(info.email);
-        if (!$('#gemail').val())   $('#gemail').val(info.email);
-        if (!$('#nemail').val())   $('#nemail').val(info.email);
-        if (!$('#memail').val())   $('#memail').val(info.email);
-        if (!$('#iemail').val())   $('#iemail').val(info.email);
-        if (!$('#uemail').val())   $('#uemail').val(info.email);
-        if (!$('#pemail').val())   $('#pemail').val(info.email);
-        if (!$('#nid').val()) {
-          var email = $('#email').val();
-          if (email.match(/^\w+@apache.org$/)) {
-            $('#nid').val(email.split('@')[0]);
-          }
-        }
-        $("#icla-form input").removeClass("loading");
-        $("#ccla-form input").removeClass("loading");
-        $("#grant-form input").removeClass("loading");
-      }, 'json');
-    }
-  });
-
-  // Classification
-  $('#doctype input[name=doctype]').click(function() {
-    var selection = $(this).attr('value');
-    $("*[id$='-form']").slideUp();
-    $("#"+selection+"-form").slideDown();
-    $("#"+selection+"2-form").slideDown();
-    if (selection == 'other') {
-      $("#buckets").show();
-      $("#buttons").hide();
-    } else {
-      $("#buckets").hide();
-      $("#buttons").show();
-    }
-    $("#replaces").val("");
-    if (selection == 'icla') {
-      $("#archive").removeAttr("disabled");
-    } else {
-      $("#archive").attr("disabled", "disabled");
-    }
-  });
-
-  // Fill in
-  $("#pubname").focus(function() {
-    if (this.value=='') this.value=$('#realname').val();
-  });
-
-  $("#email").focus(function() {
-    if (this.value=='') {
-      var source = $('#source').val();
-      if (source.indexOf('@') == -1) source='';
-      this.value = source.toLowerCase();
-    }
-  });
-
-  $("#filename, #nfilename").focus(function() {
-    if (this.value=='') this.value = generateFileName();
-  });
-
-  $("input[name=cfilename]").focus(function() {
-    if (this.value=='') {
-      var source = $('#source').val();
-      var value = $('#company').val();
-      var product = $('#product').val();
-      if (product) value += '-' + product;
-      value = asciize(value);
-      if (source.indexOf('.')>0) {
-        if (source.indexOf('@')<0 || source.match(/\.pdf$/)) {
-          value+=source.replace(/.*\./,'.');
-        }
-      }
-      value = value.replace(/-+/g,'-').toLowerCase();
-      value = value.replace(/-[.]/,'.');
-      value = value.replace(/-inc[.]/,'.');
-      this.value = value;
-    }
-  });
-
-  $("#nemail").focus(function() {
-    if (this.value=='') this.value=$('#nid').val()+'@apache.org';
-  });
-
-  // Commit prompt
-  $("input[value=Commit]").click(function() {
-    var message = prompt("Commit Message?", $('#message').attr('data-value'));
-    if (message) {
-      $('#message').attr('value', message);
-      return true;
-    } else {
-      return false;
-    }
-  });
-
-  // Cleanup prompt
-  $("input[value=Cleanup]").click(function() {
-    return confirm("Revert all changes and cleanup subversion?");
-  });
-
-  // Don't validate when junking
-  $("input[value=junk]").click(function() {
-    $('input').attr('formnovalidate', 'formnovalidate');
-    $('#message').attr('value', 'Spam')
-    return true;
-  });
-
-});

-- 
To stop receiving notification emails like this one, please contact
['"commits@whimsical.apache.org" <commits@whimsical.apache.org>'].

Mime
View raw message