Wikka : UserAdmin

HomePage :: Categories :: Index :: Changes :: Comments :: Documentation :: Blog :: Login/Register
Most recent edit on 2008-03-10 11:12:53 by DarTar [link to docs]

Additions:
This is the development page for the User Administration module. The User Administration module has been committed to the 1.1.6.4 branch and will be available with the 1.1.6.4 release. Documentation http://docs.wikkawiki.org/UserAdministration

Deletions:
This is the development page for the User Administration module. The User Administration module has been committed to the 1.1.6.4 branch and will be available with the 1.1.6.4 release.



Edited on 2008-01-28 00:15:44 by BrianKoontz [Modified links pointing to docs server]

No differences.


Edited on 2007-11-26 14:32:11 by BrianKoontz [update]

Additions:


Deletions:




Edited on 2007-11-25 17:18:46 by DarTar [adding release information]

Additions:
This is the development page for the User Administration module. The User Administration module has been committed to the 1.1.6.4 branch and will be available with the 1.1.6.4 release.

Deletions:
This is the development page for the User Administration action.



Edited on 2007-06-30 13:10:10 by DarTar [reverted]

Additions:

User Administration Module

The code below must be saved as actions/useradmin.php and can be used by adding {{useradmin}} in the body of a page.
Here's a preview of the interface (the email and IP fields are masked for privacy reasons)

sample output

User Administration


Filter view:

Records (857): 1-10 | 11-20
(Sorted by:signuptime, desc)
  User Name Email Signup Time Signup IP O E C Actions
HypeXR scott@xxxx.xx 2005-08-01 09:01:54 XX.XX.XX.XX 1 2 1 delete :: feedback
WiebTest wiebren.w@xxxx.xx 2005-07-31 19:35:26 XX.XX.XX.XX 0 0 0 delete :: feedback
DennyShimkoski denny@xxxx.xx 2005-07-31 00:22:58 XX.XX.XX.XX 6 38 1 delete :: feedback
RobinW wikka@xxxx.xx 2005-07-30 02:06:20 XX.XX.XX.XX 0 0 1 delete :: feedback
VictorManuelVarela vmvarela@xxxx.xx 2005-07-30 00:01:13 XX.XX.XX.XX 1 6 0 delete :: feedback
EniBevoli enibevoli@xxxx.xx 2005-07-29 12:13:44 XX.XX.XX.XX 0 4 1 delete :: feedback
MyPrivateWiki serayamaouche@xxxx.xx 2005-07-28 20:30:36 XX.XX.XX.XX 0 0 0 delete :: feedback
DaSt dstolars@xxxx.xx 2005-07-27 17:32:06 XX.XX.XX.XX 0 0 0 delete :: feedback
WikiSop mail-to-sophie@xxxx.xx 2005-07-27 14:59:07 XX.XX.XX.XX 0 0 0 delete :: feedback
Aragorn2005 mail.matthias.schulz@xxxx.xx 2005-07-27 08:48:46 XX.XX.XX.XX 0 0 0 delete :: feedback
Mass-action[Check all | Uncheck all]


Current version

(2005-08-31): 0.5
Files needed
(2004-09-16):

To do


The code

actions/useradmin.php

