fix adminsearch
[plewww.git] / planetlab / common / adminsearch.php
index 45a1c61..354b179 100644 (file)
@@ -1,6 +1,8 @@
 <?php
 
-  // Require login
+  // $Id$
+
+// Require login
 require_once 'plc_login.php';
 
 // Get session and API handles
@@ -16,544 +18,347 @@ include 'plc_header.php';
 // Common functions
 require_once 'plc_functions.php';
 require_once 'plc_objects.php';
-require_once 'plc_sorts.php';
+require_once 'plc_peers.php';
+require_once 'table.php';
+require_once 'form.php';
+require_once 'toggle.php';
+
+ini_set("memory_limit","256M");
+
+if ( ! plc_is_admin()) {
+  plc_warning ("DB Search is available to admins only");
+  return;
+ }
 
-function is_possible_domainname($token) {
-  if ( strpos ( $token, "@" ) === False && substr_count($token, ".") >= 2 ) {
-    return true;
+$pattern="";
+if (isset($_GET['pattern'])) { $pattern=$_GET['pattern']; }
+if (isset($_POST['pattern'])) { $pattern=$_POST['pattern']; }
+
+$tokens=explode(" ",$pattern);
+function token_filter ($t) { $t = trim($t); if (empty($t)) return false; return true; }
+$tokens=array_filter($tokens, "token_filter");
+
+
+////////////////////
+// from a single search form, extract all tokens entered 
+// and then show all entities that match one of that tokens among
+// persons - sites - slices - nodes
+////////////////////
+function display_form ($pattern) {
+  if ($pattern) {
+    $title="Searching for $pattern";
+    $visible=false;
   } else {
-    return false;
+    $title="Search for what";
+    $visible=true;
   }
+  $toggle=new PlekitToggle("admin-search",$title,array('visible'=>$visible));
+  $toggle->start();
+  print <<< EOF
+<p id='admin-search-message'>
+This form searches for <span class="bold">any entry</span> in the database 
+(among <span class="bold">persons</span>, <span class="bold">slices</span>, 
+<span class="bold">sites</span> and <span class="bold">nodes</span>) 
+matching a name fragment, or token. <br/>
+You can specify a space-separated list of tokens, all entries matching 
+<span class="bold">any token</span> would then get listed.
+</p>
+EOF;
+  print "<div id='admin-search-form'>";
+  $form=new PlekitForm ('/db/common/adminsearch.php',array());
+  $form->start();
+  print $form->label_html('pattern',"Enter space-separated tokens");
+  print $form->text_html('pattern',$pattern);
+  print $form->submit_html('submit','Submit');
+  $form->end();
+  print "</div>\n";
+  $toggle->end();
 }
 
-function get_and_print_site($site_array) {
+// just look for *token*
+function token_pattern ($token) {
+  return "*" . $token . "*";
+}
+
+// $type is e.g. 'Persons' -- $field is e.g. 'email'
+function generic_search ($type,$field,$tokens) {
   global $api;
-  $sites = $api->GetSites( $site_array, array( "name", "site_id", 
-                                              "url", "enabled", "node_ids", "person_ids", "date_created", 
-                                              "slice_ids", "max_slivers", "max_slices", "login_base" ) );
-  if ( count($sites) > 0 ) {
-    foreach( $sites as $site) {
-      $name = $site['name'];
-      $site_id = $site['site_id'];
-      $url =  $site['url'];
-      $enabled = $site['enabled'];
-      $node_ids =  $site['node_ids'];
-      $slice_ids_count =  count($site['slice_ids']);
-      $person_ids =  $site['person_ids'];
-      $date_created = date("M j G:i Y", $site['date_created']);
-      $max_slivers = $site['max_slivers'];
-      $max_slices =  $site['max_slices'];
-      $login_base =  $site['login_base'];
-
-      echo "<tr>\n";
-
-      echo "<td WIDTH='35%'><a href='/db/sites/index.php?id=$site_id'>$name</a>\n";
-      echo "(<a href='$url'>home</a>)</td>\n";
-      echo "<td NOWRAP>[ $slice_ids_count of $max_slices ]</td>\n";
-
-      echo "<td NOWRAP>";
-      if (!$enabled) echo "<font color=red>Not Enabled</font><br>\n";
-      //else echo "<td NOWRAP><i>$date_created</i></td>\n";
-      echo "<i>$date_created</i></td>\n";
-                       
-      echo "<td><strong>Nodes:</strong> ";
-      foreach ($site['node_ids'] as $node_id) 
-       { 
-         echo " <a href='/db/nodes/index.php?id=$node_id'>$node_id</a>, "; 
-       }
-
-      echo "</td></tr>\n";
+  $results=array();
+  $methodname='Get'.$type;
+  /*
+    This was broken after 598e1e840b55262fd40c6d1700148e4f0b508065 change in plcapi.
+    We no longer generate a list of methods but let the api (php) object pass them through.
+
+  if ( ! method_exists($api,$methodname)) {
+    plc_error("generic_search failed with methodname=$methodname");
+    return $results;
+  }
+  */
+  foreach ($tokens as $token) {
+    $filter=array($field=>token_pattern($token));
+    $new_results = $api->$methodname($filter);
+    if (is_array($new_results)) {
+        $results = array_merge ($results, $new_results);
     }
   }
+  return $results;
 }
 
-function get_and_print_hostname($host_array) {
-  global $api;
-  $nodes = $api->GetNodes( $host_array, array( "hostname", "node_id", 
-                                              "site_id", "date_created", "last_contact", "boot_state" ) );
-  if ( count ($nodes) > 0 ) {
-    foreach($nodes as $node) {
-      $hostname= $node['hostname'];
-      $node_id = $node['node_id'];
-      $boot_state= $node['boot_state'];
-      $last_contact = $node['last_contact'];
-      $date_created = date("M j G:i Y", $node['date_created']);
-      echo "<tr>";
-      echo "<td><a href='/db/nodes/index.php?id=$node_id'>$node_id</a></td> ";
-      echo "<td><a href='/db/nodes/index.php?id=$node_id'>$hostname</a></td>";
-      echo "<td>$boot_state</td>";
-      if( $last_contact != NULL ) {
-       $last_contact_str = timeDiff($last_contact);
+// $objects is e.g. a collection of persons
+// then, e.g. on slice,  $key='site_id'  & $plural=false
+// or,   e.g. on person, $key='site_ids'  & $plural=true
+function generic_gather_related ($objects, $key, $plural) {
+  if ( empty ($objects)) 
+    return array();
+  // else, look for either 'site_id' or 'site_ids' in the first object
+  $sample=$objects[0];
+  if ( array_key_exists($key,$sample)) {
+    $result=array();
+    foreach ($objects as $object) {
+      if ($plural) {
+       $result = array_merge ($result, $object[$key]);
       } else {
-       $last_contact_str = "Never";
+       $result []= $object[$key];
       }
-      echo "<td NOWRAP>$last_contact_str</td>\n";
-      echo "<td NOWRAP>$date_created</td>\n";
-      echo "</tr>\n";
     }
+    return $result;
+  } else {
+    plc_debug("gather_related failed with $key",$sample);
+    return array();
   }
-
-  return $nodes;
 }
 
-function get_and_print_user ($user_array) {
-  global $api;
-  $persons= $api->GetPersons( $user_array, array( "person_id", "first_name", 
-                                                 "last_name", "email", "roles", "enabled", "date_created", 
-                                                 "site_ids" ) );
-
-  if ( count($persons) > 0 ) {
-    foreach($persons as $person) {
-      $first= $person['first_name'];
-      $last = $person['last_name'];
-      $person_id= $person['person_id'];
-      $email= $person['email'];
-      $enabled= $person['enabled'];
-      $roles = $person['roles'];
-      $role = $person['roles'][0];
-      $date_created = date("M j G:i Y", $person['date_created']);
-      $site_ids= $person['site_ids'];
-
-      echo "<tr><td>\n";
-
-      echo "<a href='/db/persons/index.php?id=$person_id'>";
-      echo "$first $last</a></td>";
-      echo "<td>($person_id)</td> ";
-      echo "<td><a href='mailto:$email'>$email</a> </td>";
-
-      echo "<td NOWRAP>";
-      if (!$enabled) echo "<font color=red>Not Enabled</font><br>\n";
-      //else echo "<td NOWRAP><i>$date_created</i></td>\n";
-      echo "<i>$date_created</i></td>\n";
-      //if (!$enabled) echo "<td><font color=red> Not Enabled </font></td>";
-      //else echo "<td><i>$date</i></td>";
-                       
-      echo "<td> <strong>roles:</strong> ";
-      foreach ($person['roles'] as $role) { echo " $role, "; }
-
-      echo "</td></tr>\n";
+////////// 
+// create link from an id, using the various global hashes
+function plc_person_link ($person_id) {global $persons_hash; return l_person_obj($persons_hash[$person_id]);}
+function plc_slice_link ($slice_id) {global $slices_hash; return l_slice_obj($slices_hash[$slice_id]);}
+function plc_site_link ($site_id) {global $sites_hash; return l_site_obj($sites_hash[$site_id]);}
+function plc_node_link ($node_id) {global $nodes_hash; return l_node_obj($nodes_hash[$node_id]);}
+
+global $table_options;
+$table_options = array('notes_area'=>false);
+
+global $peers;
+$peers = new Peers ($api);
+
+function display_persons ($persons,$visible) {
+  if ( ! $persons) return;
+  
+  $toggle=new PlekitToggle('persons-area',"Persons",array('visible'=>$visible));
+  $toggle->start();
+
+  $headers=array('id'=>'int',
+                'P'=>'string',
+                'email'=>'string',
+                'sites'=>'string',
+                'slices'=>'string',
+                'roles'=>'string');
+  global $table_options;
+  global $peers;
+  $table=new PlekitTable('persons',$headers,1,$table_options);
+  $table->start();
+  foreach ($persons as $person) {
+    $table->row_start();       
+    $table->cell($person['person_id']);
+    $peers->cell($table,$person['peer_id']);
+    $table->cell(l_person_obj($person));
+    $table->cell(plc_vertical_table(array_map("plc_site_link",$person['site_ids'])));
+    $table->cell(plc_vertical_table(array_map("plc_slice_link",$person['slice_ids'])));
+    $table->cell(plc_vertical_table($person['roles']));
+    $table->row_end();
+  }
+  $table->end();
+  $toggle->end();
+}
 
-    }
-               
+function display_slices ($slices,$visible) {
+  if ( ! $slices) return;
+  
+  $toggle=new PlekitToggle('slices-area',"Slices",array('visible'=>$visible));
+  $toggle->start();
+
+  $headers=array('id'=>'int',
+                'P'=>'string',
+                'name'=>'string',
+                'site'=>'string',
+                'persons'=>'string',
+                'N'=>'string');
+  global $table_options;
+  global $peers;
+  $table=new PlekitTable('slices',$headers,1,$table_options);
+  $table->start();
+  foreach ($slices as $slice) {
+    $table->row_start();       
+    $table->cell($slice['slice_id']);
+    $peers->cell($table,$slice['peer_id']);
+    $table->cell(l_slice_obj($slice));
+    global $sites_hash;
+    $site=$sites_hash[$slice['site_id']];
+    $table->cell(l_site_obj($site));
+    $table->cell(plc_vertical_table(array_map("plc_person_link",$slice['person_ids'])));
+    // this makes really long tables, use the slice link to see details
+    //$table->cell(plc_vertical_table(array_map("plc_node_link",$slice['node_ids'])));
+    $table->cell(count($slice['node_ids']));
+    $table->row_end();
   }
-  return $persons;
+  $table->end();
+  $toggle->end();
 }
 
-// find person roles
-$_person= $plc->person;
-$_roles= $_person['role_ids'];
-
-
-///////////////////////////////////////////////////////////////////////
-// Initially, we need a search form, and blank information after that.  
-// Based on the fields of the search form, we should be able to populate a lot
-// of information, related to user, site, keys, 
-// 
-//     from a simple domain name search, it would display the site, 
-//             nodes -> pcu
-//             users grouped by role, 
-//  from a user search, it would find,
-//             user info link, site, roles, etc.
-//
-//  userquery will be one of these types:
-//             part of a user name, email, user_id
-//             part of a site name, site alias, site_id
-//             part of a node name, domain, node_id
-//
-//  we should be able to return results based on these guesses.  If they're
-//  good, we'll get hits, if nothing comes back, we won't display anything.
-
-
-echo "<div>\n
-        <form method=post action='/db/common/adminsearch.php'>\n";
-
-if( $_POST['userquery'] or $_GET['userquery']) {
-  if ( $_POST['userquery'] ) {
-    $query = $_POST['userquery'];
-  } else {
-    $query = $_GET['userquery'];
+function display_sites ($sites,$visible) {
+  if ( ! $sites) return;
+  
+  $toggle=new PlekitToggle('sites-area',"Sites",array('visible'=>$visible));
+  $toggle->start();
+
+  $headers=array('id'=>'int',
+                'P'=>'string',
+                'name'=>'string',
+                'url'=>'string',
+                'persons'=>'string',
+                'slices'=>'string',
+                'nodes'=>'string');
+  global $table_options;
+  global $peers;
+  $table=new PlekitTable('sites',$headers,1,$table_options);
+  $table->start();
+  foreach ($sites as $site) {
+    $table->row_start();       
+    $table->cell($site['site_id']);
+    $peers->cell($table,$site['peer_id']);
+    $table->cell(l_site_obj($site));
+    $table->cell(href($site['url'],$site['url']));
+    $table->cell(plc_vertical_table(array_map("plc_person_link",$site['person_ids'])));
+    $table->cell(plc_vertical_table(array_map("plc_slice_link",$site['slice_ids'])));
+    $table->cell(plc_vertical_table(array_map("plc_node_link",$site['node_ids'])));
+    $table->row_end();
   }
- }
+  $table->end();
+  $toggle->end();
+}
 
-echo "<p><label for='testinput'>User or Site Name/loginbase/Email/User or Site ID:</label> (Separate with commas for multiple queries.)\n
-        <input type='text' id='testinput' name='userquery' size=50 value='$query' />\n
-        <input type=submit value='Search' />\n
-               </form>\n
-        </div>\n
-        <br />\n";
-
-// if userquery then search based on string
-if( $_POST['userquery'] or $_GET['userquery']) {
-  if ( $_POST['userquery'] ) {
-    $query = $_POST['userquery'];
-  } else {
-    $query = $_GET['userquery'];
+function display_nodes ($nodes,$visible) {
+  if ( ! $nodes) return;
+  
+  $toggle=new PlekitToggle('nodes-area',"Nodes",array('visible'=>$visible));
+  $toggle->start();
+
+  $headers=array('id'=>'int',
+                'P'=>'string',
+                'hostname'=>'string',
+                'site'=>'string',
+                'slices'=>'string');
+  global $table_options;
+  global $peers;
+  $table=new PlekitTable('nodes',$headers,1,$table_options);
+  $table->start();
+  foreach ($nodes as $node) {
+    $table->row_start();       
+    $table->cell($node['node_id']);
+    $peers->cell($table,$node['peer_id']);
+    $table->cell(l_node_obj($node));
+    global $sites_hash;
+    $site=$sites_hash[$node['site_id']];
+    $table->cell(l_site_obj($site));
+    // same as above, too many entries, just list how many there are
+    //$table->cell(plc_vertical_table(array_map("plc_slice_link",$node['slice_ids'])));
+    $table->cell(count($node['slice_ids']));
+    $table->row_end();
   }
-  echo "<table cellspacing=2 cellpadding=1 width=100%><thead><caption><b>Search Results:</b><caption></thead><tbody>\n";
-
-  $f_commas = explode(",", $query);
-  // PHASE 1: query contains email, user_id, or part of a user name
-  foreach($f_commas as $tok) {
-    $tok = trim($tok); // strip white space.
-
-    $e = false;
-    //find user by email
-    if ( is_valid_email_addr($tok) ){
-      $u = get_and_print_user(array("email"=>$tok));
-      $e = true;
-      if( count( $u ) == 0 ) $none = 1;
-    }
-    $n = false;
-    // find user by user_id
-    if ( is_numeric($tok) ){
-      $u = get_and_print_user(array("person_id"=>intval($tok)));
-      $n = true;
-      if( count( $u ) == 0 ) $none = 1;
-    }
-    // neither of the above, 
-    if (!$n && !$e) {
-      // split on spaces, and search each part.
-      // TODO: search upper and lower-case
-      $f_spaces = explode(" ", $tok);
-      foreach($f_spaces as $stok) {
-       // assume $tok is part of a name
-       // get_user
-       $a = get_and_print_user(array("first_name"=>$stok));
-       $b = get_and_print_user(array("last_name"=>$stok));
-       // c = intersect_users(a, b)
-       // if c 
-       //     print_user(c), 
-       // else 
-       //     print_user(a,b)
-      }
-      if( count( $a ) == 0 && count( $b ) == 0 ) $none = 1;
-    }
+  $table->end();
+  $toggle->end();
+}
 
-    // PHASE 2: query contains login_base, site_id, or part of a site name
 
-    // find site by login_base
-    $lb = false;
-    $s = get_and_print_site(array("login_base"=>strtolower($tok)));
-    if( count( $s ) == 0 ) $none = 1;
-    else $lb = true;
+////////////////////////////////////////////////////////////
+display_form($pattern);
 
-    $n = false;
-    // find site by site_id
-    if ( is_numeric($tok) ){
-      $s = get_and_print_site(array("site_id"=>intval($tok)));
-      $n = true;
-      if( count( $s ) == 0 ) $none = 1;
-    }
+if ($pattern) {
 
-    if( !$lb && !$n ) {
-      $f_spaces = explode(" ", $tok);
-      foreach($f_spaces as $stok) {
-       $a = get_and_print_site(array("name"=>$stok));
-      }
-      if( count($a) == 0 ) $none = 1;
-    }
+  ////////// search database
+  // search persons on email
+  $persons = array();
+  $persons = array_merge ($persons, generic_search ('Persons','email',$tokens));
 
-    // PHASE 3:        query contains part of a node name, domain, node_id
+  // search slices on name
+  $slices=array();
+  $slices = array_merge ($slices, generic_search ('Slices','name',$tokens));
 
-    //if( $none == 1 ) echo "<tr><td>No Results. </td></tr>\n";
-    if ( is_possible_domainname($tok) ) {
-      $n = get_and_print_hostname(array("hostname"=>$tok));
-    }
+  // search sites on name and login_base
+  $sites=array();
+  $sites = array_merge ($sites, generic_search('Sites','name',$tokens));
+  $sites = array_merge ($sites, generic_search('Sites','login_base',$tokens));
+
+  // nodes on hostname
+  $nodes=array();
+  $nodes = array_merge ($nodes, generic_search('Nodes','hostname',$tokens));
+
+  print "Search results for <span class='tokens'> $pattern </span>\n";
+
+  // what kind of result have we gotten:
+  // if none : display message and exit
+  // if only one kind of objects : start toggle with visible=true
+  // otherwise start all toggles with visible=false
+  $areas=0;
+  if ($persons) $areas += 1;
+  if ($slices) $areas += 1;
+  if ($sites) $areas += 1;
+  if ($nodes) $areas += 1;
+
+  if ( $areas == 0) {
+    plc_warning ("No result found");
+    return;
+  } else if ($areas == 1) {
+    $visible=true;
+  } else {
+    $visible=false;
   }
-  echo "</tbody></table>\n";
- }
+  
 
-if (false) {
-  // if a site_id is given, display the site nodes only
-  if( $_GET['site_id'] ) {
-    $site_id= $_GET['site_id'];
+  ////////// collect all related objects 
+  $rel_person_ids = array();
+  $rel_person_ids = array_merge($rel_person_ids, generic_gather_related ($sites,'person_ids',true));
+  $rel_person_ids = array_merge($rel_person_ids, generic_gather_related ($slices,'person_ids',true));
 
-    // Get site info
-    $site_info= $api->GetSites( array( intval( $site_id ) ), array( "name", "person_ids" ) );
+  $rel_slice_ids = array();
+  $rel_slice_ids = array_merge($rel_slice_ids, generic_gather_related ($persons,'slice_ids',true));
+  $rel_slice_ids = array_merge($rel_slice_ids, generic_gather_related ($sites,'slice_ids',true));
+  $rel_slice_ids = array_merge($rel_slice_ids, generic_gather_related ($nodes,'slice_ids',true));
 
-    // Get site nodes
-    $persons= $api->GetPersons( $site_info[0]['person_ids'], array( "person_id", "first_name", "last_name", "email", "roles", "peer_id", "enabled" ) );
-         
-    drupal_set_title("People with " . $site_info[0]['name']);
+  $rel_site_ids = array();
+  $rel_site_ids = array_merge ( $rel_site_ids, generic_gather_related ($persons,'site_ids',true));
+  $rel_site_ids = array_merge ( $rel_site_ids, generic_gather_related ($slices,'site_id',false));
+  $rel_site_ids = array_merge ( $rel_site_ids, generic_gather_related ($nodes,'site_id',false));
 
-    sort_persons( $persons );
+  $rel_node_ids = array();
+  $rel_node_ids = array_merge($rel_node_ids, generic_gather_related ($sites,'node_ids',true));
+  $rel_node_ids = array_merge($rel_node_ids, generic_gather_related ($slices,'node_ids',true));
 
-    echo paginate_trash ( $persons, "person_id", "Persons", 25, "email" );
 
-  }
-  // if no person id, display list of persons to choose
-  elseif( !$_GET['id'] ) {
-
-    // GetPersons API call
-    $persons= $api->GetPersons( NULL, array( "person_id", "first_name", "last_name", "email", "roles" , "peer_id", "enabled" ) );
-
-    sort_persons( $persons );
-
-    echo "<div>\n
-                       <form method=post action='/db/persons/allinfo.php'>\n";
-    if( $pers_email ) echo "<font color=red>'$pers_email' is not a valid person email.</font>\n";
-    echo "<p><label for='testinput'>Query: </label>\n
-                       <input type='text' id='testinput' name='userquery' size=50 value='' />\n
-                       <input type=submit value='Search' />\n
-                       </div>\n
-                       <br />\n";
-  } else {
-    // get the person_id from the URL
-    $person_id= intval( $_GET['id'] );
-
-    // GetPersons API call for this person
-    $person_info= $api->GetPersons( array( $person_id ) );
-         
-    if( empty( $person_info ) ) {
-      echo "No such person.";
-                       
-    } else {
-      // vars from api
-      $first_name= $person_info[0]['first_name'];
-      $last_name= $person_info[0]['last_name'];
-      $title= $person_info[0]['title'];
-      $url= $person_info[0]['url'];
-      $phone= $person_info[0]['phone'];
-      $email= $person_info[0]['email'];
-      $enabled= $person_info[0]['enabled'];
-      $peer_id=$person_info[0]['peer_id'];
-                       
-      // arrays from api
-      $role_ids= $person_info[0]['role_ids'];
-      $roles= $person_info[0]['roles'];
-      $site_ids= $person_info[0]['site_ids'];
-      $slice_ids= $person_info[0]['slice_ids'];
-      $key_ids= $person_info[0]['key_ids'];
-                       
-      // gets more data from API calls
-      $site_info= $api->GetSites( $site_ids, array( "site_id", "name" ) );
-      $slice_info= $api->GetSlices( $slice_ids, array( "slice_id", "name" ) );
-      $key_info= $api->GetKeys( $key_ids );
-                       
-      drupal_set_title("$first_name $last_name Account Information");
-
-      // start form
-      if ( ! $peer_id ) {
-       echo "<form action='/db/persons/person_action.php' method='post'>\n";
-      } else {
-       echo "<div class='plc-foreign'>";
-      }
-      echo "<input type=hidden name='person_id' value='$person_id'>\n";
-               
-      if ( ! $peer_id ) {
-# if ( Admin, PI, or user ) allow deletion
-       if( in_array( 10, $_roles ) || 
-           ( in_array( 20, $_roles ) && in_array( $site_ids[0], $_person['site_ids'] ) )  ||
-           $_person['person_id'] == $person_id) {
-
-         // list to take person action
-         echo "<p><select name='actions' onChange=\"submit();\">\n";
-                               
-         $actions= array( ''=>'Choose Action', 'delete'=>"Delete $first_name" );
-         $select_end = "</select>\n";
-
-# if ( Admin or PI ) check whether to allow # 'enabling/disabling'.
-         if( in_array( 10, $_roles ) || 
-             ( in_array( 20, $_roles ) && in_array( $site_ids[0], $_person['site_ids'] ) ) ) { 
-
-           if( $enabled == true ) {
-             $actions['disable']= "Disable $first_name";
-           } else {
-             $actions['enable']= "Enable $first_name";
-             $select_end = $select_end . " &nbsp; <font color=red size=-1>".
-               "<- This user is not enabled. Choose here to enable or delete.</font>";
-           }
-           if ( in_array( 10, $_roles )) {
-             $actions['su'] = "Become $first_name";
-           }
-         } 
-                               
-# for all cases, list each 'select' key
-         foreach( $actions as $key => $val ) {
-           echo "<option value='$key'";
-               
-           if( $key == $_POST['actions'] )
-             echo " selected";
-               
-           echo ">$val\n";
-         }
-               
-         echo $select_end;
-       }
-      } 
-               
-      // basic person info
-      echo "<p><table border=0>\n";
-      echo "<tr><td>First Name: </td><td> $first_name</td></tr>\n";
-      echo "<tr><td>Last Name: </td><td> $last_name</td></tr>\n";
-      echo "<tr><td>Title: </td><td> $title</td></tr>\n";
-      echo "<tr><td>Email: </td><td><a href='mailto:$email'>$email</a></td></tr>\n";
-      echo "<tr><td>Password: </td><td>***********</td></tr>\n";
-      echo "<tr><td>Phone: </td><td>$phone</td></tr>\n";
-      echo "<tr><td>URL: </td><td>$url</td></tr>\n";
-      echo "</table>\n";
-               
-      if( in_array( 10, $_roles ) || $_person['person_id'] == $person_id )
-       echo "<br /><a href='/db/persons/update.php?id=$person_id'>Update account information</a>\n";
-
-      echo "<hr />\n";
-                       
-      // keys
-      $can_manage_keys = ( ( ! $peer_id ) && (in_array( "10", $_roles ) || $person_id == $_person['person_id']));
-      echo "<h3>Keys</h3>\n";
-      if( !empty( $key_ids ) ) {
-       echo "<p><table border=0 width=450>\n";
-       echo "<thead><tr><th>Type</th><th>Key</th></tr></thead><tbody>\n";
-                               
-       foreach( $key_info as $key ) {
-         $key_type= $key['key_type'];
-         $key_id= $key['key_id'];
-         $key_html= wordwrap( $key['key'], 70, "<br />\n", 1 );
-         echo "<tr><td>$key_type</td><td>$key_html</td></tr>\n";
-       }
-               
-       echo "</tbody></table>\n";
-
-               
-      } else {
-       echo "<span class='plc-warning'> This user has no known key</span>";
-      }
-                       
-      if( $can_manage_keys )
-       echo "<p><a href='/db/persons/keys.php?id=$person_id'>Manage Keys</a>\n";
-      echo "<hr />";
-               
-               
-      // sites
-      echo "<h3>Sites</h3>\n";
-      if( !empty( $site_info ) ) {
-       echo "<table cellpadding=3><tbody>\n";
-               
-       foreach( $site_info as $site ) {
-         $site_name= $site['name'];
-         $site_id= $site['site_id'];
-               
-         echo "<tr><td><a href='/db/sites/index.php?id=$site_id'>$site_name</a> </td><td> (<input type=checkbox name='rem_site[]' value='$site_id'> remove)</td></tr>\n";
-       }
-       echo "</tbody></table>\n";
-       echo "<input type=submit value='Remove Sites'>\n";
-               
-      } else {
-       echo "<span class='plc-warning'> This user is not affiliated with a site !!</span>";
-      }
-               
-      // diplay site select list to add another site for user
-      if( ! $peer_id && in_array( 10, $_roles ) ) {
-       // get site info
-       $full_site_info= $api->GetSites( NULL, array( "site_id", "name" ) );
-                               
-       if( $site_info )
-         $person_site= arr_diff( $full_site_info, $site_info );
-       else
-         $person_site= $full_site_info;
-                               
-       sort_sites( $person_site );
-               
-       echo "<p>Select a site to add this user to: ";
-       echo "<select name='site_add' onChange='submit()'>\n<option value=''>Choose a site to add:</option>\n";
-               
-       foreach( $person_site as $site ) {
-         echo "<option value=". $site['site_id'] .">". $site['name'] ."</option>\n";
-               
-       }
-               
-       echo "</select>";
-               
-      }
-      echo "<hr />\n";
-               
-      // roles
-      echo "<h3>Roles</h3>\n";
-      echo "<p><table>\n";
-      echo "<thead><tr><th>Role</th>";
-      if( in_array( "10", $_roles ) )
-       echo "<th>Remove</th>";
-      echo "</tr></thead><tbody>\n";
-               
-      // construct role array
-      for( $n=0; $n<count($roles); $n++ ) {
-       $proles[]= array( 'role_id'=>$role_ids[$n], 'name'=>$roles[$n] );
-      }
-                       
-
-      if ( !empty ($roles) ) {
-       foreach( $proles as $role ) {
-         $role_name= $role['name'];
-         $role_id= $role['role_id'];
-                               
-         echo "<tr><td>$role_name";
-                               
-
-         if( in_array( 10, $_roles ) ) {
-           echo "</td><td><input type=checkbox name='rem_role[]' value='$role_id'>";
-
-         }
-                                       
-         echo "</td></tr>\n";
-       }
-      } else {
-       echo "<span class='plc-warning'> This user has no known role !!</span>";
-      }
-                       
-      echo "</tbody></table>\n";
-
-      if ( in_array( 10, $_roles ) )
-       echo "<input type=submit value='Update Roles'><br />\n";
-
-
-               
-      // if admin show roles to add
-      if( in_array( 10, $_roles ) ) {
-       $all_roles= $api->GetRoles();
-       $addable_roles= arr_diff( $all_roles, $proles );
-               
-       if( !empty( $addable_roles ) ) {
-         echo "<p><p>Add role: <select name='add_role' onChange='submit()'>\n<option value=''>Choose a Role to add:</option>\n";
-                       
-         foreach( $addable_roles as $arole ) {
-           echo "<option value=". $arole['role_id'] .">". $arole['name'] ."</option>\n";
-                       
-         }
-                       
-         echo "</select>\n";
-               
-       }
-      }
-               
-      echo "<hr />\n";
-                       
-      // slices
-      echo "<h3>Slices</h3>\n";
-      if( !empty( $slice_info ) ) {
-                         
-       foreach( $slice_info as $slice ) {
-         $slice_name= $slice['name'];
-         $slice_id= $slice['slice_id'];
-               
-         echo "<a href='/db/slices/index.php?id=$slice_id'>$slice_name</a><br />\n";
-       }
-               
-      } else {
-       echo "No slices found for that user";
-      }
+  ////////// fetch related and store in a global hash
+  $rel_persons = $api->GetPersons ($rel_person_ids);
+  global $persons_hash; $persons_hash=array();
+  foreach ($rel_persons as $person) $persons_hash[$person['person_id']]=$person;
 
-      if ( ! $peer_id ) {
-       echo "</form>\n";
-      } else {
-       echo "</div>\n";
-      }
+  $rel_slices = $api->GetSlices ($rel_slice_ids);
+  global $slices_hash; $slices_hash=array();
+  foreach ($rel_slices as $slice) $slices_hash[$slice['slice_id']]=$slice;
 
-    }
-    echo "<br /><hr /><p><a href='/db/persons/index.php'>Back to person list</a></div>";
+  $rel_sites = $api->GetSites ($rel_site_ids);
+  global $sites_hash; $sites_hash=array();
+  foreach ($rel_sites as $site) $sites_hash[$site['site_id']]=$site;
 
-  }
- }
+  $rel_nodes = $api->GetNodes ($rel_node_ids);
+  global $nodes_hash; $nodes_hash=array();
+  foreach ($rel_nodes as $node) $nodes_hash[$node['node_id']]=$node;
+
+  ////////// show results
+  display_persons ($persons,$visible);
+  display_slices ($slices,$visible);
+  display_sites($sites,$visible);
+  display_nodes($nodes,$visible);
 
+ }
 
 // Print footer
 include 'plc_footer.php';