Index: trunk/DNSDB.pm
===================================================================
--- trunk/DNSDB.pm	(revision 322)
+++ trunk/DNSDB.pm	(revision 323)
@@ -46,4 +46,5 @@
 	&getSOA	&updateSOA &getRecLine &getDomRecs &getRecCount
 	&addRec &updateRec &delRec
+	&getLogCount &getLogEntries
 	&getTypelist
 	&parentID
@@ -69,4 +70,5 @@
 		&getSOA &updateSOA &getRecLine &getDomRecs &getRecCount
 		&addRec &updateRec &delRec
+		&getLogCount &getLogEntries
 		&getTypelist
 		&parentID
@@ -3068,4 +3070,81 @@
   return ('OK',$logdata{entry});
 } # end delRec()
+
+
+## DNSDB::getLogCount()
+# Get a count of log entries
+# Takes a database handle and a hash containing at least:
+# - Entity ID and entity type as the primary log "slice"
+sub getLogCount {
+  my $dbh = shift;
+
+  my %args = @_;
+
+  my @filterargs;
+##fixme:  which fields do we want to filter on?
+# push @filterargs, 
+
+  $errstr = 'Missing primary parent ID and/or type';
+  # fail early if we don't have a "prime" ID to look for log entries for
+  return if !$args{id};
+
+  # or if the prime id type is missing or invalid
+  return if !$args{logtype};
+  $args{logtype} = 'revzone' if $args{logtype} eq 'rdns';	# hack pthui
+  $args{logtype} = 'domain' if $args{logtype} eq 'dom';		# hack pthui
+  return if !grep /^$args{logtype}$/, ('group', 'domain', 'revzone', 'user');
+
+  $args{logtype} = 'revzone' if $args{logtype} eq 'rdns';	# hack pthui
+
+  my $sql = "SELECT count(*) FROM log ".
+	"WHERE $id_col{$args{logtype}}=?".
+	($args{filter} ? " AND entry ~* ?" : '');
+  my ($count) = $dbh->selectrow_array($sql, undef, ($args{id}, @filterargs) );
+  $errstr = $dbh->errstr if !$count;
+  return $count;
+} # end getLogCount()
+
+
+## DNSDB::getLogEntries()
+# Get a list of log entries
+# Takes arguments as with getLogCount() above, plus optional:
+# - sort field
+# - sort order
+# - offset for pagination
+sub getLogEntries {
+  my $dbh = shift;
+
+  my %args = @_;
+
+  my @filterargs;
+
+  # fail early if we don't have a "prime" ID to look for log entries for
+  return if !$args{id};
+
+  # or if the prime id type is missing or invalid
+  return if !$args{logtype};
+  $args{logtype} = 'revzone' if $args{logtype} eq 'rdns';	# hack pthui
+  $args{logtype} = 'domain' if $args{logtype} eq 'dom';		# hack pthui
+  return if !grep /^$args{logtype}$/, ('group', 'domain', 'revzone', 'user');
+
+  # Sorting defaults
+  $args{sortby} = 'stamp' if !$args{sortby};
+  $args{sortorder} = 'DESC' if !$args{sortorder};
+  $args{offset} = 0 if !$args{offset};
+
+  my %sortmap = (fname => 'name', username => 'email', entry => 'entry', stamp => 'stamp,log_id');
+  $args{sortby} = $sortmap{$args{sortby}};
+
+  my $sql = "SELECT user_id AS userid, email AS useremail, name AS userfname, entry AS logentry, ".
+	"date_trunc('second',stamp) AS logtime ".
+	"FROM log ".
+	"WHERE $id_col{$args{logtype}}=?".
+	($args{filter} ? " AND entry ~* ?" : '').
+	" ORDER BY $args{sortby} $args{sortorder}".
+	($args{offset} eq 'all' ? '' : " LIMIT $config{perpage} OFFSET ".$args{offset}*$config{perpage});
+  my $loglist = $dbh->selectall_arrayref($sql, { Slice => {} }, ($args{id}, @filterargs) );
+  $errstr = $dbh->errstr if !$loglist;
+  return $loglist;
+} # end getLogEntries()
 
 
Index: trunk/dns.cgi
===================================================================
--- trunk/dns.cgi	(revision 322)
+++ trunk/dns.cgi	(revision 323)
@@ -96,4 +96,6 @@
   $session->param('reclistsortby','host');
   $session->param('reclistorder','ASC');
+  $session->param('logsortby','stamp');
+  $session->param('logorder','DESC');
 }
 
