Changeset 65


Ignore:
Timestamp:
11/25/10 16:26:08 (13 years ago)
Author:
Kris Deugau
Message:

/trunk

checkpoint, adding permissions/ACL support

Location:
trunk
Files:
3 added
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/DNSDB.pm

    r62 r65  
    1616use DBI;
    1717use Net::DNS;
     18use Crypt::PasswdMD5;
    1819#use Net::SMTP;
    1920#use NetAddr::IP qw( Compact );
     
    2425@ISA            = qw(Exporter);
    2526@EXPORT_OK      = qw(
    26         &initGlobals &connectDB &finish
     27        &initGlobals &initPermissions &getPermissions
     28        &connectDB &finish
    2729        &addDomain &delDomain &domainName
    2830        &addGroup &delGroup &getChildren &groupName
     
    3234        &domStatus &importAXFR
    3335        %typemap %reverse_typemap
     36        %permissions
    3437        );
    3538
    3639@EXPORT         = (); # Export nothing by default.
    3740%EXPORT_TAGS    = ( ALL => [qw(
    38                 &initGlobals &connectDB &finish
     41                &initGlobals &initPermissions &getPermissions
     42                &connectDB &finish
    3943                &addDomain &delDomain &domainName
    4044                &addGroup &delGroup &getChildren &groupName
     
    4448                &domStatus &importAXFR
    4549                %typemap %reverse_typemap
     50                %permissions
    4651                )]
    4752        );
     
    6772our %reverse_typemap;
    6873
     74our %permissions;
    6975
    7076##
     
    156162  }
    157163} # end initGlobals
     164
     165
     166## DNSDB::initPermissions()
     167# Set up permissions global
     168# Takes database handle and UID
     169sub initPermissions {
     170  my $dbh = shift;
     171  my $uid = shift;
     172
     173#  %permissions = $(getPermissions($dbh,'user',$uid));
     174  getPermissions($dbh, 'user', $uid, \%permissions);
     175
     176} # end initPermissions()
     177
     178
     179## DNSDB::getPermissions()
     180# Get permissions from DB
     181# Requires DB handle, group or user flag, ID, and hashref.
     182sub getPermissions {
     183  my $dbh = shift;
     184  my $type = shift;
     185  my $id = shift;
     186  my $hash = shift;
     187
     188  my $sql = qq(
     189        SELECT
     190        p.admin,p.self_edit,
     191        p.group_create,p.group_edit,p.group_delete,
     192        p.user_create,p.user_edit,p.user_delete,
     193        p.domain_create,p.domain_edit,p.domain_delete,
     194        p.record_create,p.record_edit,p.record_delete
     195        FROM permissions p
     196        );
     197  if ($type eq 'group') {
     198    $sql .= qq(
     199        JOIN groups g ON g.permission_id=p.permission_id
     200        WHERE g.group_id=?
     201        );
     202  } else {
     203    $sql .= qq(
     204        JOIN users u ON u.permission_id=p.permission_id
     205        WHERE u.user_id=?
     206        );
     207  }
     208
     209  my $sth = $dbh->prepare($sql);
     210
     211  $sth->execute($id) or die "argh: ".$sth->errstr;
     212
     213#  my $permref = $sth->fetchrow_hashref;
     214#  return $permref;
     215#  $hash = $permref;
     216# Eww.  Need to learn how to forcibly drop a hashref onto an existing hash.
     217  ($hash->{admin},$hash->{self_edit},
     218        $hash->{group_create},$hash->{group_edit},$hash->{group_delete},
     219        $hash->{user_create},$hash->{user_edit},$hash->{user_delete},
     220        $hash->{domain_create},$hash->{domain_edit},$hash->{domain_delete},
     221        $hash->{record_create},$hash->{record_edit},$hash->{record_delete})
     222        = $sth->fetchrow_array;
     223
     224} # end getPermissions()
     225
     226
     227## DNSDB::changePermissions()
     228# Update an ACL entry
     229# Takes a db handle, type, owner-id, and hashref for the changed permissions.
     230##fixme: Must handle case of changing object's permissions from inherited to custom
     231sub changePermissions {
     232  my $dbh = shift;
     233  my $type = shift;
     234  my $id = shift;
     235  my $newperms = shift;
     236
     237  # see if we're switching from inherited to custom
     238  my $sth = $dbh->prepare("SELECT (u.permission_id=g.permission_id) AS was_inherited".
     239        " FROM ".($type eq 'user' ? 'users' : 'groups')." u ".
     240        " JOIN groups g ON u.group_id=g.group_id ".
     241        " WHERE u.".($type eq 'user' ? 'user' : 'group')."_id=?");
     242  $sth->execute($id);
     243
     244} # end changePermissions()
    158245
    159246
     
    507594
    508595##fixme: add another table to hold name/email for log table?
     596die "dying horribly\n";
    509597
    510598    # once we get here, we should have suceeded.
  • trunk/dns.cgi

    r64 r65  
    104104if ($webvar{action}) {
    105105  if ($webvar{action} eq 'login') {
     106    # Snag ACL/permissions here too
    106107    my $sth = $dbh->prepare("SELECT user_id,group_id,password,firstname,lastname FROM users WHERE username=?");
    107108    $sth->execute($webvar{username});
     
    118119    $session->param('logingroup',$gid);
    119120    $session->param('curgroup',$gid);
     121    $session->param('uid',$uid);
    120122    $session->param('username',$webvar{username});
    121123
     
    139141} # handle global webvar{action}s
    140142
     143initPermissions($dbh,$session->param('uid'));
    141144
    142145## Default page is a login page
     
    521524  $page->param(delgroupname => groupName($dbh, $webvar{id}));
    522525
     526} elsif ($webvar{page} eq 'edgroup') {
     527
     528  if ($webvar{action} eq 'updperms') {
     529    # extra safety check;  make sure user can't construct a URL to bypass ACLs
     530    my %curperms;
     531    getPermissions($dbh, 'group', $webvar{gid}, \%curperms);
     532    foreach (('group_edit','group_create','group_delete',
     533                'user_edit','user_create','user_delete',
     534                'domain_edit','domain_create','domain_delete',
     535                'record_edit','record_create','record_delete',
     536                'self_edit')
     537                ) {
     538      $webvar{$_} = 0 if !defined($webvar{$_});
     539      $webvar{$_} = 1 if $webvar{$_} eq 'on';
     540push @debugbits, "$_ has changed: '$curperms{$_}' => '$webvar{$_}'<br>\n" if $curperms{$_} ne $webvar{$_};
     541      if ($permissions{admin} || $permissions{$_}) {
     542        if (($webvar{$_} eq 'on' && !$curperms{$_}) or
     543                (!$webvar{$_} && $curperms{$_})) {
     544          push @debugbits, '&nbsp;&nbsp;'."may update $_<br>\n";
     545        }
     546      }
     547    }
     548  }
     549  $page->param(gid => $webvar{gid});
     550  $page->param(grpmeddle => groupName($dbh, $webvar{gid}));
     551  my %grpperms;
     552  getPermissions($dbh, 'group', $webvar{gid}, \%grpperms);
     553#  unless (0) {
     554  foreach (('group_edit','group_create','group_delete',
     555                'user_edit','user_create','user_delete',
     556                'domain_edit','domain_create','domain_delete',
     557                'record_edit','record_create','record_delete',
     558                'self_edit')
     559                ) {
     560#push @debugbits, "$_ => admin? '$permissions{admin}' may_$_? '$permissions{$_}' group? '$grpperms{$_}'<br>\n";
     561    $page->param("may_$_" => ($permissions{admin} || $permissions{$_}));
     562    $page->param($_ => $grpperms{$_});
     563  }
     564#  }
     565#  my %grpperms = getPermissions('group',$webvar{group});
     566
    523567} elsif ($webvar{page} eq 'useradmin') {
    524568
     
    535579  # foo?
    536580  fill_actypelist();
     581  fill_clonemelist();
    537582
    538583} elsif ($webvar{page} eq 'adduser') {
     
    544589    $msg = "Passwords don't match";
    545590  } else {
     591# assemble a permission string - far simpler than trying to pass an
     592# indeterminate set of permission flags individually
     593my $permstring;
     594if ($webvar{perms_type} eq 'custom') {
     595  $permstring = 'C:,g:,u:,d:,r:';
     596  $page->param(perm_custom => 1);
     597} elsif ($webvar{perms_type} eq 'clone') {
     598  $permstring = 'c:';
     599  $page->param(perm_clone => 1);
     600} else {
     601  $permstring = 'i';
     602#  $page->param(perm_inherit => 1);
     603}
    546604    ($code,$msg) = addUser($dbh,$webvar{uname}, $webvar{group}, $webvar{pass1},
    547605        ($webvar{makeactive} eq 'on' ? 1 : 0), $webvar{accttype},
     
    565623    $page->param(errmsg => $msg);
    566624    fill_actypelist();
     625    fill_clonemelist();
    567626  }
    568627
     
    585644      list_users($curgroup);
    586645    } else {
    587       # success.  go back to the domain list, do not pass "GO"
     646      # success.  go back to the user list, do not pass "GO"
    588647##log
    589648      logaction(0, $session->param("username"), $webvar{group}, "Added domain $webvar{domain}");
     
    594653    changepage(page => "useradmin");
    595654  }
     655
     656} elsif ($webvar{page} eq 'edituser') {
    596657
    597658} elsif ($webvar{page} eq 'dnsq') {
     
    781842  my $tmpgrplist = fill_grptree($logingroup,$curgroup);
    782843  $page->param(grptree => $tmpgrplist);
    783 
     844  $page->param(subs => ($tmpgrplist ? 1 : 0));  # probably not useful to pass gobs of data in for a boolean
    784845  $page->param(inlogingrp => $curgroup == $logingroup);
    785846
     
    848909  return if $#childlist == -1;
    849910  my @grouplist;
     911  my $foome = 0;
    850912  foreach (@childlist) {
    851913    my %row;
     
    853915    $row{grpname} = "<b>$row{grpname}</b>" if $_ == $cur;
    854916    $row{subs} = fill_grptree($_,$cur);
     917    $row{last} = 1 if ++$foome > $#childlist;
    855918    push @grouplist, \%row;
    856919  }
     
    9901053
    9911054  $page->param(actypelist       => \@actypes);
     1055}
     1056
     1057sub fill_clonemelist {
     1058  my $sth = $dbh->prepare("SELECT username,user_id FROM users WHERE group_id=$curgroup");
     1059  $sth->execute;
     1060
     1061  my @clonesrc;
     1062  while (my ($username,$uid) = $sth->fetchrow_array) {
     1063    my %row = (
     1064        username => $username,
     1065        uid => $uid,
     1066        selected => ($webvar{clonesrc} == $uid ? 1 : 0)
     1067        );
     1068    push @clonesrc, \%row;
     1069  }
     1070  $page->param(clonesrc => \@clonesrc);
    9921071}
    9931072
  • trunk/dns.sql

    r50 r65  
    77
    88-- tabledefs and preloaded data bits
     9CREATE TABLE permissions (
     10    permission_id SERIAL NOT NULL,
     11    admin boolean DEFAULT 'n' NOT NULL,
     12    self_edit boolean DEFAULT 'n' NOT NULL,
     13    group_create boolean DEFAULT 'n' NOT NULL,
     14    group_edit boolean DEFAULT 'n' NOT NULL,
     15    group_delete boolean DEFAULT 'n' NOT NULL,
     16    user_create boolean DEFAULT 'n' NOT NULL,
     17    user_edit boolean DEFAULT 'n' NOT NULL,
     18    user_delete boolean DEFAULT 'n' NOT NULL,
     19    domain_create boolean DEFAULT 'n' NOT NULL,
     20    domain_edit boolean DEFAULT 'n' NOT NULL,
     21    domain_delete boolean DEFAULT 'n' NOT NULL,
     22    record_create boolean DEFAULT 'n' NOT NULL,
     23    record_edit boolean DEFAULT 'n' NOT NULL,
     24    record_delete boolean DEFAULT 'n' NOT NULL
     25);
     26
     27-- Need *two* basic permissions;  one for the initial group, one for the default admin user
     28COPY permissions (permission_id, admin, self_edit, group_create, group_edit group_delete, user_create, user_edit, user_delete, domain_create, domain_edit, domain_delete, record_create, record_edit, record_delete) FROM stdin;
     291       n       n       n       n       n       n       n       n       n       n       n       n       n       n
     302       y       n       n       n       n       n       n       n       n       n       n       n       n       n
     31\.
     32
    933CREATE TABLE groups (
    1034    group_id serial NOT NULL,
    1135    parent_group_id integer DEFAULT 1 NOT NULL,
     36    permission_id integer DEFAULT 1 NOT NULL,
    1237    group_name character varying(255) DEFAULT ''::character varying NOT NULL
    1338);
     
    1540-- Provide a basic default group
    1641COPY groups (group_id, parent_group_id, group_name) FROM stdin;
    17 1       1       default
     421       1       1       default
    1843\.
    1944
     
    151176    "type" character(1) DEFAULT 'S'::bpchar NOT NULL,
    152177    status integer DEFAULT 1 NOT NULL,
    153     acl character varying(40) DEFAULT 'b'::character varying NOT NULL
     178    acl character varying(40) DEFAULT 'b'::character varying NOT NULL,
     179    permission_id DEFAULT 1 NOT NULL,
    154180);
    155181
    156182-- create initial default user?  may be better to create an "initialize" script or something
    157183COPY users (user_id, group_id, username, "password", firstname, lastname, phone, "type", status, acl) FROM stdin;
    158 1       1       test@test       $1$BByge8u2$48AaGX3YeHplfErX5Tlqa1      \N      \N      \N      S       1       A
     1841       1       test@test       $1$BByge8u2$48AaGX3YeHplfErX5Tlqa1      \N      \N      \N      S       1       A       2
    159185\.
    160186
     
    174200
    175201-- primary keys
     202ALTER TABLE ONLY permissions
     203    ADD CONSTRAINT permissions_permission_id_key UNIQUE (permission_id);
     204
    176205ALTER TABLE ONLY groups
    177206    ADD CONSTRAINT groups_group_id_key UNIQUE (group_id);
     
    199228
    200229-- foreign keys
     230-- fixme: permissions FK refs
    201231ALTER TABLE ONLY domains
    202232    ADD CONSTRAINT "$1" FOREIGN KEY (group_id) REFERENCES groups(group_id);
     
    216246-- set sequence start values - make sure we don't screw up adding
    217247-- records to tables that already have a few entries
     248SELECT pg_catalog.setval('permissions_permission_id_seq', 2, true);
    218249
    219250SELECT pg_catalog.setval('groups_group_id_seq', 52, true);
  • trunk/templates/dns.css

    r59 r65  
    4242        width: 100%;
    4343}
     44table.border {
     45        border: thin solid #000000;
     46}
    4447
    4548tr.row0 {
     
    6164        background-color: #F0F0F0;
    6265        text-align: left;
     66}
     67
     68th {
     69        background-color: #F0F0F0;
     70        font-size: 1.1em;
     71        font-weight:normal;
     72        padding: 4px;
    6373}
    6474
     
    128138        text-align: right;
    129139}
     140td.border {
     141        background-color: #e0e0e0;
     142        border: solid 1px #101010;
     143        padding: 2px;
     144}
     145td.noaccess {
     146        background-color: #ffe8e8;
     147        color: #2f0000;
     148}
    130149
    131150.meat {
     
    136155        font-size: 10px;
    137156}
     157
    138158ul {
    139159        margin-left: 10px;
     
    143163/* F*** ME BUT THIS LOOKS LIKE CRAP! */
    144164/* Need to find a way to vertically centre the plus image on the text.  >:(  */
    145 /*li.hassub {
    146         list-style: none outside url('../images/plus.gif');
     165li.hassub {
     166        background-image: url('../images/fwd.png');
     167        background-repeat: no-repeat;
     168        background-position: 0px 1px;
     169        padding-left: 10px;
     170        //list-style: none outside url('../images/fwd.png');
     171        margin-left: 0px;
    147172}
    148173li.leaf {
    149         list-style: none outside none;
    150 } */
    151 
     174        //list-style: none outside none;
     175        //margin-left: 0px;
     176}
     177li.lastinlvl {
     178        background-image: url('../images/ASC.png');
     179        background-repeat: no-repeat;
     180        background-position: 0px 1px;
     181        padding-left: 10px;
     182        //list-style: none outside url('../images/fwd.png');
     183        margin-left: 0px;
     184}
     185ul.grptree {
     186        list-style-type: none;
     187        padding: 0px;
     188        margin: 0px;
     189}
    152190#grptree {
    153         //margin-left: 15px;
     191        //margin-left: 10px;
    154192}
    155193/* general classes */
  • trunk/templates/grpman.tmpl

    r44 r65  
    3131<TMPL_LOOP name=grouptable>
    3232<tr class="row<TMPL_VAR name=bg>">
    33         <td align="left"><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=grpman&amp;action=chgroup&amp;group=<TMPL_VAR NAME=groupid>"><TMPL_VAR NAME=groupname></a></td>
     33        <td align="left"><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=edgroup&amp;gid=<TMPL_VAR NAME=groupid>"><TMPL_VAR NAME=groupname></a></td>
    3434        <td><TMPL_VAR name=pgroup></td>
    3535        <td><TMPL_VAR name=nusers></td>
  • trunk/templates/grptree.tmpl

    r41 r65  
    11<ul>
    2 <TMPL_LOOP NAME=treelvl><li class="<TMPL_IF NAME=subs>hassub<TMPL_ELSE>leaf</TMPL_IF>"><TMPL_VAR NAME=grpname>
     2<TMPL_LOOP NAME=treelvl><li class="<TMPL_IF NAME=subs>hassub<TMPL_ELSE>leaf</TMPL_IF><TMPL_IF last> lastinlvl</TMPL_IF>"><TMPL_VAR NAME=grpname>
    33<TMPL_VAR NAME=subs></li>
    44</TMPL_LOOP></ul>
  • trunk/templates/menu.tmpl

    r43 r65  
    2323<a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=grpman">Manage groups</a><br />
    2424<div id="grptree">
    25 <ul>
    26 <li><TMPL_IF inlogingrp><b><TMPL_VAR NAME=logingrp></b><TMPL_ELSE><TMPL_VAR NAME=logingrp></TMPL_IF>
     25<ul class="grptree">
     26<li class="<TMPL_IF NAME=subs>hassub<TMPL_ELSE>leaf</TMPL_IF>"><TMPL_IF inlogingrp><b><TMPL_VAR NAME=logingrp></b><TMPL_ELSE><TMPL_VAR NAME=logingrp></TMPL_IF>
    2727<TMPL_VAR NAME=grptree>
    2828</li>
  • trunk/templates/newuser.tmpl

    r38 r65  
    1212<input type="hidden" name="newuser" value="yes" />
    1313
    14 <table class="container" width="450">
    15 <tr><td>
    16     <table border="0" cellspacing="2" cellpadding="2" width="100%">
    17 <TMPL_IF add_failed>    <tr><td class="errhead" colspan="2">Error adding user <TMPL_VAR NAME=uname>: <TMPL_VAR NAME=errmsg></td></tr></TMPL_IF>
     14<table border="0" cellspacing="2" cellpadding="2" width="450">
     15<TMPL_IF add_failed>    <tr>
     16                <td class="errhead" colspan="2">Error adding user <TMPL_VAR NAME=uname>: <TMPL_VAR NAME=errmsg></td>
     17        </tr></TMPL_IF>
    1818        <tr class="darkrowheader"><td colspan="2" align="center">Add User</td></tr>
    1919
     
    5353                <td>Create as active user</td><td><input type="checkbox" name="makeactive" checked="checked" /></td>
    5454        </tr>
     55
     56        <tr>
     57                <td colspan="2">
     58
     59<table style="border: thin solid #000000;" border="0" cellspacing="5" cellpadding="0" width="100%">
     60<tr class="tableheader">
     61        <td align="center" colspan="5">
     62        <input type="radio" name="perms_type" value="inherit" <TMPL_IF add_failed><TMPL_IF perm_inherit>checked="checked"</TMPL_IF><TMPL_ELSE>checked="checked"</TMPL_IF>/> Inherit permissions from group
     63        </td>
     64</tr>
     65<tr>
     66        <td align="right">Group:</td>
     67        <td><input type="checkbox"<TMPL_IF i_grped> checked="checked"</TMPL_IF> disabled="disabled" /> Edit</td>
     68        <td><input type="checkbox"<TMPL_IF i_grpcreate> checked="checked"</TMPL_IF> disabled="disabled" /> Create</td>
     69        <td><input type="checkbox"<TMPL_IF i_grpdel> checked="checked"</TMPL_IF> disabled="disabled" /> Delete</td>
     70</tr>
     71<tr>
     72        <td align="right">User:</td>
     73        <td><input type="checkbox"<TMPL_IF i_usered> checked="checked"</TMPL_IF> disabled="disabled" /> Edit</td>
     74        <td><input type="checkbox"<TMPL_IF i_usercreate> checked="checked"</TMPL_IF> disabled="disabled" /> Create</td>
     75        <td><input type="checkbox"<TMPL_IF i_userdel> checked="checked"</TMPL_IF> disabled="disabled" /> Delete</td>
     76</tr>
     77<tr>
     78        <td align="right">Domain:</td>
     79        <td><input type="checkbox"<TMPL_IF i_domed> checked="checked"</TMPL_IF> disabled="disabled" /> Edit</td>
     80        <td><input type="checkbox"<TMPL_IF i_domcreate> checked="checked"</TMPL_IF> disabled="disabled" /> Create</td>
     81        <td><input type="checkbox"<TMPL_IF i_domdel> checked="checked"</TMPL_IF> disabled="disabled" /> Delete</td>
     82        <!-- td>+ Delegate</td -->
     83</tr>
     84<tr>
     85        <td align="right">Domain Record:</td>
     86        <td><input type="checkbox"<TMPL_IF i_reced> checked="checked"</TMPL_IF> disabled="disabled" /> Edit</td>
     87        <td><input type="checkbox"<TMPL_IF i_reccreate> checked="checked"</TMPL_IF> disabled="disabled" /> Create</td>
     88        <td><input type="checkbox"<TMPL_IF i_recdel> checked="checked"</TMPL_IF> disabled="disabled" /> Delete</td>
     89        <!-- td>+ Delegate</td -->
     90</tr>
     91<tr>
     92        <td align="right">Self:</td>
     93        <td><input type="checkbox"<TMPL_IF i_edself> checked="checked"</TMPL_IF> disabled="disabled" /> Edit</td>
     94</tr>
     95
     96<tr class="tableheader">
     97        <td align="center" colspan="5">
     98        <input type="radio" name="perms_type" value="clone" <TMPL_IF add_failed><TMPL_IF perm_clone> checked="checked"</TMPL_IF></TMPL_IF>/> Clone permissions from an existing user
     99        </td>
     100</tr>
     101<tr>
     102        <td align="center" colspan="5">
     103        Note: Only users in the current group may be cloned<br>
     104        <select name="clonesrc">
     105        <option>-</option>
     106        <TMPL_LOOP name=clonesrc><option value="<TMPL_VAR NAME=uid>"<TMPL_IF selected> selected</TMPL_IF>><TMPL_VAR NAME=username></option>
     107        </TMPL_LOOP></select>
     108        </td>
     109</tr>
     110<tr class="tableheader">
     111        <td align="center" colspan="5">
     112                <input type="radio" name="perms_type" value="custom" <TMPL_IF add_failed><TMPL_IF perm_custom> checked="checked"</TMPL_IF></TMPL_IF>/> Specify permissions
     113        </td>
     114</tr>
     115<TMPL_INCLUDE name="permlist_enabled.tmpl">
     116
     117</table>
     118
     119                </td>
     120        </tr>
     121
    55122        <tr><td colspan="2" class="tblsubmit"><input type="submit" value="Add user" /></td></tr>
     123
    56124<tr><td colspan="2">tmp note:  radio button select "group template" vs "clone user"?</td></tr>
    57125    </table>
    58     </td>
    59 </tr>
    60 </table>
    61126
    62127</fieldset>
  • trunk/templates/useradmin.tmpl

    r59 r65  
    3131<TMPL_LOOP name=usertable>
    3232<tr class="row<TMPL_VAR name=bg>">
    33         <td align="left"><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=useradmin&amp;action=chuser&amp;user=<TMPL_VAR NAME=userid>"><TMPL_VAR NAME=username></a></td>
     33        <td align="left"><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=edituser&amp;user=<TMPL_VAR NAME=userid>"><TMPL_VAR NAME=username></a></td>
    3434        <td class="data_nowrap"><TMPL_VAR name=userfull></td>
    3535        <td><TMPL_VAR name=usertype></td>
Note: See TracChangeset for help on using the changeset viewer.