%
  # options example...
  #
  # 'name'  =>
  # 'table' =>
  # #? 'primary_key' => #required when the dbdef doesn't know...???
  # 'labels' => {
  #               'column' => 'Label',
  #             }
  #
  # listref - each item is a literal column name (or method) or (notyet) coderef
  # if not specified all columns (except for the primary key) will be editable
  # 'fields' => [
  #             ]
  #
  # 'menubar'     => '', #menubar arrayref
  #
  # 'viewall_dir' => '', #'search' or 'browse', defaults to 'search'
  my(%opt) = @_;
  #false laziness w/process.html
  my $table = $opt{'table'};
  my $class = "FS::$table";
  my $pkey = dbdef->table($table)->primary_key; #? $opt{'primary_key'} || 
  my $fields = $opt{'fields'}
               #|| [ grep { $_ ne $pkey } dbdef->table($table)->columns ];
               || [ grep { $_ ne $pkey } fields($table) ];
  my $object;
  if ( $cgi->param('error') ) {
    $object = $class->new( {
      map { $_ => scalar($cgi->param($_)) } fields($table)
    });
  } elsif ( $cgi->keywords ) { #editing
    my( $query ) = $cgi->keywords;
    $query =~ /^(\d+)$/;
    $object = qsearchs( $table, { $pkey => $1 } );
  } else { #adding
    $object = $class->new( {} );
  }
  my $action = $object->$pkey() ? 'Edit' : 'Add';
  my $title = "$action $opt{'name'}";
  my @menubar = ();
  if ( $opt{'menubar'} ) {
    @menubar = @{ $opt{'menubar'} };
  } else {
    @menubar = (
      'Main menu' => $p, #eventually get rid of this when the ACL/UI update is done
      #eventually use Lingua::bs to pluralize
      "View all $opt{'name'}s" => $p. ( $opt{'viewall_dir'} || 'search' ).
                                  "/$table.html",
    );
  }
%>
<%= include("/elements/header.html", $title,
              include( '/elements/menubar.html', @menubar )
           )
%>
<% if ( $cgi->param('error') ) { %>
  Error: <%= $cgi->param('error') %>
  
<% } %>