Note: this action uses the ipaddress field that has been added as a beta antispam feature to this server. Lines relative to this field have been commented out in the code to allow installation on a native wikka package
The code has also been adapted to take advantage of the getCount() method, which is required for the action to work.
  1. <?php
  2. /**
  3. * Display a module for user management.
  4. *
  5. * This action allows admins to display information on registered users.
  6. * Users can be searched, paged, filtered. User-related statistics are given,
  7. * showing the number of commented, created and modified pages. A feedback
  8. * handler allows admins to send an email to single users. If the current user
  9. * is not an administrator, then the lastuser action is displayed instead.
  10. *
  11. * @package        Actions
  12. * @name        Useradmin
  13. *
  14. * @author        {@link http://wikka.jsnx.com/DarTar Dario Taraborelli}
  15. * @version        0.5
  16. * @since        Wikka 1.1.X.X
  17. *
  18. * @input        integer $colcolor  optional: enables color for statistics columns
  19. *                1: enables colored columns;
  20. *                0: disables colored columns;
  21. *                default: 1;
  22. * @input        integer $rowcolor  optional: enables alternate row colors
  23. *                1: enables colored rows;
  24. *                0: disables colored rows;
  25. *                default: 1;
  26. *
  27. * @output        A list of users registered on the current server.
  28. *
  29. * @todo
  30. *            - mass-operations;
  31. *            - deleting/banning users;
  32. *            - integrate with other admin modules.
  33. */
  34. //utilities
  35. /**
  36. * Build an array of numbers consisting of 'ranges' with increasing step size in each 'range'.
  37. *
  38. * A list of numbers like this is useful for instance for a dropdown to choose
  39. * a period expressed in number of days: a difference between 2 and 5 days may
  40. * be significant while that between 92 and 95 may not be.
  41. *
  42. * @author        {@link http://wikka.jsnx.com/JavaWoman JavaWoman}
  43. * @copyright    Copyright (c) 2005, Marjolein Katsma
  44. * @license        http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
  45. * @version        1.0
  46. *
  47. * @param    mixed    $limits    required: single integer or array of integers;
  48. *                    defines the upper limits of the ranges as well as the next step size
  49. * @param    int        $max    required: upper limit for the whole list
  50. *                    (will be included if smaller than the largest limit)
  51. * @param    int        $firstinc optional: increment for the first range; default 1
  52. * @return    array    resulting list of numbers
  53. */
  54. // utilities
  55. function optionRanges($limits, $max, $firstinc = 1)
  56. {
  57.     // initializations
  58.     if (is_int($limits)) $limits = array($limits);
  59.     if ($firstinc < 1) $firstinc = 1;
  60.     $opts = array();
  61.     $inc = $firstinc;
  62.     // first element is the first increment
  63.     $opts[] = $inc;
  64.     // each $limit is the upper limit of a 'range'
  65.     foreach ($limits as $limit)
  66.     {
  67.         for ($i = $inc + $inc; $i <= $limit && $i < $max; $i += $inc)
  68.         {
  69.             $opts[] = $i;
  70.         }
  71.         // we quit at $max, even if there are more $limit elements
  72.         if ($limit >= $max)
  73.         {
  74.             // add $max to the list; then break out of the loop
  75.             $opts[] = $max;
  76.             break;
  77.         }
  78.         // when $limit is reached, it becomes the new start and increment for the next 'range'
  79.         $inc = $limit;
  80.     }
  81.     return $opts;
  82. }
  83. // restrict access to admins
  84. if ($this->IsAdmin($this->GetUser())) {
  85.     // -------------------------------------
  86.     // set default values as constants
  87.     define('DEFAULT_RECORDS_LIMIT', '10'); # number of records per page
  88.     define('DEFAULT_MIN_RECORDS_DISPLAY', '5'); # min number of records
  89.     define('DEFAULT_RECORDS_RANGE',serialize(array('10','50','100','500','1000'))); #range array for records pager
  90.     define('DEFAULT_SORT_FIELD', 'signuptime'); # sort field
  91.     define('DEFAULT_SORT_ORDER', 'desc'); # sort order, ascendant or descendant
  92.     define('DEFAULT_START', '0'); # start record
  93.     define('DEFAULT_SEARCH', ''); # keyword to restrict search
  94.     define('ALTERNATE_ROW_COLOR', '1'); # switch alternate row color
  95.     define('STAT_COLUMN_COLOR', '1'); # switch color for statistics columns
  96.     // -------------------------------------
  97.     // User-interface: icons
  98.    
  99.     define('OWNED_ICON', 'images/icons/16x16/stock_keyring.png');
  100.     define('EDITS_ICON', 'images/icons/16x16/text-editor.png');
  101.     define('COMMENTS_ICON', 'images/icons/16x16/stock_help-agent.png');
  102.     // -------------------------------------
  103.     // User-interface: strings
  104.     define('PAGE_TITLE','User Administration');
  105.     define('FORM_LEGEND','Filter view:');
  106.     define('FORM_SEARCH_STRING_LABEL','Search user:');
  107.     define('FORM_SEARCH_STRING_TITLE','Enter a search string');
  108.     define('FORM_SEARCH_SUBMIT','Submit');
  109.     define('FORM_PAGER_LABEL_BEFORE','Show');
  110.     define('FORM_PAGER_TITLE','Select records-per-page limit');
  111.     define('FORM_PAGER_LABEL_AFTER','records per page');
  112.     define('FORM_PAGER_SUBMIT','Apply');
  113.     define('FORM_PAGER_LINK','Show records from %d to %d');
  114.     define('FORM_RESULT_INFO','Records');
  115.     define('FORM_RESULT_SORTED_BY','Sorted by:');
  116.     define('TABLE_HEADING_USERNAME','User Name');
  117.     define('TABLE_HEADING_USERNAME_TITLE','Sort by user name');
  118.     define('TABLE_HEADING_EMAIL','Email');
  119.     define('TABLE_HEADING_EMAIL_TITLE','Sort by email');
  120.     define('TABLE_HEADING_SIGNUPTIME','Signup Time');
  121.     define('TABLE_HEADING_SIGNUPTIME_TITLE','Sort by signup time');
  122.     define('TABLE_HEADING_SIGNUPIP','Signup IP');
  123.     define('TABLE_HEADING_SIGNUPIP_TITLE','Sort by signup IP');
  124.     define('TABLE_SUMMARY','List of users registered on this server');
  125.     define('TABLE_HEADING_OWNED_TITLE','Owned Pages');
  126.     define('TABLE_HEADING_EDITS_TITLE','Page Edits');
  127.     define('TABLE_HEADING_COMMENTS_TITLE','Comments');
  128.     define('ACTION_DELETE_LINK_TITLE','Remove user %s');
  129.     define('ACTION_FEEDBACK_LINK_TITLE','Send feedback to user %s');
  130.     define('ACTION_DELETE_LINK','delete');
  131.     define('ACTION_FEEDBACK_LINK','feedback');
  132.     define('TABLE_CELL_OWNED_TITLE','Display pages owned by %s (%d)');
  133.     define('TABLE_CELL_EDITS_TITLE','Display page edits by %s (%d)');
  134.     define('TABLE_CELL_COMMENTS_TITLE','Display comments by %s (%d)');
  135.     define('SELECT_RECORD_TITLE','Select %s');
  136.     define('CHECK_ALL_TITLE','Check all records');
  137.     define('CHECK_ALL','Check all');
  138.     define('UNCHECK_ALL_TITLE','Uncheck all records');
  139.     define('UNCHECK_ALL','Uncheck all');
  140.     define('FORM_MASSACTION_LEGEND','Mass-action');
  141.     define('FORM_MASSACTION_LABEL','With selected');
  142.     define('FORM_MASSACTION_SELECT_TITLE','Choose action to apply to selected records');
  143.     define('FORM_MASSACTION_OPT_DELETE','Remove all');
  144.     define('FORM_MASSACTION_OPT_FEEDBACK','Send feedback to all');
  145.     define('FORM_MASSACTION_SUBMIT','Submit');
  146.     define('ERROR_NO_MATCHES','Sorry, there are no users matching "%s"');
  147.                
  148.     //initialize row & column colors variables
  149.     $r = 1; #initialize row counter
  150.     $r_color = ALTERNATE_ROW_COLOR; #get alternate row color option
  151.     $c_color = STAT_COLUMN_COLOR; #get column color option
  152.     // record dropdown
  153.     $user_limits = unserialize(DEFAULT_RECORDS_RANGE);
  154.     // pager
  155.     $prev = '';       
  156.     $next = '';       
  157.    
  158.     //override defaults with action parameters
  159.     if (is_array($vars))
  160.     {
  161.         foreach ($vars as $param => $value)
  162.         {
  163.             switch ($param)
  164.             {
  165.                 case 'colcolor':
  166.                     $c_color = (preg_match('/[01]/',$value))? $value : STAT_COLUMN_COLOR;
  167.                     break;
  168.                 case 'rowcolor':
  169.                     $r_color = (preg_match('/[01]/',$value))? $value : ALTERNATE_ROW_COLOR;
  170.                     break;
  171.             }
  172.         }
  173.     }
  174.    
  175.     //perform actions if required
  176.     if ($_GET['action'] == 'feedback' || $_REQUEST['mail'])
  177.     {
  178.         echo $this->Action('userfeedback');
  179.     }
  180.     elseif ($_GET['action'] == 'owned')
  181.     {
  182.         echo $this->Action('userpages');
  183.     }
  184.     elseif ($_GET['action'] == 'changes')
  185.     {
  186.         echo $this->Action('userchanges');
  187.     }
  188.     elseif ($_GET['action'] == 'comments')
  189.     {
  190.         echo $this->Action('usercomments');
  191.     }
  192.     else
  193.     {
  194.    
  195.         // process URL variables
  196.         # JW 2005-07-19 some modifications to avoid notices but these are still not actually secure
  197.    
  198.         // number of records per page
  199.         if (isset($_POST['l']))
  200.             $l = $_POST['l'];
  201.         elseif (isset($_GET['l']))
  202.             $l = $_GET['l'];
  203.         else
  204.             $l = DEFAULT_RECORDS_LIMIT;
  205.    
  206.         // sort field
  207.         $sort = (isset($_GET['sort'])) ? $_GET['sort'] : DEFAULT_SORT_FIELD;
  208.         // sort order
  209.         $d = (isset($_GET['d'])) ? $_GET['d'] : DEFAULT_SORT_ORDER;
  210.         // start record
  211.         $s = (isset($_GET['s'])) ? $_GET['s'] : DEFAULT_START;
  212.    
  213.         // search string
  214.         if (isset($_POST['q']))
  215.             $q = $_POST['q'];
  216.         elseif (isset($_GET['q']))
  217.             $q = $_GET['q'];
  218.         else
  219.             $q = DEFAULT_SEARCH;
  220.    
  221.         // select all
  222.         $checked = '';
  223.         if (isset($_GET['selectall']))
  224.         {
  225.             $checked = (1 == $_GET['selectall']) ? ' checked="checked"' : '';
  226.         }
  227.    
  228.         // restrict MySQL query by search string
  229.         $where = ('' == $q) ? "1" : "`name` LIKE '%".$q."%'";
  230.         // get total number of users
  231.         $numusers = $this->getCount('users', $where);
  232.    
  233.         // print page header
  234.         echo $this->Format('==== '.PAGE_TITLE.' ==== --- ');
  235.    
  236.         // build pager form   
  237.         $form = $this->FormOpen('','','post','user_admin_panel');
  238.         $form .= '<fieldset><legend>'.FORM_LEGEND.'</legend>'."\n";
  239.         $form .= '<label for="q">'.FORM_SEARCH_STRING_LABEL.'</label> <input type ="text" id="q" name="q" title="'.FORM_SEARCH_STRING_TITLE.'" size="20" maxlength="50" value="'.$q.'"/> <input type="submit" value="'.FORM_SEARCH_SUBMIT.'" /><br />'."\n";
  240.         // ranged drop-down
  241.         $users_opts = optionRanges($user_limits,$numusers,DEFAULT_MIN_RECORDS_DISPLAY);
  242.         $form .= '<label for="l">'.FORM_PAGER_LABEL_BEFORE.'</label> '."\n";
  243.         $form .= '<select name="l" id="l" title="'.FORM_PAGER_TITLE.'">'."\n";
  244.         // build drop-down
  245.         foreach ($users_opts as $opt) {
  246.             $selected = ($opt == $l) ? ' selected="selected"' : '';
  247.             $form .= '<option value="'.$opt.'"'.$selected.'>'.$opt.'</option>'."\n";
  248.         }
  249.         $form .=  '</select> <label for="l">'.FORM_PAGER_LABEL_AFTER.'</label> <input type="submit" value="'.FORM_PAGER_SUBMIT.'" /><br />'."\n";
  250.    
  251.         // build pager links
  252.         if ($s > 0)
  253.             $prev = '<a href="' .$this->Href('','','l='.$l.'&sort='.$sort.'&d='.$d.'&s='.($s-$l)).'&q='.$q.'" title="'.sprintf(FORM_PAGER_LINK, ($s-$l+1), $s).'">'.($s-$l+1).'-'.$s.'</a> |  '."\n";
  254.         if ($numusers > ($s + $l))
  255.             $next = ' | <a href="'.$this->Href('','','l='.$l.'&sort='.$sort.'&d='.$d.'&s='.($s+$l)).'&q='.$q.'" title="'.sprintf(FORM_PAGER_LINK, ($s+$l+1), ($s+2*$l)).'">'.($s+$l+1).'-'.($s+2*$l).'</a>'."\n";
  256.         $form .= FORM_RESULT_INFO.' ('.$numusers.'): '.$prev.($s+1).'-'.($s+$l).$next.'<br />'."\n";
  257.         $form .= '('.FORM_RESULT_SORTED_BY.'<em>'.$sort.', '.$d.'</em>)'."\n";
  258.         $form .= '</fieldset>'.$this->FormClose()."\n";
  259.         // print form
  260.         echo $form;
  261.    
  262.         // get user list
  263.         $userdata = $this->LoadAll("SELECT * FROM ".$this->config["table_prefix"]."users WHERE ".
  264.         $where." ORDER BY ".$sort." ".$d." limit ".$s.", ".$l);
  265.    
  266.         if ($userdata) {
  267.    
  268.             // build header links
  269.             $nameheader = '<a href="'.$this->Href('','', (($sort == 'name' && $d == 'asc')? 'l='.$l.'&sort=name&d=desc' : 'l='.$l.'&sort=name&d=asc')).'" title="'.TABLE_HEADING_USERNAME_TITLE.'">'.TABLE_HEADING_USERNAME.'</a>';
  270.             $emailheader = '<a href="'.$this->Href('','', (($sort == 'email' && $d == 'asc')? 'l='.$l.'&sort=email&d=desc' : 'l='.$l.'&sort=email&d=asc')).'" title="'.TABLE_HEADING_EMAIL_TITLE.'">'.TABLE_HEADING_EMAIL.'</a>';
  271.             $timeheader = '<a href="'.$this->Href('','', (($sort == 'signuptime' && $d == 'desc')? 'l='.$l.'&sort=signuptime&d=asc' : 'l='.$l.'')).'" title="'.TABLE_HEADING_SIGNUPTIME_TITLE.'">'.TABLE_HEADING_SIGNUPTIME.'</a>';
  272.             /*$ipheader = '<a href="'.$this->Href('','', (($sort == 'ipaddress' && $d == 'desc')? 'l='.$l.'&sort=ipaddress&d=asc' : 'l='.$l.'&sort=ipaddress&d=desc')).'" title="'.TABLE_HEADING_SIGNUPIP_TITLE.'">'.TABLE_HEADING_SIGNUPIP.'</a>'; # installed as beta feature at wikka.jsnx.com  */
  273.    
  274.             // build table headers
  275.             $htmlout = "<table summary=\"".TABLE_SUMMARY."\" border=\"1px\" id=\"admin_table\">\n<thead>\n".
  276.               "  <tr>\n".
  277.                 "    <th> </th>\n".
  278.                 "    <th>".$nameheader."</th>\n".
  279.                 "    <th>".$emailheader."</th>\n".
  280.                 "    <th>".$timeheader."</th>\n".
  281.                 /* "    <th>".$ipheader."</th>\n". # installed as beta feature at wikka.jsnx.com */
  282.                 "    <th class=\"number ".(($c_color == 1)? ' c1' : '')."\" title=\"".TABLE_HEADING_OWNED_TITLE."\">;<img src=\"".OWNED_ICON."\" alt=\"O\"/></th>\n".
  283.                 "    <th class=\"number ".(($c_color == 1)? ' c2' : '')."\" title=\"".TABLE_HEADING_EDITS_TITLE."\"><img src=\"".EDITS_ICON."\" alt=\"E\"/></th>\n".
  284.                 "    <th class=\"number ".(($c_color == 1)? ' c3' : '')."\" title=\"".TABLE_HEADING_COMMENTS_TITLE."\"><img src=\"".COMMENTS_ICON."\" alt=\"C\"/></th>\n".
  285.                 "    <th class=\"center\">Actions</th>\n".
  286.                     "  </tr>\n</thead>\n";
  287.    
  288.             // print user table
  289.             foreach($userdata as $user) {
  290.    
  291.                 // get counts   
  292.                 $where_owned    = "`owner` = '".$user['name']."' AND latest = 'Y'";
  293.                 $where_changes    = "`user` = '".$user['name']."'";
  294.                 $where_comments    = "`user` = '".$user['name']."'";
  295.                 $numowned = $this->getCount('pages', $where_owned);
  296.                 $numchanges = $this->getCount('pages', $where_changes);
  297.                 $numcomments = $this->getCount('comments', $where_comments);
  298.        
  299.                 // build statistics links if needed
  300.                 $ownedlink = ($numowned > 0)? '<a title="'.sprintf(TABLE_CELL_OWNED_TITLE,$user['name'],$numowned).'" href="'.$this->Href('','','user='.$user['name'].'&action=owned').'">'.$numowned.'</a>' : '0';
  301.                 $changeslink = ($numchanges > 0)? '<a title="'.sprintf(TABLE_CELL_EDITS_TITLE,$user['name'],$numchanges).'" href="'.$this->Href('','','user='.$user['name'].'&action=changes').'">'.$numchanges.'</a>' : '0';
  302.                 $commentslink = ($numcomments > 0)? '<a title="'.sprintf(TABLE_CELL_COMMENTS_TITLE,$user['name'],$numcomments).'" href="'.$this->Href('','','user='.$user['name'].'&action=comments').'">'.$numcomments.'</a>' : '0';
  303.                 // build handler links
  304.                 $deleteuser = '<a title="'.sprintf(ACTION_DELETE_LINK_TITLE, $user['name']).'" href="'.$this->Href('','','user='.$user['name'].'&action=delete').'">'.ACTION_DELETE_LINK.'</a>';
  305.                 $feedbackuser = '<a title="'.sprintf(ACTION_FEEDBACK_LINK_TITLE, $user['name']).'" href="'.$this->Href('','','user='.$user['name'].'&action=feedback').'">'.ACTION_FEEDBACK_LINK.'</a>';
  306.    
  307.                 // build table body
  308.                 $htmlout .= "<tbody>\n";
  309.                 if ($r_color == 1) {
  310.                     $htmlout .= "<tr ".(($r%2)? '' : 'class="alt"').">\n"; #enable alternate row color
  311.                 } else {
  312.                     $htmlout .= "<tr>\n"; #disable alternate row color
  313.                 }
  314.                 $htmlout .= "    <td><input type=\"checkbox\" id=\"".$user['id']."\"".$checked." title=\"".sprintf(SELECT_RECORD_TITLE,$user['name'])."\"/></td>\n".   
  315.                 "    <td>".(($this->ExistsPage($user['name']))? $this->Link($user['name']) : $user['name'])."</td>\n". #check if userpage exists
  316.                 "    <td>".$user['email']."</td>\n".
  317.                 "    <td class=\"time\">".$user['signuptime']."</td>\n".
  318.                 /* "    <td>".$user['ipaddress']."</td>\n". # installed as beta feature at wikka.jsnx.com */
  319.                 "    <td class=\"number".(($c_color == 1)? ' c1' : '')."\">".$ownedlink."</td>\n"#set column color
  320.                 "    <td class=\"number".(($c_color == 1)? ' c2' : '')."\">".$changeslink."</td>\n". #set column color
  321.                 "    <td class=\"number".(($c_color == 1)? ' c3' : '')."\">".$commentslink."</td>\n".   #set column color
  322.                 "    <td class=\"center\">".$deleteuser." :: ".$feedbackuser."</td>\n";
  323.                 $htmlout .= "  </tr>\n</tbody>\n";
  324.    
  325.                 //increase row counter    ----- alternate row colors
  326.                 if ($r_color == 1) $r++;
  327.                 }
  328.             $htmlout .= "</table>\n";
  329.             // print the table
  330.             echo $this->FormOpen('','','get');
  331.             echo $htmlout;
  332.            
  333.             // multiple-user operations (forthcoming)        JW 2005-07-19 accesskey removed (causes more problems than it solves)
  334.             echo '<fieldset><legend>'.FORM_MASSACTION_LEGEND.'</legend>';
  335.             echo '[<a href="'.$this->Href('','','l='.$l.'&sort='.$sort.'&d='.$d.'&s='.$s.'&q='.$q.'&selectall=1').'" title="'.CHECK_ALL_TITLE.'">'.CHECK_ALL.'</a> | <a href="'.$this->Href('','','l='.$l.'&sort='.$sort.'&d='.$d.'&s='.$s.'&q='.$q.'&selectall=0').'" title="'.UNCHECK_ALL_TITLE.'">'.UNCHECK_ALL.'</a>]<br />';
  336.             echo '<label for="action" >'.FORM_MASSACTION_LABEL.'</label> <select title="'.FORM_MASSACTION_SELECT_TITLE.'" id="action" name="action">';
  337.             echo '<option value="" selected="selected">---</option>';
  338.             echo '<option value="massdelete">'.FORM_MASSACTION_OPT_DELETE.'</option>';
  339.             echo '<option value="massfeedback">'.FORM_MASSACTION_OPT_FEEDBACK.'</option>';
  340.             echo '</select> <input type="submit" value="'.FORM_MASSACTION_SUBMIT.'" />';
  341.             echo '</fieldset>';
  342.             echo $this->FormClose();
  343.         }
  344.         else
  345.         {
  346.             // no records matching the search string: print error message
  347.             echo '<p><span class="error">'.sprintf(ERROR_NO_MATCHES, $q).'</span></p>';
  348.         }
  349.     }
  350. } else {
  351.     // user is not admin
  352.     echo $this->Action('lastusers');
  353. }
  354. ?>