@@ -1497,11 +1499,9 @@
 } elsif ($webvar{page} eq 'log') {
 
-##fixme put in some real log-munching stuff
-  my $sql = "SELECT user_id, email, name, entry, date_trunc('second',stamp) FROM log WHERE ";
   my $id = $curgroup;  # we do this because the group log may be called from (almost) any page,
                        # but the others are much more limited.  this is probably non-optimal.
 
   if ($webvar{ltype} && $webvar{ltype} eq 'user') {
-    $sql .= "user_id=?";
+##fixme:  where should we call this from?
     $id = $webvar{id};
     if (!check_scope(id => $id, type => 'user')) {
@@ -1511,5 +1511,4 @@
     $page->param(logfor => 'user '.userFullName($dbh,$id));
   } elsif ($webvar{ltype} && $webvar{ltype} eq 'dom') {
-    $sql .= "domain_id=?";
     $id = $webvar{id};
     if (!check_scope(id => $id, type => 'domain')) {
@@ -1519,5 +1518,4 @@
     $page->param(logfor => 'domain '.domainName($dbh,$id));
   } elsif ($webvar{ltype} && $webvar{ltype} eq 'rdns') {
-    $sql .= "rdns_id=?";
     $id = $webvar{id};
     if (!check_scope(id => $id, type => 'revzone')) {
@@ -1528,27 +1526,43 @@
   } else {
     # Default to listing curgroup log
-    $sql .= "group_id=?";
     $page->param(logfor => 'group '.groupName($dbh,$id));
     # note that scope limitations are applied via the change-group check;
     # group log is always for the "current" group
   }
+  $webvar{ltype} = 'group' if !$webvar{ltype};
+  my $lcount = getLogCount($dbh, (id => $id, logtype => $webvar{ltype})) or push @debugbits, $DNSDB::errstr;
+
+  $page->param(id => $id);
+  $page->param(ltype => $webvar{ltype});
+
+  fill_fpnla($lcount);
+  fill_pgcount($lcount, "log entries", '');
+  $page->param(curpage => $webvar{page}.($webvar{ltype} ? "&amp;ltype=$webvar{ltype}" : ''));
+
+  $sortby = 'stamp';
+  $sortorder = 'DESC';	# newest-first;  although filtering is probably going to be more useful than sorting
+# sort/order
+  $session->param($webvar{page}.'sortby', $webvar{sortby}) if $webvar{sortby};
+  $session->param($webvar{page}.'order', $webvar{order}) if $webvar{order};
+
+  $sortby = $session->param($webvar{page}.'sortby') if $session->param($webvar{page}.'sortby');
+  $sortorder = $session->param($webvar{page}.'order') if $session->param($webvar{page}.'order');
+
+  # Set up the column headings with the sort info
+  my @cols = ('fname','username','entry','stamp');
+  my %colnames = (fname => 'Name', username => 'Username/Email', entry => 'Log Entry', stamp => 'Date/Time');
+  fill_colheads($sortby, $sortorder, \@cols, \%colnames);
+
+##fixme:  increase per-page limit or use separate limit for log?  some ops give *lots* of entries...
+  my $logentries = getLogEntries($dbh, (id => $id, logtype => $webvar{ltype},
+	offset => $webvar{offset}, sortby => $sortby, sortorder => $sortorder));
+  $page->param(logentries => $logentries);
+
 ##fixme:
 # - filtering
 # - show reverse zone column?
-# - pagination/limiting number of records - put newest-first so user
-#   doesn't always need to go to the last page for recent activity?
-  my $sth = $dbh->prepare($sql);
-  $sth->execute($id);
-  my @logbits;
-  while (my ($uid, $email, $name, $entry, $stamp) = $sth->fetchrow_array) {
-    my %row;
-    $row{userfname} = $name;
-    $row{userid} = $uid;
-    $row{useremail} = $email;
-    ($row{logentry} = $entry) =~ s/\n/<br>\n/g;
-    ($row{logtime}) = ($stamp =~ /^(.+)-\d\d$/);
-    push @logbits, \%row;
-  }
-  $page->param(logentries => \@logbits);
+# - on log record creation, bundle "parented" log actions (eg, "AXFR record blah for domain foo",
+#   or "Add record bar for new domain baz") into one entry (eg, "AXFR domain foo", "Add domain baz")?
+#   need a way to expand this into the complete list, and to exclude "child" entries
 
   # scope check fail target
Index: trunk/templates/log.tmpl
===================================================================
--- trunk/templates/log.tmpl	(revision 322)
+++ trunk/templates/log.tmpl	(revision 323)
@@ -10,13 +10,24 @@
 
 <table border="0" width="90%">
-<tr><th colspan="5"><div class="center maintitle">Log entries for <TMPL_VAR NAME=logfor></div></th></tr>
-  <tr class="darkrowheader">
-      <td>Name</td>
+<tr><th colspan="3"><div class="center maintitle">Log entries for <TMPL_VAR NAME=logfor></div></th></tr>
+<tr>
+<td class="leftthird"><TMPL_INCLUDE NAME="pgcount.tmpl"></td>
+<td align="center"><TMPL_INCLUDE NAME="fpnla.tmpl"></td>
+<td class="rightthird">&nbsp;</td>
+</tr>
+</table>
+<table border="0" width="90%">
       <!-- Not sure "Customer ID" (filled with uid) is of any use... -->
       <!-- td>Customer ID</td -->
-      <td>Username/Email</td>
-      <td>Log Entry</td>
-      <td>Date / Time</td>
-  </tr>
+<tr class="darkrowheader">
+<TMPL_LOOP NAME=colheads><TMPL_IF firstcol></TMPL_IF>
+	<td><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=<TMPL_VAR NAME=page><TMPL_IF
+ NAME=offset>&amp;offset=<TMPL_VAR NAME=offset></TMPL_IF>&amp;sortby=<TMPL_VAR
+ NAME=sortby>&amp;order=<TMPL_VAR NAME=order>&amp;id=<TMPL_VAR NAME=id>&amp;ltype=<TMPL_VAR
+ NAME=ltype>"><TMPL_VAR NAME=colname></a><TMPL_IF
+ NAME=sortorder>&nbsp;<img alt="<TMPL_VAR NAME=sortorder>" src="images/<TMPL_VARNAME=sortorder>.png"
+ /></TMPL_IF></td></TMPL_LOOP>
+</tr>
+
 <TMPL_IF logentries>
 <TMPL_LOOP NAME=logentries>
