adding new files for testing the column configuration panel (modified only my_slice...
authorPanos <antoniad@akastos.local>
Fri, 24 Sep 2010 13:03:19 +0000 (15:03 +0200)
committerPanos <antoniad@akastos.local>
Fri, 24 Sep 2010 13:03:19 +0000 (15:03 +0200)
planetlab/css/my_slice.css
planetlab/includes/plc_visibletags2.php [new file with mode: 0644]
planetlab/slices/slice2.php [new file with mode: 0644]
plekit/php/columns.php [new file with mode: 0644]
plekit/php/tophat_api.php [new file with mode: 0644]
plekit/php/updateColumn.php [new file with mode: 0644]
plekit/php/updateConf.php [new file with mode: 0644]
plekit/table/columns.js [new file with mode: 0644]

index c79af1f..d33730f 100644 (file)
@@ -40,4 +40,35 @@ table#leases_data {
 #leases_buttons { padding: 0px 10px 10px 10px; }
 #leases_clear { position: relative; left: 30%;}
 #leases_submit { position: relative; left: 60%; }
-        
+       
+
+/*panos: some additional styles for the column configuration */
+
+div#toggle-container-my-slice-nodes-configuration {
+    background: #f8f8f8;
+}
+
+div.out{background-color:white; color:black}
+div.in{background-color:#CAE8EA; color:#4f6b72}
+div.selected{background-color:gray; color:black}
+div.invisible{display:none}
+
+.myslice {
+        font: 11px Arial, Helvetica, sans-serif;
+        color: gray;
+}
+
+.myslice .title {
+       font: 11px;
+       font-weight: bold;
+}
+
+.myslice .desc {
+       font: 11px;
+       font-style: italic;
+}
+
+.myslice .subtitle{
+        color: #bb9c61;
+}
+
diff --git a/planetlab/includes/plc_visibletags2.php b/planetlab/includes/plc_visibletags2.php
new file mode 100644 (file)
index 0000000..10341c3
--- /dev/null
@@ -0,0 +1,88 @@
+<?php
+
+  // $Id: plc_functions.php 15734 2009-11-13 10:52:31Z thierry $
+
+  // utility function for displaying extra columns based on tags and categories
+  // expected type is e.g. 'node' 
+
+class VisibleTags {
+  var $api;
+  var $type;
+  
+  function VisibleTags ($api,$type) {
+    $this->api=$api;
+    $this->type=$type;
+    $this->columns=NULL;
+  }
+  
+  // returns an ordered set of columns - compute only once
+  function columns () {
+    # if cached
+    if ($this->columns != NULL) 
+      return $this->columns;
+
+    // scan tag types to find relevant additional columns
+    $tag_types = $this->api->GetTagTypes(array('category'=>"$type*/ui*"));
+    
+    $columns = array();
+    foreach ($tag_types as $tag_type) {
+      $tagname=$tag_type['tagname'];
+      $column=array();
+      $column['tagname']=$tagname;
+      // defaults
+      $column['header']=$tagname;
+      $column['rank']=$tagname;
+      $column['type']='string';
+      $column['description']=$tag_type['description'];
+      // split category and parse any setting
+      $category_tokens=explode('/',$tag_type['category']);
+      foreach ($category_tokens as $token) {
+       $assign=explode('=',$token);
+       if (count($assign)==2) 
+         $column[$assign[0]]=$assign[1];
+      }
+      $columns []= $column;
+    }
+    
+    // sort upon 'rank'
+    usort ($columns, create_function('$col1,$col2','return strcmp($col1["header"],$col2["header"]);'));
+
+    # cache for next time
+    $this->columns=$columns;
+//    plc_debug('columns',$columns);
+    return $columns;
+  }
+
+  // extract tagname
+  function column_names () {
+    return array_map(create_function('$tt','return $tt["tagname"];'),$this->columns());
+  }
+  
+  // to add with array_merge to the headers part of the Plekit Table
+  function headers () {
+    $headers=array();
+    $columns=$this->columns();
+    foreach ($columns as $column)
+           //panos: needed a few more fields in the header array
+       $headers[$column['header']]=array('header'=>$column['header'],'type'=>$column['type'],'tagname'=>$column['tagname'],'title'=>$column['description']);
+    /*
+      if ($column['header'] == $column['tagname']) 
+       $headers[$column['header']]=$column['type'];
+      else
+       $headers[$column['header']]=array('type'=>$column['type'],'title'=>$column['description']);
+     */
+    return $headers;
+  }
+
+  // to add with array_merge to the notes part of the Plekit Table
+  function notes () {
+    $notes=array();
+    $columns=$this->columns();
+    foreach ($columns as $column)
+      if ($column['header'] != $column['tagname']) 
+       $notes []= strtoupper($column['header']) . ' = ' . $column['description'];
+    return $notes;
+  }
+
+}
+?>
diff --git a/planetlab/slices/slice2.php b/planetlab/slices/slice2.php
new file mode 100644 (file)
index 0000000..385c8b5
--- /dev/null
@@ -0,0 +1,962 @@
+<?php
+
+// $Id$
+
+// Require login
+require_once 'plc_login.php';
+
+// Get session and API handles
+require_once 'plc_session.php';
+global $plc, $api;
+
+// Print header
+require_once 'plc_drupal.php';
+include 'plc_header.php';
+
+// Common functions
+require_once 'plc_functions.php';
+require_once 'plc_peers.php';
+require_once 'plc_objects.php';
+require_once 'plc_visibletags2.php';
+require_once 'linetabs.php';
+require_once 'table2.php';
+require_once 'details.php';
+require_once 'toggle.php';
+require_once 'form.php';
+require_once 'columns.php';
+
+// keep css separate for now
+drupal_set_html_head('
+<link href="/planetlab/css/my_slice.css" rel="stylesheet" type="text/css" />
+');
+
+// -------------------- admins potentially need to get full list of users
+ini_set('memory_limit','32M');
+
+// -------------------- 
+// recognized URL arguments
+$slice_id=intval($_GET['id']);
+if ( ! $slice_id ) { plc_error('Malformed URL - id not set'); return; }
+
+////////////////////
+// Get all columns as we focus on only one entry
+$slices= $api->GetSlices( array($slice_id));
+
+if (empty($slices)) {
+  drupal_set_message ("Slice " . $slice_id . " not found");
+  return;
+ }
+
+$slice=$slices[0];
+
+// pull all node info to vars
+$name= $slice['name'];
+$expires = date( "d/m/Y", $slice['expires'] );
+$site_id= $slice['site_id'];
+
+$person_ids=$slice['person_ids'];
+
+// get peers
+$peer_id= $slice['peer_id'];
+$peers=new Peers ($api);
+$local_peer = ! $peer_id;
+
+// gets site info
+$sites= $api->GetSites( array( $site_id ) );
+$site=$sites[0];
+$site_name= $site['name'];
+$max_slices = $site['max_slices'];
+
+//////////////////////////////////////// building blocks for the renew area
+// Constants
+global $DAY;           $DAY = 24*60*60;
+global $WEEK;          $WEEK = 7 * $DAY; 
+global $MAX_WEEKS;     $MAX_WEEKS= 8;          // weeks from today
+global $GRACE_DAYS;    $GRACE_DAYS=10;         // days for renewal promoted on top
+global $NOW;           $NOW=mktime();
+
+////////////////////////////////////////////////////////////
+// make the renew area on top and open if the expiration time is less than 10 days from now
+function renew_needed ($slice) {
+  global $DAY, $NOW, $GRACE_DAYS;
+  $current_exp=$slice['expires'];
+
+  $time_left = $current_exp - $NOW;
+  $visible = $time_left/$DAY <= $GRACE_DAYS;
+  return $visible;
+}
+
+function renew_area ($slice,$site,$visible) {
+  global $DAY, $WEEK, $MAX_WEEKS, $GRACE_DAYS, $NOW;
+  $current_exp=$slice['expires'];
+  $current_text = gmstrftime("%A %b-%d-%y %T %Z", $current_exp);
+  $max_exp= $NOW + ($MAX_WEEKS * $WEEK); // seconds since epoch
+  $max_text = gmstrftime("%A %b-%d-%y %T %Z", $max_exp);
+
+  // xxx some extra code needed to enable this area only if the slice description is OK:
+  // description and url must be non void
+  $toggle=
+    new PlekitToggle('renew',"Expires $current_text - Renew this slice",
+                    array("bubble"=>
+                          "Enter this zone if you wish to renew your slice",
+                          'visible'=>$visible));
+  $toggle->start();
+
+  // xxx message could take roles into account
+  if ($site['max_slices']<=0) {
+     $message= <<< EOF
+<p class='my-slice-renewal'>Slice creation and renewal have been temporarily disabled for your
+<site. This may have occurred because your site's nodes have been down
+or unreachable for several weeks, and multiple attempts to contact
+your site's PI(s) and Technical Contact(s) have all failed. If so,
+contact your site's PI(s) and Technical Contact(s) and ask them to
+bring up your site's nodes. Please visit your <a
+href='/db/sites/index.php?id=$site_id'>site details</a> page to find
+out more about your site's nodes, and how to contact your site's PI(s)
+and Technical Contact(s).</p>
+EOF;
+     echo $message;
+  } else {
+    // xxx this is a rough cut and paste from the former UI
+    // showing a datepicker view could be considered as well with some extra work
+    // calculate possible extension lengths
+    $selectors = array();
+    foreach ( array ( 1 => "One more week", 
+                     2 => "Two more weeks", 
+                     3 => "Three more weeks", 
+                     4 => "One more month" ) as $weeks => $text ) {
+      $candidate_exp = $current_exp + $weeks*$WEEK;
+      if ( $candidate_exp < $max_exp) {
+       $selectors []= array('display'=>"$text (" . gmstrftime("%A %b-%d-%y %T %Z", $candidate_exp) . ")",
+                            'value'=>$candidate_exp);
+       $max_renewal_weeks=$weeks;
+       $max_renewal_date= gmstrftime("%A %b-%d-%y %T %Z", $candidate_exp);
+      }
+    }
+
+    if ( empty( $selectors ) ) {
+      print <<< EOF
+<div class='my-slice-renewal'>
+Slices annot be renewed more than $MAX_WEEKS weeks from now, i.e. not beyond $max_text. 
+For this reason, the current slice cannot be renewed any further into the future, try again closer to expiration date.
+</div>
+EOF;
+     } else {
+      print <<< EOF
+<div class='my-slice-renewal'>
+<p>You must provide a short description as well as a link to a project website before renewing it.
+Do <span class='bold'>not</span> provide bogus information; if a complaint is lodged against your slice 
+and PlanetLab Operations is unable to determine what the normal behavior of your slice is, 
+your slice may be deleted to resolve the complaint.</p>
+<p><span class='bold'>NOTE:</span> 
+Slices cannot be renewed beyond another $max_renewal_weeks week(s) ($max_renewal_date).
+</p>
+</div>
+EOF;
+
+      $form = new PlekitForm (l_actions(),
+                             array('action'=>'renew-slice',
+                                   'slice_id'=>$slice['slice_id']));
+      $form->start();
+      print $form->label_html('expires','Duration');
+      print $form->select_html('expires',$selectors,array('label'=>'Pick one'));
+      print $form->submit_html('renew-button','Renew');
+      $form->end();
+    }
+  }
+  $toggle->end();
+}
+
+////////////////////////////////////////////////////////////
+
+$am_in_slice = in_array(plc_my_person_id(),$person_ids);
+
+if ($am_in_slice) {
+  drupal_set_title("My slice " . $name);
+ } else {
+  drupal_set_title("Slice " . $name);
+}
+
+$privileges = ( $local_peer && (plc_is_admin()  || plc_is_pi() || $am_in_slice));
+$tags_privileges = $privileges || plc_is_admin();
+
+$tabs=array();
+$tabs [] = tab_nodes_slice($slice_id);
+$tabs [] = tab_site($site_id);
+
+// are these the right privileges for deletion ?
+if ($privileges) {
+  $tabs ['Delete']= array('url'=>l_actions(),
+                         'method'=>'post',
+                         'values'=>array('action'=>'delete-slice','slice_id'=>$slice_id),
+                         'bubble'=>"Delete slice $name",
+                         'confirm'=>"Are you sure to delete slice $name");
+
+  $tabs["Events"]=array_merge(tablook_event(),
+                             array('url'=>l_event("Slice","slice",$slice_id),
+                                   'bubble'=>"Events for slice $name"));
+  $tabs["Comon"]=array_merge(tablook_comon(),
+                            array('url'=>l_comon("slice_id",$slice_id),
+                                  'bubble'=>"Comon page about slice $name"));
+}
+
+plekit_linetabs($tabs);
+
+////////////////////////////////////////
+$peers->block_start($peer_id);
+
+//////////////////////////////////////// renewal area 
+// (1) close to expiration : show on top and open
+
+if ($local_peer ) {
+  $renew_visible = renew_needed ($slice);
+  if ($renew_visible) renew_area ($slice,$site,true);
+ }
+
+
+//////////////////// details
+// default for opening the details section or not ?
+if ($local_peer) {
+  $default_show_details = true;
+ } else {
+  $default_show_details = ! $renew_visible;
+ }
+  
+$toggle = 
+  new PlekitToggle ('my-slice-details',"Details",
+                   array('bubble'=>
+                         'Display and modify details for that slice',
+                         'visible'=>get_arg('show_details',$default_show_details)));
+$toggle->start();
+
+$details=new PlekitDetails($privileges);
+$details->form_start(l_actions(),array('action'=>'update-slice',
+                                      'slice_id'=>$slice_id,
+                                      'name'=>$name));
+
+$details->start();
+if (! $local_peer) {
+  $details->th_td("Peer",$peers->peer_link($peer_id));
+  $details->space();
+ }
+
+
+$details->th_td('Name',$slice['name']);
+$details->th_td('Description',$slice['description'],'description',
+               array('input_type'=>'textarea',
+                     'width'=>50,'height'=>5));
+$details->th_td('URL',$slice['url'],'url',array('width'=>50));
+$details->tr_submit("submit","Update Slice");
+$details->th_td('Expires',$expires);
+$details->th_td('Instantiation',$slice['instantiation']);
+$details->th_td('Site',l_site_obj($site));
+// xxx show the PIs here
+//$details->th_td('PIs',...);
+$details->end();
+
+$details->form_end();
+$toggle->end();
+
+//////////////////// persons
+$person_columns = array('email','person_id','first_name','last_name','roles');
+// get persons in slice
+if (!empty($person_ids))
+  $persons=$api->GetPersons(array('person_id'=>$slice['person_ids']),$person_columns);
+// just propose to add everyone else
+// xxx this is maybe too much for admins as it slows stuff down 
+// as regular persons can see only a fraction of the db anyway
+$potential_persons=
+  $api->GetPersons(array('~person_id'=>$slice['person_ids'],
+                        'peer_id'=>NULL,
+                        'enabled'=>true),
+                  $person_columns);
+$count=count($persons);
+
+$toggle=
+  new PlekitToggle ('my-slice-persons',"$count users",
+                   array('bubble'=>
+                         'Manage accounts attached to this slice',
+                         'visible'=>get_arg('show_persons',false)));
+$toggle->start();
+
+////////// people currently in
+// visible:
+// hide if both current+add are included
+// so user can chose which section is of interest
+// show otherwise
+$toggle_persons = new PlekitToggle ('my-slice-persons-current',
+                                   "$count people currently in $name",
+                                   array('visible'=>get_arg('show_persons_current',!$privileges)));
+$toggle_persons->start();
+
+$headers=array();
+$headers['email']='string';
+$headers['first']='string';
+$headers['last']='string';
+$headers['R']='string';
+if ($privileges) $headers[plc_delete_icon()]="none";
+$table=new PlekitTable2('persons',$headers,'0', NULL,
+                      array('notes_area'=>false));
+$form=new PlekitForm(l_actions(),array('slice_id'=>$slice['slice_id']));
+$form->start();
+$table->start();
+if ($persons) foreach ($persons as $person) {
+  $table->row_start();
+  $table->cell(l_person_obj($person));
+  $table->cell($person['first_name']);
+  $table->cell($person['last_name']);
+  $table->cell(plc_vertical_table ($person['roles']));
+  if ($privileges) $table->cell ($form->checkbox_html('person_ids[]',$person['person_id']));
+  $table->row_end();
+}
+// actions area
+if ($privileges) {
+
+  // remove persons
+  $table->tfoot_start();
+
+  $table->row_start();
+  $table->cell($form->submit_html ("remove-persons-from-slice","Remove selected"),
+              array('hfill'=>true,'align'=>'right'));
+  $table->row_end();
+ }
+$table->end();
+$toggle_persons->end();
+
+////////// people to add
+if ($privileges) {
+  $count=count($potential_persons);
+  $toggle_persons = new PlekitToggle ('my-slice-persons-add',
+                                     "$count people may be added to $name",
+                                     array('visible'=>get_arg('show_persons_add',false)));
+  $toggle_persons->start();
+  if ( ! $potential_persons ) {
+    // xxx improve style
+    echo "<p class='not-relevant'>No person to add</p>";
+  } else {
+    $headers=array();
+    $headers['email']='string';
+    $headers['first']='string';
+    $headers['last']='string';
+    $headers['R']='string';
+    $headers['+']="none";
+    $options = array('notes_area'=>false,
+                    'search_width'=>15,
+                    'pagesize'=>8);
+    // show search for admins only as other people won't get that many names to add
+    if ( ! plc_is_admin() ) $options['search_area']=false;
+    
+    $table=new PlekitTable2('add_persons',$headers,'0',NULL,$options);
+    $form=new PlekitForm(l_actions(),array('slice_id'=>$slice['slice_id']));
+    $form->start();
+    $table->start();
+    if ($potential_persons) foreach ($potential_persons as $person) {
+       $table->row_start();
+       $table->cell(l_person_obj($person));
+       $table->cell($person['first_name']);
+       $table->cell($person['last_name']);
+       $table->cell(plc_vertical_table ($person['roles']));
+       $table->cell ($form->checkbox_html('person_ids[]',$person['person_id']));
+       $table->row_end();
+      }
+    // add users
+    $table->tfoot_start();
+    $table->row_start();
+    $table->cell($form->submit_html ("add-persons-in-slice","Add selected"),
+                array('hfill'=>true,'align'=>'right'));
+    $table->row_end();
+    $table->end();
+    $form->end();
+  }
+  $toggle_persons->end();
+}
+$toggle->end();
+
+//////////////////////////////////////////////////////////// Nodes
+// the nodes details to display here
+// (1) we search for the tag types for which 'category' matches 'node*/ui*'
+// all these tags will then be tentatively displayed in this area
+// (2) further information can also be optionally specified in the category:
+//     (.) we split the category with '/' and search for assignments of the form var=value
+//     (.) header can be set to supersede the column header (default is tagname)
+//     (.) rank can be used for ordering the columns (default is tagname)
+//     (.) type is passed to the javascript table, for sorting (default is 'string')
+
+// minimal list as a start
+//$node_fixed_columns = array('hostname','node_id','slice_ids_whitelist','boot_state','last_contact');
+// create a VisibleTags object : basically the list of tag columns to show
+//$visibletags = new VisibleTags ($api, 'node');
+//$visiblecolumns = $visibletags->column_names();
+//$node_columns=array_merge($node_fixed_columns,$visiblecolumns);
+
+
+
+/* TEST PlekitColumns */
+
+//prepare fix and configurable columns
+
+$fix_columns = array();
+$fix_columns[]=array('tagname'=>'hostname', 'header'=>'hostname', 'type'=>'string', 'title'=>'The name of the node');
+$fix_columns[]=array('tagname'=>'peer_id', 'header'=>'AU', 'type'=>'string', 'title'=>'Authority');
+$fix_columns[]=array('tagname'=>'run_level', 'header'=>'ST', 'type'=>'string', 'title'=>'Status');
+
+$visibletags = new VisibleTags ($api, 'node');
+$visibletags->columns();
+$tag_columns = $visibletags->headers();
+
+$extra_columns = array();
+$extra_columns[]=array('tagname'=>'site_id', 'header'=>'SN', 'type'=>'string', 'title'=>'Site name', 'description'=>'Site name');
+
+//$configurable_columns = array_merge($tag_columns, $extra_columns);
+//usort ($configurable_columns, create_function('$col1,$col2','return strcmp($col1["header"],$col2["header"]);'));
+
+
+$first_time_configuration = 'false';
+$default_configuration = "hostname:f|ST:f|AU:f|Rw|AST";
+$column_configuration = "";
+
+$DescTags=$api->GetSliceTags (array('slice_id'=>$slice['slice_id']));
+for ($i=0; $i<count($DescTags); $i++ ) {
+        if ($DescTags [$i]['tagname']=='Configuration'){
+               $column_configuration = $DescTags [$i]['value'];
+                break;
+        }
+}
+
+if ($column_configuration == "")
+{
+       $first_time_configuration = 'true';
+       $column_configuration = $default_configuration;
+}
+
+//print("<p>GOT CONFIGURATION: ".$column_configuration);
+
+//$test_configuration = "hostname:f|AU:f|ST:f|Rw|AST";
+//print("<p>Parsing configuration ".$test_configuration);
+
+
+$ConfigureColumns =new PlekitColumns($column_configuration, $fix_columns, $tag_columns, $extra_columns);
+
+$node_requested_data = $ConfigureColumns->node_tags();
+$nodes=$api->GetNodes(array('node_id'=>$slice['node_ids']),$node_requested_data);
+$potential_nodes=$api->GetNodes(array('~node_id'=>$slice['node_ids']),$node_requested_data);
+
+//$nodes = array();
+//$potential_nodes = array();
+
+//print("<p>RESULTS for ".print_r(array('~node_id'=>$slice['node_ids'])));
+//print_r($nodes);
+
+$count=count($nodes);
+
+$toggle=new PlekitToggle ('my-slice-nodes',"$count nodes",
+                         array('bubble'=>
+                               'Manage nodes attached to this slice',
+                               'visible'=>get_arg('show_nodes',false)));
+$toggle->start();
+
+
+$toggle_nodes=new PlekitToggle('my-slice-nodes-configuration',
+                              "Node table column configuration",
+                              array('visible'=>'1'));
+
+$toggle_nodes->start();
+
+
+//usort ($table_headers, create_function('$col1,$col2','return strcmp($col1["header"],$col2["header"]);'));
+
+//print("<p>HEADERS TO SHOW<p>");
+//print_r($headersToShow);
+
+//print("<p>TABLE HEADERS<p>");
+//print_r($table_headers);
+
+print("<div id='debug'></div>");
+print("<input type='hidden' id='slice_id' value='".$slice['slice_id']."' />");
+print("<input type='hidden' size='80' id='column_configuration' value='".$column_configuration."' />");
+print("<input type='hidden' id='previousConf' value='".$column_configuration."'></input>");
+print("<input type='hidden' id='defaultConf' value='".$default_configuration."'></input>");
+//print("<input type='button' id='testFunctions' onclick=\"highlightOption('AU')\" value='test'></input>");
+
+$ConfigureColumns->javascript_vars();
+
+$ConfigureColumns->configuration_panel_html(true);
+
+print("<div align='center' id='loadingDiv'></div>");
+
+$toggle_nodes->end();
+
+
+////////// nodes currently in
+
+$count=count($nodes);
+
+$toggle_nodes=new PlekitToggle('my-slice-nodes-current',
+                              "$count nodes currently in $name",
+                              array('visible'=>get_arg('show_nodes_current',!$privileges)));
+
+$toggle_nodes->start();
+
+$edit_header = array();
+if ($privileges) $edit_header[plc_delete_icon()]="none";
+
+$table_options = array('search_width'=>15,
+                       'pagesize'=>20);
+//$table=new PlekitTable2('nodes',$headers,'1',$table_options);
+//$table=new PlekitTable2('nodes_pairwise',array_merge($ConfigureColumns->plekit_columns_get_headers(),$edit_header),NULL,$headersToShow, $table_option);
+//$headersToShow = $ConfigureColumns->plekit_columns_visible();
+
+$table=new PlekitTable2('nodes',array_merge($ConfigureColumns->get_headers(),$edit_header),NULL,NULL, $table_option); 
+
+$form=new PlekitForm(l_actions(),array('slice_id'=>$slice['slice_id']));
+$form->start();
+$table->start();
+if ($nodes) foreach ($nodes as $node) {
+  $table->row_start();
+
+  $table->cell($node['node_id'], array('display'=>'none'));
+  $table->cell(l_node_obj($node));
+
+  $peers->cell($table,$node['peer_id']);
+  $run_level=$node['run_level'];
+  list($label,$class) = Node::status_label_class_($node);
+  $table->cell ($label,array('name'=>'ST', 'class'=>$class, 'display'=>'table-cell'));
+
+$ConfigureColumns->cells($table, $node);
+
+
+
+  if ($privileges) $table->cell ($form->checkbox_html('node_ids[]',$node['node_id']));
+  $table->row_end();
+}
+// actions area
+if ($privileges) {
+
+  // remove nodes
+  $table->tfoot_start();
+
+  $table->row_start();
+  $table->cell($form->submit_html ("remove-nodes-from-slice","Remove selected"),
+              array('hfill'=>true,'align'=>'right'));
+  $table->row_end();
+ }
+$table->end();
+$toggle_nodes->end();
+
+////////// nodes to add
+if ($privileges) {
+  $new_potential_nodes = array();
+  if ($potential_nodes) foreach ($potential_nodes as $node) {
+      $emptywl=empty($node['slice_ids_whitelist']);
+      $inwl = (!emptywl) and in_array($slice['slice_id'],$node['slice_ids_whitelist']);
+      if ($emptywl or $inwl)
+       $new_potential_nodes[]=$node;
+  }
+  $potential_nodes=$new_potential_nodes;
+
+  $count=count($potential_nodes);
+  $toggle_nodes=new PlekitToggle('my-slice-nodes-add',
+                                "$count more nodes available",
+                                array('visible'=>get_arg('show_nodes_add',false)));
+  $toggle_nodes->start();
+
+  if ( ! $potential_nodes ) {
+    // xxx improve style
+    echo "<p class='not-relevant'>No node to add</p>";
+  } else {
+
+    $edit_header = array();
+    if ($privileges) $edit_header['+']="none";
+    
+    //$table=new PlekitTable2('add_nodes',$headers,'1', $table_options);
+    $table=new PlekitTable2('add_nodes',array_merge($ConfigureColumns->get_headers(), $edit_header),NULL,$headersToShow, $table_options);
+    $form=new PlekitForm(l_actions(),
+                        array('slice_id'=>$slice['slice_id']));
+    $form->start();
+    $table->start();
+
+    if ($potential_nodes) foreach ($potential_nodes as $node) {
+       $table->row_start();
+
+  $table->cell($node['node_id'], array('display'=>'none'));
+  $table->cell(l_node_obj($node));
+
+  $peers->cell($table,$node['peer_id']);
+  $run_level=$node['run_level'];
+  list($label,$class) = Node::status_label_class_($node);
+  $table->cell ($label,array('name'=>'ST', 'class'=>$class, 'display'=>'table-cell'));
+
+$ConfigureColumns->cells($table, $node);
+
+       $table->cell ($form->checkbox_html('node_ids[]',$node['node_id']));
+       $table->row_end();
+      }
+    // add nodes
+    $table->tfoot_start();
+    $table->row_start();
+    $table->cell($form->submit_html ("add-nodes-in-slice","Add selected"),
+                array('hfill'=>true,'align'=>'right'));
+    $table->row_end();
+    $table->end();
+    $form->end();
+  }
+  $toggle_nodes->end();
+}
+$toggle->end();
+
+//////////////////////////////////////////////////////////// Tags
+//if ( $local_peer ) {
+  $tags=$api->GetSliceTags (array('slice_id'=>$slice_id));
+  function get_tagname ($tag) { return $tag['tagname'];}
+  $tagnames = array_map ("get_tagname",$tags);
+  
+  $toggle = new PlekitToggle ('slice-tags',count_english_warning($tags,'tag'),
+                             array('bubble'=>'Inspect and set tags on tat slice',
+                                   'visible'=>get_arg('show_tags',false)));
+  $toggle->start();
+  
+  $headers=array(
+    "Name"=>"string",
+    "Value"=>"string",
+    "Node"=>"string",
+    "NodeGroup"=>"string");
+  if ($tags_privileges) $headers[plc_delete_icon()]="none";
+  
+  $table_options=array("notes_area"=>false,"pagesize_area"=>false,"search_width"=>10);
+  $table=new PlekitTable2("slice_tags",$headers,'0',NULL,$table_options);
+  $form=new PlekitForm(l_actions(),
+                       array('slice_id'=>$slice['slice_id']));
+  $form->start();
+  $table->start();
+  if ($tags) {
+    foreach ($tags as $tag) {
+      $node_name = "ALL";
+      if ($tag['node_id']) {
+        $nodes = $api->GetNodes(array('node_id'=>$tag['node_id']));
+        if($nodes) {
+          $node = $nodes[0];
+          $node_name = $node['hostname'];
+        }
+      }
+      $nodegroup_name="n/a";
+      if ($tag['nodegroup_id']) { 
+        $nodegroup=$api->GetNodeGroups(array('nodegroup_id'=>$tag['nodegroup_id']));
+        if ($nodegroup) {
+          $nodegroup = $nodegroup[0];
+          $nodegroup_name = $nodegroup['groupname'];
+        }
+      }
+      $table->row_start();
+      $table->cell(l_tag_obj($tag));
+      $table->cell($tag['value']);
+      $table->cell($node_name);
+      $table->cell($nodegroup_name);
+      if ($tags_privileges) $table->cell ($form->checkbox_html('slice_tag_ids[]',$tag['slice_tag_id']));
+      $table->row_end();
+    }
+  }
+  if ($tags_privileges) {
+    $table->tfoot_start();
+    $table->row_start();
+    $table->cell($form->submit_html ("delete-slice-tags","Remove selected"),
+                 array('hfill'=>true,'align'=>'right'));
+    $table->row_end();
+    
+    $table->row_start();
+    function tag_selector ($tag) {
+      return array("display"=>$tag['tagname'],"value"=>$tag['tag_type_id']);
+    }
+    $all_tags= $api->GetTagTypes( array ("category"=>"slice*","-SORT"=>"+tagname"), array("tagname","tag_type_id"));
+    $selector_tag=array_map("tag_selector",$all_tags);
+    
+    function node_selector($node) { 
+      return array("display"=>$node["hostname"],"value"=>$node['node_id']);
+    }
+    $all_nodes = $api->GetNodes( array ("node_id" => $slice['node_ids']), array("hostname","node_id"));
+    $selector_node=array_map("node_selector",$all_nodes);
+    
+    function nodegroup_selector($ng) {
+      return array("display"=>$ng["groupname"],"value"=>$ng['nodegroup_id']);
+    }
+    $all_nodegroups = $api->GetNodeGroups( array("groupname"=>"*"), array("groupname","nodegroup_id"));
+    $selector_nodegroup=array_map("nodegroup_selector",$all_nodegroups);
+    
+    $table->cell($form->select_html("tag_type_id",$selector_tag,array('label'=>"Choose Tag")));
+    $table->cell($form->text_html("value","",array('width'=>8)));
+    $table->cell($form->select_html("node_id",$selector_node,array('label'=>"All Nodes")));
+    $table->cell($form->select_html("nodegroup_id",$selector_nodegroup,array('label'=>"No Nodegroup")));
+    $table->cell($form->submit_html("add-slice-tag","Set Tag"),array('columns'=>2,'align'=>'left'));
+    $table->row_end();
+  }
+    
+  $table->end();
+  $form->end();
+  $toggle->end();
+//}
+
+
+//////////////////////// renew slice
+if ($local_peer ) {
+  if ( ! $renew_visible) renew_area ($slice,$site,false);
+ }
+
+$peers->block_end($peer_id);
+
+// Print footer
+include 'plc_footer.php';
+
+?>
+
+
+<script type="text/javascript">
+
+var sourceComon = '<a target="source_window" href="http://comon.cs.princeton.edu/">CoMoN</a>';
+var sourceTophat = '<b><a target="source_window" href="http://www.top-hat.info/">TopHat</a></b>';
+var sourceTophatAPI = '<b><a target="source_window" href="http://www.top-hat.info/API/">TopHat API</a></b>';
+var sourceMySlice = '<b><a target="source_window" href="http://myslice.info/">MySlice</a></b>';
+var sourceCymru = '<b><a target="source_window" href="http://www.team-cymru.org/">Team Cymru</a></b>';
+var sourceMyPLC = '<b><a target="source_window" href="http://www.planet-lab.eu/PLCAPI/">MyPLC API</a></b>';
+var sourceManiacs = '<b><a target="source_window" href="http://www.ece.gatech.edu/research/labs/MANIACS/as_taxonomy/">MANIACS</a></b>';
+var sourceMonitor = '<b><a target="source_window" href="http://monitor.planet-lab.org/">Monitor</a></b>';
+var selectReferenceNode ='Select reference node: <select id="reference_node" onChange="updateDefaultConf(this.value)"><option value=planetlab-europe-07.ipv6.lip6.fr>planetlab-europe-07.ipv6.lip6.fr</option></select>';
+var addButton = '<input id="addButton" type="button" value="Add" onclick=addColumnAjax(document.getElementById("list1").value)></input>';
+var deleteButton = '<input id="deleteButton" type="button" value="Delete" onclick=deleteColumn(window.document.getElementById("list1").value)></input>';
+
+var titleAU = 'Authority';
+var detailAU = '<i>The authority of the global PlanetLab federation that the site of the node belongs to.</i>';
+var valuesAU = 'Values: <b>PLC</b> (PlanetLab Central), <b>PLE</b> (PlanetLab Europe)';
+var sourceAU = '<b>Source:</b> '+sourceMyPLC;
+var descAU = '<span class="myslice title">'+titleAU+'</span><p>'+detailAU+'<p>'+valuesAU+'<p>'+sourceAU;
+
+var descHOSTNAME = "test";
+
+
+var titleAS = 'Autonomous system ID';
+var sourceAS = 'Source: '+sourceCymru+' (via '+sourceTophat+')';
+var valuesAS = 'Unit: <b>Integer between 0 and 65535</b>';
+var descAS = '<span class="myslice title">'+titleAS+'</span><p>'+valuesAS+'<p>' + sourceAS;
+
+var titleAST = 'Autonomous system type';
+var sourceAST = 'Source: '+sourceManiacs;
+var valuesAST = 'Values: <b>t1</b> (tier-1), <b>t2</b> (tier-2), <b>edu</b> (university), <b>comp</b> (company), <b>nic</b> (network information center), <b>ix</b> (IXP), <b>n/a</b>';
+var descAST = '<span class="myslice title">'+titleAST+'</span><p>'+valuesAST+'<p>'+sourceAST;
+
+var titleASN = 'Autonomous system name';
+var sourceASN = 'Source: '+sourceTophat;
+var descASN = '<span class="myslice title">'+titleASN+'</span><p>'+sourceASN;
+
+var selectPeriodBU = 'Select period: <select id="selectperiodBU" onChange=updatePeriod("BU")><option value=w>Week</option><option value=m>Month</option><option value=y>Year</option></select>';
+var titleBU = 'Bandwidth utilization ';
+var sourceBU = 'Source: '+sourceComon+' (via '+sourceMySlice+')';
+var valuesBU ='Unit: <b>Kbps</b>';
+var detailBU = '<i>The average Transmited bandwidh (Tx) over the selected period. The period is the most recent for which data is available, with CoMoN data being collected by MySlice daily.</i>'
+var descBU = '<span class="myslice title">'+titleBU+'</span><p>'+detailBU+'<p>'+selectPeriodBU+'<p>'+valuesBU+'<p>'+sourceBU; 
+
+var titleBW= 'Bandwidth limit';
+var sourceBW = 'Source: '+sourceComon;
+var valuesBW = 'Unit: <b>Kbps</b>';
+var detailBW = '<i>The bandwidth limit is a cap on the total outbound bandwidth usage of a node. It is set by the site administrator (PI). For more details see <a href="http://www.planet-lab.org/doc/BandwidthLimits">Bandwidth Limits (planet-lab.org)</a></i>';
+var descBW = '<span class="myslice title">'+titleBW+'</span><p>'+detailBW+'<p>'+valuesBW+'<p>'+sourceBW;
+
+var titleCC = 'Number of CPU cores';
+var sourceCC = 'Source: '+sourceComon;
+var valuesCC = 'Current PlanetLab hardware requirements: 4 cores min. <br><i>(Older nodes may have fewer cores)</i>';
+var descCC = '<span class="myslice title">'+titleCC+'</span><p>'+valuesCC+'<p>'+sourceCC;
+
+var titleCN = 'Number of CPUs';
+var sourceCN = 'Source: '+sourceComon;
+var valuesCN = 'Current PlanetLab hardware requirements: <b>1 (if quad core) or 2 (if dual core)</b>';
+var descCN = '<span class="myslice title">'+titleCN+'</span><p>'+valuesCN+'<p>'+sourceCN;
+
+var titleCR = 'CPU clock rate';
+var sourceCR = 'Source: '+sourceComon;
+var valuesCR = 'Unit: <b>GHz</b><p>Current PlanetLab hardware requirements: <b>2.4 GHz</b>';
+var descCR = '<span class="myslice title">'+titleCR+'</span><p>'+valuesCR+'<p>'+sourceCR;
+
+var selectPeriodCF = 'Select period: <select id="selectperiodCF" onChange=updatePeriod("CF")><option value=w>Week</option><option value=m>Month</option><option value=y>Year</option></select>';
+var titleCF = 'Free CPU';
+var sourceCF = 'Source: '+sourceComon+' (via '+sourceMySlice+')';
+var valuesCF = 'Unit: <b>%</b>';
+var detailCF = '<i> The average CPU percentage that could be allocated by a test slice (burb) run periodically by CoMoN </i>';
+var descCF = '<span class="myslice title">'+titleCF+'</span><p>'+detailCF+'<p>'+selectPeriodCF+'<p>'+valuesCF+'<p>'+sourceCF; 
+
+var titleDS = 'Disk size';
+var sourceDS = 'Source: '+sourceComon;
+var valuesDS = 'Unit: <b>GB</b><p>Current PlanetLab hardware requirements: <b>500GB</b>';
+var descDS = '<span class="myslice title">'+titleDS+'</span><p>'+valuesDS+'<p>'+sourceDS;
+
+var titleDU = 'Current disk utilization';
+var sourceDU = 'Source: '+sourceComon+' (via '+sourceMySlice+')';
+var valuesDU = 'Unit: <b>GB</b>';
+var detailDU = '<i> The amount of disk space currently consumed (checked daily) </i>';
+var descDU = '<span class="myslice title">'+titleDU+'</span><p>'+detailDU+'<p>'+valuesDU+'<p>'+sourceDU;
+
+var titleHC = 'Hop count from a reference node';
+var sourceHC = 'Source: '+sourceTophat;
+var detailHC = '<i>TopHat conducts traceroutes every five minutes in a full mesh between all PlanetLab nodes. The hop count is the length of the traceroute from the node to the reference node, based upon the most recently reported traceroute</i>.';
+var descHC = '<span class="myslice title">'+titleHC+'</span><p>'+detailHC+'<p>'+selectReferenceNode+'<p>'+sourceHC;
+
+var titleIP = 'IP address';
+var sourceIP = 'Source: '+sourceTophat;
+var descIP = '<span class="myslice title">'+titleIP+'</span><p>'+sourceIP;
+
+var selectPeriodL = 'Select period: <select id="selectperiodL" onChange=updatePeriod("L")><option value=w>Week</option><option value=m>Month</option><option value=y>Year</option></select>';
+var titleL= 'Load ';
+var sourceL = 'Source: '+sourceComon;
+var valuesL = 'Unit: <b>float</b>';
+var detailL = '<i>The average 5-minute Unix load (as reported by the uptime command) over the selected period</i>';
+var descL = '<span class="myslice title">'+titleL+'</span><p>'+detailL+'<p>'+selectPeriodL+'<p>'+valuesL+'<p>'+sourceL; 
+
+var titleLON= 'Longitude';
+var sourceLON = 'Source: '+sourceTophat;
+var descLON = '<span class="myslice title">'+titleLON+'</span><p>'+sourceLON;
+
+var titleLAT= 'Latitude';
+var sourceLAT = 'Source: '+sourceTophat;
+var descLAT = '<span class="myslice title">'+titleLAT+'</span><p>'+sourceLAT;
+
+var titleLCN= 'Location (Country)';
+var sourceLCN = 'Source: '+sourceTophat;
+var detailLCN = '<i>Based on the latitude, longitude information</i>';
+var descLCN = '<span class="myslice title">'+titleLCN+'</span><p>'+detailLCN+'<p>'+sourceLCN;
+
+var titleLCT= 'Location (Continent)';
+var sourceLCT = 'Source: '+sourceTophat;
+var detailLCT = '<i>Based on the latitude, longitude information</i>';
+var descLCT = '<span class="myslice title">'+titleLCT+'</span><p>'+detailLCT+'<p>'+sourceLCT;
+
+var titleLCY= 'Location (City)';
+var sourceLCY = 'Source: '+sourceTophat;
+var detailLCY = '<i>Based on the latitude, longitude information</i>';
+var descLCY = '<span class="myslice title">'+titleLCY+'</span><p>'+detailLCY+'<p>'+sourceLCY;
+
+var titleLPR= 'Location precision radius';
+var sourceLPR = 'Source: '+sourceTophat;
+var valuesLPR = 'Unit: <b>float</b>';
+var detailLPR = '<i>The radius of the circle corresponding to the error in precision of the geolocalization</i>';
+var descLPR = '<span class="myslice title">'+titleLPR+'</span><p>'+detailLPR+'<p>'+valuesLPR+'<p>'+sourceLPR;
+
+var titleLRN= 'Location (Region)';
+var sourceLRN = 'Source: '+sourceTophat;
+var detailLRN = '<i>Based on the latitude, longitude information</i>';
+var descLRN = '<span class="myslice title">'+titleLRN+'</span><p>'+detailLRN+'<p>'+sourceLRN;
+
+var titleMS= 'Memory size';
+var sourceMS = 'Source: '+sourceComon;
+var valuesMS = 'Unit: <b>GB</b><p>Current PlanetLab hardware requirements: <b>4GB</b>';
+var descMS = '<span class="myslice title">'+titleMS+'</span><p>'+valuesMS+'<p>'+sourceMS;
+
+var selectPeriodMU = 'Select period: <select id="selectperiodMU" onChange=updatePeriod("MU")><option value=w>Week</option><option value=m>Month</option><option value=y>Year</option></select>';
+var titleMU = 'Memory utilization';
+var sourceMU = 'Source: '+sourceComon;
+var valuesMU = '<p>Unit: <b>%</b>';
+var detailMU = '<i>The average active memory utilization as reported by CoMoN</i>';
+var descMU = '<span class="myslice title">'+titleMU+'</span><p>'+detailMU+'<p>'+selectPeriodMU+'<p>'+valuesMU+'<p>'+sourceMU; 
+
+var titleNEC= 'Network information (ETOMIC)';
+var sourceNEC = 'Source: '+sourceTophat;
+var valuesNEC = 'Values: <b>yes/no</b>';
+var detailNEC = '<i>The existence of a colocated ETOMIC box. When an ETOMIC box is present you have the possibility to conduct high-precision measurements through the '+sourceTophatAPI+'</i>';
+var descNEC = '<span class="myslice title">'+titleNEC+'</span><p>'+detailNEC+'<p>'+valuesNEC+'<p>'+sourceNEC;
+
+var titleNTH= 'Network information (TopHat)';
+var sourceNTH = 'Source: '+sourceTophat;
+var valuesNTH = 'Values: <b>yes/no</b>';
+var detailNTH = '<i>The existence of a colocated TopHat agent. When a TopHat agent is present you have the possibility to conduct high-precision measurements through the '+sourceTophatAPI+'</i>';
+var descNTH = '<span class="myslice title">'+titleNTH+'</span><p>'+detailNTH+'<p>'+valuesNTH+'<p>'+sourceNTH;
+
+var titleNDS= 'Network information (DIMES)';
+var sourceNDS = 'Source: '+sourceTophat;
+var valuesNDS = 'Values: <b>yes/no</b>';
+var detailNDS = '<i>The existence of a colocated DIMES agent. When a DIMES agent is present you have the possibility to conduct high-precision measurements through the '+sourceTophatAPI+'</i>';
+var descNDS = '<span class="myslice title">'+titleNDS+'</span><p>'+detailNDS+'<p>'+valuesNDS+'<p>'+sourceNDS;
+
+var titleNSF= 'Network information (spoof)';
+var sourceNSF = 'Source: '+sourceTophat;
+var valuesNSF = '<p>Values: <b>yes/no</b>';
+var detailNSF = '<i> Whether the node can send packets packets using the IP spoof option</i>';
+var descNSF = '<span class="myslice title">'+titleNSF+'</span><p>'+detailNSF+'<p>'+valuesNSF+'<p>'+sourceNSF;
+
+var titleNSR= 'Network information (source route)';
+var sourceNSR = 'Source: '+sourceTophat;
+var valuesNSR = '<p>Values: <b>yes/no</b>';
+var detailNSR = '<i> Whether the node can send packets packets using the IP source route option</i>';
+var descNSR = '<span class="myslice title">'+titleNSR+'</span><p>'+detailNSR+'<p>'+valuesNSR+'<p>'+sourceNSR;
+
+var titleNTP= 'Network information (timestamp)';
+var sourceNTP = 'Source: '+sourceTophat;
+var valuesNTP = '<p>Values: <b>yes/no</b>';
+var detailNTP = '<i> Whether the node can send packets packets using the IP timestamp option</i>';
+var descNTP = '<span class="myslice title">'+titleNTP+'</span><p>'+detailNTP+'<p>'+valuesNTP+'<p>'+sourceNTP;
+
+var titleNRR= 'Network information (record route)';
+var sourceNRR = 'Source: '+sourceTophat;
+var valuesNRR = '<p>Values: <b>yes/no</b>';
+var detailNRR = '<i> Whether the node can send packets packets using the IP record route option</i>';
+var descNRR = '<span class="myslice title">'+titleNRR+'</span><p>'+detailNRR+'<p>'+valuesNRR+'<p>'+sourceNRR;
+
+var titleOS = 'Operating system';
+var sourceOS = 'Source: '+sourceMyPLC;
+var valuesOS = 'Values: <b>Fedora, Cent/OS, other, n/a</b>';
+var descOS = '<span class="myslice title">'+titleOS+'</span><p>'+valuesOS+'<p>'+sourceOS;
+
+var selectPeriodR = 'Select period: <select id="selectperiodR" onChange=updatePeriod("R")><option value=w>Week</option><option value=m>Month</option><option value=y>Year</option></select>';
+var titleR = 'Reliability';
+var sourceR = 'Source: '+sourceComon+' (via '+sourceMySlice+')';
+var detailR = '<i>CoMoN queries nodes every 5 minutes, for 255 queries per day. The average reliability is the percentage of queries over the selected period for which CoMoN reports a value. The period is the most recent for which data is available, with CoMoN data being collected by MySlice daily</i>';
+var valuesR = 'Unit: <b>%</b>';
+var descR = '<span class="myslice title">'+titleR+'</span><p>'+detailR+'<p>'+selectPeriodR+'<p>'+valuesR+'<p>'+sourceR; 
+
+var titleRES = 'Reservation capabilities';
+var sourceRES = 'Source: '+sourceMyPLC;
+var valuesRES = 'Values: <b>yes/no</b>';
+var detailRES = '<i> Whether the node can be reserved for a certain duration</i>';
+var descRES = '<span class="myslice title">'+titleRES+'</span><p>'+detailRES+'<p>'+valuesRES+'<p>'+sourceRES;
+
+var selectPeriodS = 'Select period: <select id="selectperiodS" onChange=updatePeriod("S")><option value=w>Week</option><option value=m>Month</option><option value=y>Year</option></select>';
+var titleS = 'Active slices';
+var sourceS = 'Source: '+sourceComon+' (via '+sourceMySlice+')';
+var valuesS = 'Unit: <b>%</b>';
+var detailS = '<i>Average number of active slices over the selected period for which CoMoN reports a value. The period is the most recent for which data is available, with CoMoN data being collected by MySlice daily</i>';
+var descS = '<span class="myslice title">'+titleS+'</span><p>'+detailS+'<p>'+selectPeriodS+'<p>'+valuesS+'<p>'+sourceS; 
+
+var titleSN = 'Site name';
+var sourceSN = 'Source: '+sourceMyPLC;
+var descSN = '<span class="myslice title">'+titleSN+'</span><p>'+sourceSN;
+
+var selectPeriodSSH = 'Select period: <select id="selectperiodSSH" onChange=updatePeriod("SSH")><option value=w>Week</option><option value=m>Month</option><option value=y>Year</option></select>';
+var titleSSH = 'Average SSH response delay';
+var valuesSSH = 'Unit: <b>%</b>';
+var detailSSH = '<i>The average response delay of the node to SSH logins over the selected period for which CoMoN reports a value. The period is the most recent for which data is available, with CoMoN data being collected by MySlice daily</i>';
+var sourceSSH ='Source: '+sourceComon+' (via '+sourceMySlice+')';
+var descSSH = '<span class="myslice title">'+titleSSH+'</span><p>'+detailSSH+'<p>'+selectPeriodSSH+'<p>'+valuesSSH+'<p>'+sourceSSH; 
+
+var titleST = 'Status';
+var sourceST = 'Source: '+sourceMonitor;
+var valuesST = 'Values: <b>online</b> (up and running), <b>good</b> (up and running recently), <b>offline</b> (unreachable today), <b>down</b> (unreachable nodes for more than 1 day), <b>failboot</b> (safeboot, MyPLC API term for debug)';
+var descST = '<span class="myslice title">'+titleST+'</span><p>'+valuesST+'<p>'+sourceST;
+
+highlightOption("AU");
+overrideTitles();
+
+
+/*
+document.defaultAction = false;
+document.onkeyup = detectEvent;
+
+function detectEvent(e) {
+       var evt = e || window.event;
+       //debugfilter(evt.type);
+       //debugfilter('keyCode is ' + evt.keyCode);
+       //debugfilter('charCode is ' + evt.charCode);
+       //debugfilter(document.getElementById('testfocus').focused);
+       return document.defaultAction;
+}
+*/
+
+
+</script>
diff --git a/plekit/php/columns.php b/plekit/php/columns.php
new file mode 100644 (file)
index 0000000..987d9be
--- /dev/null
@@ -0,0 +1,557 @@
+<?php
+
+require_once 'tophat_api.php';
+
+drupal_set_html_head('
+<script type="text/javascript" src="/plekit/table/columns.js"></script>
+');
+
+class PlekitColumns {
+
+var $column_configuration = "";
+var $reference_node = "";
+var $first_time = false;
+
+var $all_headers = array();
+var $this_table_headers = array();
+var $visible_headers = array();
+
+var $all_columns = array();
+var $fix_columns = array();
+var $tag_columns = array();
+var $extra_columns = array();
+
+var $table_ids;
+
+var $HopCount = array();
+
+  function PlekitColumns ($column_configuration, $fix_columns, $tag_columns, $extra_columns=NULL, $this_table_headers=NULL) {
+
+       $this->fix_columns = $fix_columns;
+       $this->tag_columns = $tag_columns;
+       $this->extra_columns = $extra_columns;
+
+       //print("<p>FIX<p>");
+       //print_r($this->fix_columns);
+       //print("<p>TAG<p>");
+       //print_r($this->tag_columns);
+       //print("<p>EXTRA<p>");
+       //print_r($this->extra_columns);
+
+       $this->prepare_headers();
+       $this->parse_configuration($column_configuration);
+
+       $this->visible_headers = $this->get_visible();
+
+       //print("<p>VISIBLE<p>");
+       //print_r($this->visible_headers);
+
+       $this->all_columns = array_merge($fix_columns, $tag_columns, $extra_columns);
+}
+
+
+
+/*
+
+INFO
+
+*/
+
+function prepare_headers() {
+
+foreach ($this->fix_columns as $column) {
+$this->all_headers[$column['header']]=array('header'=>$column['header'],'type'=>$column['type'],'tagname'=>$column['tagname'],'title'=>$column['title'], 'description'=>$column['title'], 'label'=>$column['header'], 'fixed'=>true, 'visible'=>false);
+}
+
+foreach ($this->tag_columns as $column) {
+$this->all_headers[$column['header']]=array('header'=>$column['header'],'type'=>$column['type'],'tagname'=>$column['tagname'],'title'=>$column['tagname'], 'description'=>$column['title'], 'label'=>$this->removeDuration($column['header']),'visible'=>false);
+}
+
+foreach ($this->extra_columns as $column) {
+$this->all_headers[$column['header']]=array('header'=>$column['header'],'type'=>$column['type'],'tagname'=>$column['tagname'],'title'=>$column['tagname'], 'description'=>$column['title'], 'label'=>$this->removeDuration($column['header']),'visible'=>false);
+}
+
+return $this->all_headers;
+
+}
+
+
+function get_headers() {
+
+return $this->all_headers;
+
+}
+
+function node_tags() {
+
+       $fetched_tags = array('node_id');       
+
+       foreach ($this->all_headers as $h)
+       {
+               if ($h['visible'] == true)
+                       $fetched_tags[] = $h['tagname'];
+       }
+
+       return $fetched_tags;
+}
+
+function print_headers() {
+
+       $headers = "";  
+
+       foreach ($this->all_headers as $h)
+       {
+               $headers.="<br>".$h['header'].":".$h['label'].":".$h['tagname'];
+       }
+       return $headers;
+}
+
+function get_visible() {
+
+       $visibleHeaders = array();      
+
+       foreach ($this->all_headers as $h)
+       {
+               if ($h['visible'] == true)
+                       $visibleHeaders[] = $h['header'];
+       }
+       return $visibleHeaders;
+}
+
+function headerIsVisible($header_name) {
+
+$headersToShow = $this->visible_headers;
+
+
+       if ($this->inTypeC($header_name."w") || $this->inTypeC($header_name."m") || $this->inTypeC($header_name."y"))
+               return (in_array($header_name."w", $headersToShow) || in_array($header_name."m", $headersToShow) || in_array($header_name."y", $headersToShow));
+       else
+               return in_array($header_name, $headersToShow);
+}
+
+
+
+
+/*
+
+CONFIGURATION
+
+*/
+
+
+function parse_configuration($column_configuration) {
+
+       $this->column_configuration = $column_configuration;
+       //$this->default_configuration = $default_configuration;
+
+       $columns_conf = explode("|", $column_configuration);
+       foreach ($columns_conf as $c)
+       {
+               $conf = explode(":",$c);
+
+                $this->all_headers[$conf[0]]['visible']=true;
+               //print("<p>".$conf[0]."should be visible now");
+               //print_r($this->all_headers[$conf[0]]);
+
+               if ($conf[1] == "f")
+                       continue;
+
+               else if ($this->inTypeC($conf[0]))
+               {
+                       $this->all_headers[$conf[0]]['duration']= substr($conf[0], strlen($conf[0])-1, strlen($conf[0]));
+                       $threshold = explode(",",$conf[1]);
+                       $this->all_headers[$conf[0]]['threshold']=$threshold;
+               }
+               else if ($this->inTypeD($conf[0]))
+               {
+                       $this->reference_node = $conf[1];
+                       $this->reference_node = "planetlab-europe-07.ipv6.lip6.fr";
+                       $this->all_headers[$conf[0]]['refnode']=$this->reference_node;
+                       $threshold = explode(",",$conf[1]);
+                       $this->all_headers[$conf[0]]['threshold']=$threshold;
+               }
+               else if ($this->inTypeA($conf[0]))
+               {
+                       $exclude_list = explode(",",$conf[1]);
+                       $this->all_headers[$conf[0]]['exclude_list']=$exclude_list;
+               }
+               else
+               {
+                       $threshold = explode(",",$conf[1]);
+                       $this->all_headers[$conf[0]]['threshold']=$threshold;
+               }
+       }
+}
+
+
+               
+
+
+/*
+
+CELLS
+
+*/
+
+function getHopCount($ref_node, $planetlab_nodes)
+{
+
+$tophat_auth = array( 'AuthMethod' => 'password', 'Username' => 'guest', 'AuthString' => 'guest');
+$tophat_api = new TopHatAPI($tophat_auth);
+
+$traceroute = $tophat_api->Get('traceroute', 'latest', array('src_hostname' => $ref_node, 'dst_hostname' => $planetlab_nodes), array('dst_hostname', 'hop_count') );
+
+$hopcount = array();
+
+if ($traceroute) foreach ($traceroute as $t)
+$hopcount[$t['dst_hostname']]=$t['hop_count'];
+return $hopcount;
+}
+
+
+//Depending on the columns selected more data might need to be fetched from
+//external sources
+
+function fetch_data($nodes) {
+
+//TopHat pairwise data
+
+       if ($this->reference_node != "")
+       {
+               $dd = array();
+
+               if ($nodes) foreach ($nodes as $n)
+                       $dd[] = $n['hostname'];
+
+               if ($potential_nodes) foreach ($potential_nodes as $n)
+                       $dd[] = $n['hostname'];
+
+               print("Calling tophat api for reference node = ".$this->reference_node);
+               $st = time() + microtime();
+               $HopCount = $this->getHopCount($this->reference_node, $dd);
+               printf(" (%.2f ms)<br/>", (time() + microtime()-$st)*100);
+               //print_r($HopCount);
+       }
+
+}
+
+
+function excludeItems($value, $exclude_list, $hh) {
+
+        if ($value == "")
+                $value = "n/a";
+
+        if ($exclude_list)
+        if (in_array($value, $exclude_list))
+                return array($value, array('name'=>$hh, 'display'=>'table-cell'));
+        else
+                return array($value, array('name'=>$hh, 'display'=>'table-cell'));
+
+        return array($value, array('name'=>$hh, 'display'=>'table-cell'));
+}
+
+function checkThreshold($value, $threshold, $hh) {
+
+        if ($value == "")
+                return array("n/a", array('name'=>$hh, 'display'=>'table-cell'));
+
+        if ($threshold)
+        if ((float) $value >= (float) $threshold[0] && (float) $value <= (float) $threshold[1])
+                return array(round($value,1), array('name'=>$hh, 'display'=>'table-cell'));
+        else
+                return array(round($value,1), array('name'=>$hh, 'display'=>'table-cell'));
+
+        return array(round($value,1), array('name'=>$hh, 'display'=>'table-cell'));
+}
+
+
+function cells($table, $node) {
+
+
+foreach ($this->all_headers as $h)
+{
+
+if (!$h['fixed'] && $h['visible'])
+{
+if ($this->inTypeC($h['header']))
+{
+        $tagname = $h['tagname'];
+        $value = $node[$tagname];
+        $v = $this->checkThreshold($value, $h['threshold'], $h['header']);
+        $table->cell($v[0],$v[1]);
+}
+else if ($this->inTypeB($h['header']))
+{
+        $value = $node[$h['tagname']];
+        $v = $this->checkThreshold($value, $h['threshold'], $h['header']);
+        $table->cell($v[0],$v[1]);
+}
+else if ($this->inTypeD($h['header']))
+{
+        $value = $this->HopCount[$node['hostname']];
+        $v = $this->excludeItems($value, $h['threshold'], $h['header']);
+        $table->cell($v[0],$v[1]);
+}
+else if ($this->inTypeA($h['header']))
+{
+        $value = $node[$h['tagname']];
+        $v = $this->excludeItems($value, $h['exclude_list'], $h['header']);
+        $table->cell($v[0],$v[1]);
+}
+else
+{
+        $value = $node[$h['tagname']];
+        $table->cell($value,array('name'=>$h['header'], 'display'=>'table-cell'));
+}
+}
+else
+        $table->cell("??", array('name'=>$h['header'], 'display'=>'none'));
+
+}
+
+}
+
+
+/*
+
+HTML
+
+*/
+
+
+function javascript_vars() {
+
+print("<input type='hidden' id='reference_node' value='".$this->reference_node."' />");
+
+$all_columns_string = "";
+foreach ($this->all_headers as $h)
+        $all_columns_string.= $h['header'].",";
+
+print("<script type='text/javascript'>");
+
+print("var column_table = new Array();"); 
+print ("var column_index=0;");
+print("var column_table2 = new Array();");
+print("var column_headers = new Array();");
+print("var columns_to_fetch = new Array();");
+//document.onkeyup = scrollList('test');
+foreach ($this->all_headers as $h)
+{
+/*
+        print("column_table2[column_index] = new Array();");
+        print("column_table2[column_index]['header'] = ".$h['header'].";");
+        print("column_table2[column_index]['label'] = ".$h['label'].";");
+        if ($h['visible'])
+       {
+        print("column_table2[column_index]['visible'] = true;");
+        print("column_table2[column_index]['fetched'] = true;");
+       }
+        else
+       {
+        print("column_table2[column_index]['visible'] = false;");
+        print("column_table2[column_index]]['fetch'] = false;");
+       }
+        print("column_table2[column_index]['tagname'] = '".$h['tagname']."';");
+        print("column_table2[column_index]['description'] = '".$h['description']."';");
+*/
+
+        print("column_table[\"".$h['header']."\"] = new Array();");
+        if ($h['visible'])
+       {
+        print("column_table['".$h['header']."']['visible'] = true;");
+        print("column_table['".$h['header']."']['fetched'] = true;");
+       }
+        else
+       {
+        print("column_table['".$h['header']."']['visible'] = false;");
+        print("column_table['".$h['header']."']['fetch'] = false;");
+       }
+
+        print("column_table['".$h['header']."']['tagname'] = '".$h['tagname']."';");
+        print("column_table['".$h['header']."']['description'] = '".$h['description']."';");
+}
+
+foreach ($this->all_headers as $h)
+        print("column_headers['".$h['label']."'] = '0';");
+
+print("var all_columns_string = '".substr($all_columns_string,0,strlen($all_columns_string)-1)."';");
+
+//if ($first_time == true)
+        //print("resetConfiguration();");
+//print("debugfilter('vars are OK');");
+
+print("</script>");
+
+}
+
+function configuration_panel_html($showDescription) {
+
+
+if ($showDescription)
+       $table_width = 700;
+else
+       $table_width = 350;
+
+print("<table align=center cellpadding=10 width=".$table_width.">");
+print("<tr><th>Add/delete columns</th>");
+
+if ($showDescription)
+       print("<th>Column description and configuration</th>");
+
+print("</tr><tr><td valign=top width=300>");
+
+       print('<div id="scrolldiv" onFocusOut="debugfilter(this.id);" style="border : solid 2px grey; padding:4px; width:300px; height:180px; overflow:auto;">');
+print ("<table>");
+       $prev_label="";
+       $optionclass = "out";
+       foreach ($this->all_headers as $h)
+       {
+               if ($h['header'] == "hostname")
+                       continue;
+
+               if ($h['fixed'])
+                       $disabled = "disabled=true";
+               else
+                       $disabled = "";
+
+               if ($this->headerIsVisible($h['label']))
+               {
+                       $selected = "checked=true";
+                       //print("header ".$h['label']." checked!");
+               }
+               else
+               {
+                       $selected = "";
+               }
+
+               if ($prev_label == $h['label'])
+                       continue;
+
+               $prev_label = $h['label'];
+
+               print ("<tr><td><div id='".$h['label']."' class='".$optionclass."' onkeyup='debugfilter(this.id)' onclick='highlightOption(this.id)'><table width=280 id='table".$h['label']."'><tr><td align=left width=30>".$h['label']."</td><td align=left>&nbsp;<span style='height:10px' id ='htitle".$h['label']."'>".$h['title']."</span>&nbsp;</td><td align=right width=20><input id='check".$h['label']."' type='checkbox' ".$selected." ".$disabled." autocomplete='off' value='".$h['label']."' onclick='changeCheckStatus(this.id)'></input></td></tr></table></div></td></tr>");
+       }
+
+       print("</table> </div></td>");
+
+if ($showDescription)
+{
+       print("<td valign=top width=400>");
+       print("<div class='myslice' id='selectdescr'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</div></td>");
+}
+
+print("</tr><tr><td align=center>");
+//print("<input type='button' value='Reset' onclick=resetCols('previousConf') />");
+//print("<input type='button' value='Default' onclick=saveConfiguration('defaultConf') />");
+print("<input type='button' value='Reset table' onclick=\"resetConfiguration('defaultConf')\" />");
+print("&nbsp;<input type='button' value='Save configuration' onclick=saveConfiguration('column_configuration') />");
+print("&nbsp;<input type='button' id='fetchbutton' onclick='fetchData()' value='Fetch data' disabled=true /> </td>");
+
+if ($showDescription)
+       print("<td></td>");
+
+print(" </tr> </table>");
+}
+
+function column_filter () {
+
+echo <<< EOF
+
+Highlight <select onChange="filterByType(this.value)">
+<option value="none">None</option>
+<option value="capabilities">Capabilities</option>
+<option value="statistics">Statistics</option>
+<option value="network">Network</option>
+<option value="pairwise">Pairwise</option>
+<option value="other">Other</option>
+</select>
+<p>
+
+EOF;
+}
+
+  function column_html ($colHeader, $colName, $colId, $fulldesc, $visible) {
+
+       if ($visible) 
+               $display = 'display:table-cell';
+       else 
+               $display = 'color:red;display:none';
+
+    return "
+       <th class='sample plekit_table' name='confheader".$colHeader."' id='testid' style='".$display."'>
+       <div id=\"".$colId."\" onclick=\"showDescription('".$colHeader."')\" onmouseover=\"showDescription('".$colHeader."')\">$colHeader</div>
+               </th>
+       ";
+  }
+
+  function column_fix_html ($colHeader, $colName, $colId) {
+
+       $display = 'display:table-cell';
+
+        $res="<th name='confheader".$colHeader."' class='fix plekit_table' style='$display'>";
+               $res.= "<div id='$colId' onmouseover=\"showDescription('".$colHeader."')\">$colHeader</div></th>";
+
+       return $res;
+  }
+
+
+function graph_html($colHeader) {
+
+       return "<p><img src='/planetlab/slices/graph.png' width='20' align='BOTTOM'><input type='checkbox' id='graph".$colHeader."'></input> Show details on mouse over";
+
+       }
+
+function threshold_html($colHeader) {
+
+       $updatecall = "updateColumnThreshold('".$colHeader."',window.document.getElementById('min".$colHeader."').value,window.document.getElementById('max".$colHeader."').value);";
+
+       $bubble="<b>Grey-out values between</b>  <input type='text' id='min".$colHeader."' size='2' value='5'> (low) and <input type='text' id='max".$colHeader."' size='2' value='90'> (high) <input type='submit' value='Update' onclick=".$updatecall.">&nbsp;</input>"; 
+
+       return $bublle;
+}
+
+
+/*
+
+UTILS
+
+*/
+
+//simple strings
+function inTypeA($header_name) {
+       $typeA = array('ST','SN','RES','OS','NRR','NTP','NSR','NSF','NDS','NTH','NEC','LRN','LCY','LPR','LCN','LAT','LON','IP','ASN','AST');
+       return in_array($header_name, $typeA);
+}
+
+//integers
+function inTypeB($header_name) {
+       $typeB = array('BW','DS','MS','CC','CR','AS','DU','CN');
+       return in_array($header_name, $typeB);
+}
+
+//statistical values
+function inTypeC($header_name) {
+       $typeC = array('Rw','Rm','Ry','Lw','Lm','Ly','Sw','Sm','Sy','CFw','CFm','CFy','BUw','BUm','BUy','MUw','MUm','MUy','SSHw','SSHm','SSHy');
+       return in_array($header_name, $typeC);
+}
+
+//tophat
+function inTypeD($header_name) {
+       $typeD = array('HC');
+       return in_array($header_name, $typeD);
+}
+
+
+function removeDuration($header)
+{
+       if ($this->inTypeC($header))
+               return substr($header, 0, strlen($header)-1);
+       else
+               return $header;
+}
+
+}
+
+?>
+
+
diff --git a/plekit/php/tophat_api.php b/plekit/php/tophat_api.php
new file mode 100644 (file)
index 0000000..50eb146
--- /dev/null
@@ -0,0 +1,292 @@
+<?php
+//
+// TopHat API PHP interface
+//
+
+// TODO add tophat_api in the php default path in /etc/plc.d/httpd
+// TODO occurences to PLC
+
+define('TOPHAT_API_HOST', 'www.top-hat.info');
+define('TOPHAT_API_PATH', '/API/');
+define('TOPHAT_API_PORT', 443);
+
+class TopHatAPI
+{
+  var $auth;
+  var $server;
+  var $port;
+  var $path;
+  var $errors;
+  var $trace;
+  var $calls;
+  var $multicall;
+
+  function TopHatAPI($auth = NULL,
+                 $server = TOPHAT_API_HOST,
+                 $port = TOPHAT_API_PORT,
+                 $path = TOPHAT_API_PATH,
+                 $cainfo = NULL)
+  {
+    $this->auth = $auth;
+    $this->server = $server;
+    $this->port = $port;
+    $this->path = $path;
+    $this->cainfo = $cainfo;
+    $this->errors = array();
+    $this->trace = array();
+    $this->calls = array();
+    $this->multicall = false;
+  }
+
+  function error_log($error_msg, $backtrace_level = 1)
+  {
+    $backtrace = debug_backtrace();
+    $file = $backtrace[$backtrace_level]['file'];
+    $line = $backtrace[$backtrace_level]['line'];
+
+    $this->errors[] = 'TopHatAPI error:  ' . $error_msg . ' in ' . $file . ' on line ' . $line;
+    error_log(end($this->errors));
+  }
+
+  function error()
+  {
+    if (empty($this->trace)) {
+      return NULL;
+    } else {
+      $last_trace = end($this->trace);
+      return implode("\\n", $last_trace['errors']);
+    }
+  }
+
+  function trace()
+  {
+    return $this->trace;
+  }
+
+  function microtime_float()
+  {
+    list($usec, $sec) = explode(" ", microtime());
+    return ((float) $usec + (float) $sec);
+  }
+
+  function call($method, $args = NULL)
+  {
+    if ($this->multicall) {
+      $this->calls[] = array ('methodName' => $method,
+                               'params' => $args);
+      return NULL;
+    } else {
+      return $this->internal_call ($method, $args, 3);
+    }
+  }
+
+  function internal_call($method, $args = NULL, $backtrace_level = 2)
+  {
+      $curl = curl_init();
+
+      // Verify peer certificate if talking over SSL
+      if ($this->port == 443) {
+          curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); // XXX 2
+          if (!empty($this->cainfo)) {
+              curl_setopt($curl, CURLOPT_CAINFO, $this->cainfo);
+          } elseif (defined('PLC_API_CA_SSL_CRT')) {
+              curl_setopt($curl, CURLOPT_CAINFO, PLC_API_CA_SSL_CRT);
+          }
+          $url = 'https://';
+      } else {
+          $url = 'http://';
+      }
+
+      // Set the URL for the request
+      $url .= $this->server . ':' . $this->port . '/' . $this->path;
+      curl_setopt($curl, CURLOPT_URL, $url);
+
+      // Marshal the XML-RPC request as a POST variable. <nil/> is an
+      // extension to the XML-RPC spec that is supported in our custom
+      // version of xmlrpc.so via the 'allow_null' output_encoding key.
+      $request = xmlrpc_encode_request($method, $args, array('allow_null' => TRUE));
+      curl_setopt($curl, CURLOPT_POSTFIELDS, $request);
+
+      // Construct the HTTP header
+      $header[] = 'Content-type: text/xml';
+      $header[] = 'Content-length: ' . strlen($request);
+      curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
+
+      // Set some miscellaneous options
+      curl_setopt($curl, CURLOPT_TIMEOUT, 180);
+
+      // Get the output of the request
+      curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
+      $t0 = $this->microtime_float();
+      $output = curl_exec($curl);
+      $t1 = $this->microtime_float();
+
+      if (curl_errno($curl)) {
+          $this->error_log('curl: ' . curl_error($curl), true);
+          $ret = NULL;
+      } else {
+          $ret = xmlrpc_decode($output);
+          if (is_array($ret) && xmlrpc_is_fault($ret)) {
+              $this->error_log('Fault Code ' . $ret['faultCode'] . ': ' .
+                      $ret['faultString'], $backtrace_level, true);
+              $ret = NULL;
+          }
+      }
+
+      curl_close($curl);
+
+      $this->trace[] = array('method' => $method,
+              'args' => $args,
+              'runtime' => $t1 - $t0,
+              'return' => $ret,
+              'errors' => $this->errors);
+      $this->errors = array();
+
+      return $ret;
+  }
+
+  function begin()
+  {
+    if (!empty($this->calls)) {
+      $this->error_log ('Warning: multicall already in progress');
+    }
+
+    $this->multicall = true;
+  }
+
+  function commit()
+  {
+    if (!empty ($this->calls)) {
+      $ret = array();
+      $results = $this->internal_call ('system.multicall', array ($this->calls));
+      foreach ($results as $result) {
+        if (is_array($result)) {
+          if (xmlrpc_is_fault($result)) {
+            $this->error_log('Fault Code ' . $result['faultCode'] . ': ' .
+                             $result['faultString'], 1, true);
+            $ret[] = NULL;
+           // Thierry - march 30 2007 
+           // using $adm->error() is broken with begin/commit style 
+           // this is because error() uses last item in trace and checks for ['errors']
+           // when using begin/commit we do run internal_call BUT internal_call checks for 
+           // multicall's result globally, not individual results, so ['errors'] comes empty
+           // I considered hacking internal_call 
+           // to *NOT* maintain this->trace at all when invoked with multicall
+           // but it is too complex to get all values right
+           // so let's go for the hacky way, and just record individual errors at the right place
+            $this->trace[count($this->trace)-1]['errors'][] = end($this->errors);
+          } else {
+            $ret[] = $result[0];
+          }
+        } else {
+          $ret[] = $result;
+        }
+      }
+    } else {
+      $ret = NULL;
+    }
+
+    $this->calls = array();
+    $this->multicall = false;
+
+    return $ret;
+  }
+
+  //
+  // TopHatAPI Methods
+  //
+
+  // Gets measurement information.
+  //
+  // Returns the measurement (cf doc).
+
+  function Test($param = NULL)
+  {
+    $args[] = $this->auth;
+    if (func_num_args() > 0) $args[] = $param;
+    return $this->call('Test', $args);
+  }
+
+  function Get($method, $timestamp, $input_filter = NULL, $output_fields = NULL, $callback = NULL)
+  {
+    $args[] = $this->auth;
+    $args[] = $method;
+    $args[] = $timestamp;
+    if (func_num_args() > 2) $args[] = $input_filter;
+    if (func_num_args() > 3) $args[] = $output_fields;
+    if (func_num_args() > 4) $args[] = $callback;
+    return $this->call('Get', $args);
+  }
+
+  // TDMI Methods
+
+  function GetPlatforms($input_filter = NULL, $output_fields = NULL)
+  {
+    $args[] = $this->auth;
+    if (func_num_args() > 0) $args[] = $input_filter;
+    if (func_num_args() > 1) $args[] = $output_fields;
+    return $this->call('GetPlatforms', $args);
+  }
+
+  function GetTraceroutes($input_filter = NULL, $output_fields = NULL)
+  {
+    $args[] = $this->auth;
+    if (func_num_args() > 0) $args[] = $input_filter;
+    if (func_num_args() > 1) $args[] = $output_fields;
+    return $this->call('GetTraceroutes', $args);
+  }
+
+  // Imported PLC Methods
+
+  // Returns a new session key if a user or node authenticated
+  // successfully, faults otherwise.
+  
+  function GetSession ()
+  {
+    $args[] = $this->auth;
+    return $this->call('GetSession', $args);
+  }
+  
+  // Returns an array of structs containing details about users sessions. If
+  // session_filter is specified and is an array of user identifiers or
+  // session_keys, or a struct of session attributes, only sessions matching the
+  // filter will be returned. If return_fields is specified, only the
+  // specified details will be returned.
+  
+  function GetSessions ($session_filter = NULL)
+  {
+    $args[] = $this->auth;
+    if (func_num_args() > 0) $args[] = $session_filter;
+    return $this->call('GetSessions', $args);
+  }
+  
+  // Returns an array of structs containing details about users. If
+  // person_filter is specified and is an array of user identifiers or
+  // usernames, or a struct of user attributes, only users matching the
+  // filter will be returned. If return_fields is specified, only the
+  // specified details will be returned.
+  // 
+  // Users and techs may only retrieve details about themselves. PIs
+  // may retrieve details about themselves and others at their
+  // sites. Admins and nodes may retrieve details about all accounts.
+  
+  function GetPersons ($person_filter = NULL, $return_fields = NULL)
+  {
+    $args[] = $this->auth;
+    if (func_num_args() > 0) $args[] = $person_filter;
+    if (func_num_args() > 1) $args[] = $return_fields;
+    return $this->call('GetPersons', $args);
+  }
+  
+  // Returns 1 if the user or node authenticated successfully, faults
+  // otherwise.
+  
+  function AuthCheck ()
+  {
+    $args[] = $this->auth;
+    return $this->call('AuthCheck', $args);
+  }
+
+}
+
+?>
diff --git a/plekit/php/updateColumn.php b/plekit/php/updateColumn.php
new file mode 100644 (file)
index 0000000..a030cf9
--- /dev/null
@@ -0,0 +1,49 @@
+
+<?php
+
+// Require login
+require_once 'plc_login.php';
+
+// Get session and API handles
+require_once 'plc_session.php';
+global $plc, $api;
+
+//print header
+require_once 'plc_drupal.php';
+
+// Common functions
+require_once 'plc_functions.php';
+
+$slice_id=intval($_GET["slice_id"]);
+$tagN=$_GET["tagName"];
+
+$slices= $api->GetSlices( array($slice_id));
+
+if (empty($slices)) {
+  drupal_set_message ("Slice " . $slice_id . " not found");
+  return;
+ }
+
+$slice=$slices[0];
+
+$nodetags = array('node_id');
+$extratags = explode("|", $tagN);
+
+$nodes=$api->GetNodes(array('node_id'=>$slice['node_ids']),array_merge($nodetags, $extratags));
+$potential_nodes=$api->GetNodes(array('~node_id'=>$slice['node_ids']),array_merge($nodetags, $extratags));
+
+echo "---attached---";
+if ($nodes) foreach ($nodes as $node) {
+       echo "|".$node['node_id'];
+       foreach ($extratags as $t)
+               echo ":".$node[$t];
+}
+echo "|---potential---";
+if ($potential_nodes) foreach ($potential_nodes as $potential_node) {
+       echo "|".$potential_node['node_id'];
+       foreach ($extratags as $t)
+               echo ":".$potential_node[$t];
+}
+
+?> 
+
diff --git a/plekit/php/updateConf.php b/plekit/php/updateConf.php
new file mode 100644 (file)
index 0000000..4722688
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+
+// Require login
+require_once 'plc_login.php';
+
+// Get session and API handles
+require_once 'plc_session.php';
+global $plc, $api;
+
+//print header
+require_once 'plc_drupal.php';
+
+// Common functions
+require_once 'plc_functions.php';
+
+$value=$_GET["value"];
+$slice_id=intval($_GET["slice_id"]);
+$tagN=$_GET["tagName"];
+
+$fields= array( "$tagN"=>$value);
+$api->UpdateSlice( $slice_id , $fields );
+
+$myFile = "/var/log/myslice-log";
+$fh = fopen($myFile, 'a') or die("can't open file");
+$stringData = "\n".date('Ymd-H:i')."|".$slice_id.":".$value;
+fwrite($fh, $stringData);
+fclose($fh);
+
+?> 
diff --git a/plekit/table/columns.js b/plekit/table/columns.js
new file mode 100644 (file)
index 0000000..115fc7c
--- /dev/null
@@ -0,0 +1,1078 @@
+
+/* $Id: table.js 13009 2009-04-10 10:49:28Z baris $ */
+
+var filtered_color = "grey";
+var normal_color = "black";
+
+
+function inTypeA(header_name) {
+       var typeA = ['ST','SN','RES','OS','NRR','NTP','NSR','NSF','NDS','NTH','NEC','LRN','LCY','LPR','LCN','LAT','LON','IP','ASN','AST'];
+       return typeA.indexOf(header_name);
+}
+
+function inTypeB(header_name) {
+       var typeB = ['BW','DS','MS','CC','CR','AS','MU','DU','CN'];
+       return typeB.indexOf(header_name);
+}
+
+function inTypeC(header_name) {
+       var typeC = ['Rw','Rm','Ry','BWw','BWm','BWy','Lw','Lm','Ly','Sw','Sm','Sy','CFw','CFm','CFy','BUw','BUm','BUy','MUw','MUm','MUy','SSHw','SSHm','SSHy'];
+       return typeC.indexOf(header_name);
+}
+
+function inTypeD(header_name) {
+       var typeD = ['HC'];
+       return typeD.indexOf(header_name);
+}
+
+function debugfilter(s) {
+       document.getElementById('debug').innerHTML+=s;
+}
+
+function scrollList(tableid) {
+
+debugfilter("here");
+
+if (event.keyCode == 40)
+       debugfilter("down");
+else if (event.keyCode == 38)
+       debugfilter("up");
+
+}
+
+function highlightOption(divid) {
+
+//debugfilter("highlighting option "+divid);
+
+for (var kk in column_headers) {
+
+if (document.getElementById(kk))
+       document.getElementById(kk).className = 'out'; 
+}
+
+document.getElementById(divid).className = 'selected';
+
+showDescription(divid);
+
+}
+
+
+function showDescription(h) {
+
+       //debugfilter("showing description "+h);
+
+       if (document.getElementById('selectdescr'))
+       {
+               if (window['desc'+h])
+                       document.getElementById('selectdescr').innerHTML = ""+window['desc'+h];
+               else if (column_table[h] && column_table[h]['description'])
+                       document.getElementById('selectdescr').innerHTML = column_table[h]['description'];
+               else 
+                       document.getElementById('selectdescr').innerHTML = "No description provided";
+       }
+}
+
+
+function overrideTitles() {
+
+       //debugfilter("<p>overriding...");
+
+       for (var kk in column_headers) {
+
+       //debugfilter("here "+kk);
+
+       if (document.getElementById(kk) && window['title'+kk])
+               document.getElementById('htitle'+kk).innerHTML = window['title'+kk];
+       }
+
+}
+
+function changeCheckStatus(column) {
+
+
+if (document.getElementById('selectdescr'))
+{
+showDescription(document.getElementById(column).value);
+
+if (document.getElementById(column).checked)
+       addColumn(document.getElementById(column).value);
+else
+       deleteColumn(document.getElementById(column).value);
+}
+
+}
+
+function updatePeriod(h) {
+       deleteColumn2(h, h+'w');
+       deleteColumn2(h, h+'m');
+       deleteColumn2(h, h+'y');
+       addColumn(h);
+}
+
+function filterByType(selectedtype) {
+
+var notselectedyet = true;
+
+for (var kk in column_headers) {
+
+       if (document.getElementById(kk))
+       {
+               if (window['type'+kk] == selectedtype)
+               {
+                       document.getElementById(kk).className = 'in';
+                       if (notselectedyet)
+                               highlightOption(kk);
+                       notselectedyet = false;
+               }
+               else
+                       document.getElementById(kk).className = 'out';
+       }
+}
+}
+
+
+/*
+RESET/SAVE CONFIGURATION
+*/
+
+
+function resetColumns() {
+
+       for (var kk in column_table) {
+
+       if (column_table[kk]['visible'] == true && column_table[kk]['fetch'] == false)
+               deleteColumn(kk);
+       else if (column_table[kk]['visible'] == false && column_table[kk]['fetch'] == true)
+               addColumn(kk);
+       }
+
+}
+
+function resetCols(which_conf) {
+
+       var target_configuration = "|"+document.getElementById(which_conf).value+"|";
+       
+       //debugfilter("<p>Target configuration =  "+target_configuration);
+
+       for (var kk in column_table) {
+               //debugfilter("in "+kk+" ");
+
+               if (target_configuration.indexOf("|"+kk+"|")>=0)
+               {
+                       if (document.getElementById('check'+kk))
+                       if (document.getElementById('check'+kk).checked == false)
+                       {
+                               debugfilter("<p>Adding "+kk);
+                               addColumn(kk);
+                       }
+               }
+               else
+               {
+                       if (document.getElementById('check'+kk))
+                       if (document.getElementById('check'+kk).checked == true)
+                       {
+                               debugfilter("<p>Deleting "+kk);
+                               deleteColumn(kk);
+                       }
+               }
+       }
+}
+
+function saveConfiguration(which_conf)
+{
+       var slice_id = document.getElementById('slice_id').value;
+
+       var target_configuration = document.getElementById(which_conf).value;
+
+       debugfilter("saving configuration "+target_configuration);
+       updateColumnConfiguration(slice_id, target_configuration);
+}
+
+function updateColumnConfiguration(slice_id, value)
+{
+
+        if (window.XMLHttpRequest)
+          {// code for IE7+, Firefox, Chrome, Opera, Safari
+          xmlhttp=new XMLHttpRequest();
+          }
+        else
+          {// code for IE6, IE5
+          xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
+          }
+        xmlhttp.onreadystatechange=function()
+          {
+          if (xmlhttp.readyState==4) // && xmlhttp.status==200)
+            {
+                //value=xmlhttp.responseText;
+                //debugfilter(value);
+               if (document.getElementById('column_configuration'))
+               document.getElementById('column_configuration').value=value;
+            }
+          }
+        xmlhttp.open("GET","/plekit/php/updateConf.php?value="+value+"&slice_id="+slice_id+"&tagName=Configuration",true);
+
+        xmlhttp.send();
+}
+
+
+
+function resetConfiguration(which_conf)
+{
+       var slice_id = document.getElementById('slice_id').value;
+       var target_configuration = document.getElementById(which_conf).value;
+
+       debugfilter("reseting configuration "+target_configuration);
+
+        if (window.XMLHttpRequest)
+          {// code for IE7+, Firefox, Chrome, Opera, Safari
+          xmlhttp=new XMLHttpRequest();
+          }
+        else
+          {// code for IE6, IE5
+          xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
+          }
+        xmlhttp.onreadystatechange=function()
+          {
+          if (xmlhttp.readyState==4) // && xmlhttp.status==200)
+            {
+                //value=xmlhttp.responseText;
+                //debugfilter(value);
+                window.location.reload(true);
+            }
+          }
+        xmlhttp.open("GET","/plekit/php/updateConf.php?value="+target_configuration+"&slice_id="+slice_id+"&tagName=Configuration",true);
+
+        xmlhttp.send();
+}
+
+function addColumnToConfiguration(column) {
+
+
+       var old_configuration = document.getElementById('column_configuration').value;
+       var slice_id = document.getElementById('slice_id').value;
+
+       var new_configuration = "";
+
+       if (old_configuration != "")
+               new_configuration = old_configuration += "|"+column;
+       else
+               new_configuration = column;
+
+       //debugfilter("new configuration = "+new_configuration);
+
+       updateColumnConfiguration(slice_id, new_configuration);
+}
+
+
+
+/*
+ADD/REMOVE COLUMNS
+
+*/
+
+
+function getHTTPObject()
+{
+        if (typeof XMLHttpRequest != 'undefined')
+        { return new XMLHttpRequest(); }
+
+        try
+        { return new ActiveXObject("Msxml2.XMLHTTP"); }
+        catch (e)
+        {
+                try { return new ActiveXObject("Microsoft.XMLHTTP"); }
+                catch (e) {}
+        }
+        return false;
+}
+
+
+function load_html(column, url) {
+       var req = getHTTPObject();
+       var res;
+       req.open('GET', url, true);
+       req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
+       req.onreadystatechange =
+        function() {
+                if (req.readyState == 4)
+                { updateColumnData(column, req.responseText); }
+        }
+       req.send(null);
+}
+
+function updateColumnData(column,data) {
+
+
+var headers = column.split("|");
+var data_table = data.split("|"); 
+
+//debugfilter("<p>headers[0] = "+headers[0]);
+//debugfilter("<p>sample line = "+data_table[2]);
+
+
+  var node_data;
+
+  var table_id1 = 'nodes';
+  var table=$(table_id1);
+  var css='#'+table_id1+'>tbody';
+  var rows = $$(css)[0].rows;
+
+  var data_array1 = new Array();
+
+  //debugfilter("COLUMN "+column+"<p>");
+
+  for (var node_index = 1; node_index < data_table.length; node_index++) {
+       if (data_table[node_index] == '---potential---')        
+               break;
+       node_data = data_table[node_index].split(':');
+
+       data_array1[node_data[0]] = new Array();
+
+       for (var h_index=0; h_index < headers.length; h_index++) {
+               
+               if (node_data[h_index+1] == "")
+                       data_array1[node_data[0]][h_index] = "n/a";
+               else
+                       data_array1[node_data[0]][h_index] = node_data[h_index+1];
+       }
+
+  }
+       
+  if (rows)
+  for (var row_index = 0; row_index < rows.length ; row_index++) {
+    var tr=rows[row_index];
+
+    for (var column_index=0; column_index < tr.cells.length; column_index++) {
+               //debugfilter("<p>node id = "+tr.cells[0].innerHTML);
+               var found_index = headers.indexOf(tr.cells[column_index].getAttribute('name'));
+               if (found_index != -1)
+                       tr.cells[column_index].innerHTML = data_array1[tr.cells[0].innerHTML][found_index];
+    }
+  }
+
+//potential nodes
+if (data_table[node_index] == '---potential---')       
+{
+
+  var table_id2 = 'add_nodes';
+  var table2=$(table_id2);
+  var css2='#'+table_id2+'>tbody';
+  var rows2 = $$(css2)[0].rows;
+
+  var data_array2 = new Array();
+
+  //debugfilter("COLUMN "+column+"<p>");
+
+  for (; node_index < data_table.length; node_index++) {
+       if (data_table[node_index] == '')       
+               continue;
+       node_data = data_table[node_index].split(':');
+
+       data_array2[node_data[0]] = new Array();
+
+       for (var h_index=0; h_index < headers.length; h_index++) {
+               
+               if (node_data[h_index+1] == "")
+                       data_array2[node_data[0]][h_index] = "n/a";
+               else
+                       data_array2[node_data[0]][h_index] = node_data[h_index+1];
+       }
+  }
+       
+  if (rows)
+  for (var row_index = 0; row_index < rows2.length ; row_index++) {
+    var tr=rows2[row_index];
+
+    for (var column_index=0; column_index < tr.cells.length; column_index++) {
+               var found_index = headers.indexOf(tr.cells[column_index].getAttribute('name'));
+               if (found_index != -1)
+                       tr.cells[column_index].innerHTML = data_array2[tr.cells[0].innerHTML][found_index];
+    }
+  }
+
+}
+
+  document.getElementById('loadingDiv').innerHTML = ""
+}
+
+
+
+
+
+function addColumnCells(column,header) {
+
+       //debugfilter("adding cells now: "+column+":"+header);
+       column_table[header]['visible']=true;
+
+       var cells = document.getElementsByName(header);
+
+
+       //debugfilter("got cells -"+cells+"- for "+header);
+       for(var j = 0; j < cells.length; j++) 
+               cells[j].style.display = "table-cell";
+}
+
+function addColumnSamples(column) {
+
+       var cellsheader = document.getElementsByName("confheader"+column);
+       for(var j = 0; j < cellsheader.length; j++) 
+               cellsheader[j].style.display = "table-cell";
+
+}
+
+function addColumnAjax(column) {
+
+       var selectedperiod="";
+
+       if (document.getElementById('selectperiod'+column))
+               selectedperiod = document.getElementById('selectperiod'+column).value;
+
+       var header = column+""+selectedperiod;
+
+       addColumnCells(column, header);
+
+       var t = column_table[header]['tagname'];
+       var slice_id = document.getElementById('slice_id').value;
+       document.getElementById('loadingDiv').innerHTML = "<img src=/plekit/icons/ajax-loader.gif>LOADING ...";
+       var url = "/plekit/php/updateColumn.php?slice_id="+slice_id+"&tagName="+t;
+       load_html(header, url);
+
+       addColumnToConfiguration(header);
+}
+
+
+function addColumn2(column) {
+
+       var selectedperiod="";
+
+       if (document.getElementById('selectperiod'+column))
+               selectedperiod = document.getElementById('selectperiod'+column).value;
+
+       var header = column+""+selectedperiod;
+
+       addColumnCells(column,header);
+
+       addColumnToConfiguration(column);
+
+       checkDataToFetch();
+}
+
+function addColumn(column) {
+
+       var selectedperiod="";
+       var header=column;
+
+       //debugfilter("adding column "+column+" and header "+header);
+
+       if (inTypeC(column)!=-1)
+       {
+               column = column.substring(0,column.length-1);
+       }
+       else if (document.getElementById('selectperiod'+column))
+       {
+               selectedperiod = document.getElementById('selectperiod'+column).value;
+               header = column+""+selectedperiod;
+       }
+
+       //debugfilter("adding "+column+","+header);
+
+       addColumnCells(column, header);
+
+       addColumnToConfiguration(header);
+
+       column_table[header]['visible'] = true;
+       
+       checkDataToFetch();
+}
+
+function checkDataToFetch() {
+
+var dataExist = false;
+
+for (var kk in column_table) {
+
+       if (document.getElementById(kk))
+       {
+               if (column_table[kk]['visible'] == true && column_table[kk]['fetch'] == false)
+               {
+                       document.getElementById('fetchbutton').disabled = false;
+                       document.getElementById('fetchbutton').style.color = 'red';
+                       dataExist = true;
+               }
+       }
+}
+
+if (!dataExist)
+{
+       document.getElementById('fetchbutton').disabled = true;
+       document.getElementById('fetchbutton').style.color = 'grey';
+}
+
+}
+
+
+function fetchData() {
+
+var tagnames = "";
+var headers = "";
+
+for (var kk in column_table) {
+
+if (column_table[kk]['visible'] == true && column_table[kk]['fetch'] == false)
+       if (tagnames == "")
+       {       
+               tagnames = column_table[kk]['tagname'];
+               headers = kk;
+       }
+       else
+       {
+               tagnames += "|"+column_table[kk]['tagname'];
+               headers += "|"+kk;
+       }
+}
+
+//debugfilter("fetching these columns: "+tagnames+ "("+headers+")");
+
+       var slice_id = document.getElementById('slice_id').value;
+       document.getElementById('loadingDiv').innerHTML = "&nbsp;&nbsp;&nbsp;<img src=/plekit/icons/ajax-loader.gif>&nbsp;Loading data. Please wait ...";
+       var url = "/plekit/php/updateColumn.php?slice_id="+slice_id+"&tagName="+tagnames;
+       load_html(headers, url);
+}
+
+function deleteColumnCells(column, header) {
+
+       column_table[header]['visible']=false;
+
+       var cells = document.getElementsByName(header);
+       for(var j = 0; j < cells.length; j++) 
+               cells[j].style.display = "none";
+
+}
+
+function deleteColumnSample() {
+       var cellsheader = document.getElementsByName("confheader"+column);
+       for(var j = 0; j < cellsheader.length; j++) 
+               cellsheader[j].style.display = "none";
+
+}
+
+function deleteColumnFromConfiguration(column) {
+
+
+       var old_configuration = document.getElementById('column_configuration').value;
+       var slice_id = document.getElementById('slice_id').value;
+
+       var old_columns = old_configuration.split("|");
+       var new_columns = new Array();
+
+       for (var column_index = 0; column_index < old_columns.length ; column_index++) {
+               var conf = old_columns[column_index].split(':');
+               if (conf[0] != column)
+                       new_columns.push(old_columns[column_index]);
+       }
+
+       var new_configuration = new_columns.join("|");
+       updateColumnConfiguration(slice_id, new_configuration);
+
+       checkDataToFetch();
+}
+
+function deleteColumn2(column, header) {
+
+       deleteColumnCells(column,header);
+
+       deleteColumnFromConfiguration(column);
+
+       column_table[header]['visible'] = false;
+       document.getElementById('check'+column).checked = false;
+}
+
+function deleteColumn(column) {
+
+       var selectedperiod="";
+       var header=column;
+
+       if (inTypeC(column)!=-1)
+       {
+               column = column.substring(0,column.length-1);
+       }
+       else if (document.getElementById('selectperiod'+column))
+       {
+               selectedperiod = document.getElementById('selectperiod'+column).value;
+               header = column+""+selectedperiod;
+       }
+
+       //debugfilter("deleting "+column+","+header);
+
+       deleteColumnCells(column,header);
+
+       deleteColumnFromConfiguration(header);
+
+       column_table[header]['visible'] = false;
+
+       //document.getElementById('check'+column).checked = false;
+}
+
+
+
+
+/*
+
+HIGHLIGHTING
+
+
+
+function updateColumnThreshold(column, minT, maxT) {
+
+debugfilter("updating threshold for "+column+" with "+minT+" and "+maxT);
+
+var cells = document.getElementsByName(column);
+
+for(var j = 0; j < cells.length; j++) 
+{
+var val = parseFloat(cells[j].innerHTML);
+
+if (val >= minT && val <= maxT)
+       cells[j].style.color = filtered_color;
+else
+       cells[j].style.color = normal_color;
+}
+
+var old_configuration = document.getElementById('column_configuration').value;
+var slice_id = document.getElementById('slice_id').value;
+
+var old_columns = old_configuration.split("|");
+var new_columns = new Array();
+
+for (var column_index = 0; column_index < old_columns.length ; column_index++) {
+       var conf = old_columns[column_index].split(':');
+       if (conf[0] != column)
+               new_columns.push(old_columns[column_index]);
+       else
+               new_columns.push(column+":"+minT+","+maxT);
+}
+
+var new_configuration = new_columns.join("|");
+
+updateColumnConfiguration(slice_id, new_configuration);
+
+}
+
+function updateExcludeList(column, excludeList) {
+
+//debugfilter("updating list");
+debugfilter("updating list for "+column+" with "+excludeList);
+
+var cells = document.getElementsByName(column);
+
+for(var j = 1; j < cells.length; j++) 
+{
+var val = cells[j].innerHTML;
+
+if (excludeList == val)
+       cells[j].style.color = filtered_color;
+else
+       cells[j].style.color = normal_color;
+}
+
+var old_configuration = document.getElementById('column_configuration').value;
+var slice_id = document.getElementById('slice_id').value;
+
+var old_columns = old_configuration.split("|");
+var new_columns = new Array();
+
+for (var column_index = 0; column_index < old_columns.length ; column_index++) {
+       var conf = old_columns[column_index].split(':');
+       if (conf[0] != column)
+               new_columns.push(old_columns[column_index]);
+       else
+               new_columns.push(column+":"+excludeList);
+}
+
+var new_configuration = new_columns.join("|");
+
+updateColumnConfiguration(slice_id, new_configuration);
+
+}
+
+
+
+/*
+ROW FILTERING
+
+
+function plekit_table_showAll (slicetable_id) {
+
+  var table=$(slicetable_id);
+  var css='#'+slicetable_id+'>tbody';
+  var rows = $$(css)[0].rows;
+
+  // scan rows, elaborate 'visible'
+  for (var row_index = 0; row_index < rows.length ; row_index++) {
+    var tr=rows[row_index];
+    var visible=true;
+    plekit_table_row_visible(tr,visible);
+  }
+
+    plekit_table_count_filtered(slicetable_id);
+
+  tablePaginater.init(slicetable_id);
+  
+}
+
+function plekit_table_count_filtered (slicetable_id) {
+  var table=$(slicetable_id);
+  var css='#'+slicetable_id+'>tbody';
+  var rows = $$(css)[0].rows;
+
+  var no_filtered=0;
+
+  // scan rows, elaborate 'visible'
+  for (var row_index = 0; row_index < rows.length ; row_index++) {
+    var tr=rows[row_index];
+    var filtered = false;
+
+    for (var column_index=0; column_index < tr.cells.length; column_index++) 
+               if (tr.cells[column_index].style.color == "red")
+                       filtered = true;
+
+       if (filtered)
+       no_filtered++;
+
+  }
+
+  debugfilter(no_filtered+' nodes do not satisfy the requested threshold');
+}
+
+
+function plekit_table_hide_filtered (slicetable_id) {
+  var table=$(slicetable_id);
+  var css='#'+slicetable_id+'>tbody';
+  var rows = $$(css)[0].rows;
+
+  var reg = /(^|\s)invisibleRow(\s|$)/;
+
+
+  if (!document.getElementById('filtercheck').checked)
+  {
+       plekit_table_showAll(slicetable_id);
+       return;
+  }
+
+  var hidden=0;
+
+  // scan rows, elaborate 'visible'
+  for (var row_index = 0; row_index < rows.length ; row_index++) {
+    var tr=rows[row_index];
+    var visible=true;
+
+    for (var column_index=0; column_index < tr.cells.length; column_index++) {
+               if (tr.cells[column_index].style.color == filtered_color)
+                       visible = false;
+    }
+    if (!visible)
+       hidden++;
+
+    plekit_table_row_visible(tr,visible);
+  }
+
+  //debugfilter('hidden '+hidden+' nodes');
+  debugfilter(hidden+' nodes do not satisfy the requested threshold (hidden)');
+  
+  tablePaginater.init(slicetable_id);
+}
+
+
+
+
+function plekit_table_apply_config(slicetable_id, configuration) {
+
+var new_configuration = document.getElementById('new_conf').value;
+var all_columns = new_configuration.split("|");
+
+var min_values = new Array();
+var max_values = new Array();
+
+
+for (var column_index = 0; column_index < all_columns.length ; column_index++) {
+
+       var conf = all_columns[column_index].split(':');
+       
+       if (inTypeB(conf[0]) != -1)
+       {
+               var threshold = conf[1].split(',');
+               if (threshold.length == 2)
+               {
+               min_values.push(parseFloat(threshold[0]));
+               max_values.push(parseFloat(threshold[1]));
+               }
+       }
+       else if (inTypeC(conf[0]) == -1)
+       {
+               var threshold = conf[2].split(',');
+               if (threshold.length == 2)
+               {
+               min_values.push(parseInt(threshold[0]));
+               max_values.push(parseInt(threshold[1]));
+               }
+       }
+       else
+       {
+               min_values.push(-1);
+               max_values.push(-1);
+       }
+       
+}
+
+  var table=$(slicetable_id);
+  var css='#'+slicetable_id+'>tbody';
+  var rows = $$(css)[0].rows;
+
+
+  var no_filtered=0;
+
+  for (var row_index = 0; row_index < rows.length ; row_index++) {
+
+       var tr=rows[row_index];
+
+       var filtered = false;
+
+       for (var column_index = 0; column_index < all_columns.length ; column_index++) 
+       if (min_values[column_index]!=-1)
+       {
+               var val = parseFloat(tr.cells[3+column_index].innerHTML);
+               
+               if (val >= min_values[column_index] && val <= max_values[column_index])
+               {
+                       tr.cells[3+column_index].style.color = filtered_color;
+                       filtered = true;
+               }
+               else
+                       tr.cells[3+column_index].style.color = normal_color;
+       }
+       else
+               if (tr.cells[3+column_index].style.color == filtered_color)
+                       filtered = true;
+               
+
+       if (filtered)
+       no_filtered++;
+  }
+
+  debugfilter(no_filtered+' nodes do not satisfy the requested threshold');
+
+  //tablePaginater.init(slicetable_id);
+
+}
+
+
+function reset_select () {
+  var table=$(slicetable_id);
+  var css='#'+slicetable_id+'>tbody';
+  var rows = $$(css)[0].rows;
+
+var action = document.getElementById('onlyselected');
+action.checked=false;
+
+  // scan rows, elaborate 'visible'
+  for (var row_index = 0; row_index < rows.length ; row_index++) {
+    var tr=rows[row_index];
+
+    document.getElementById("check"+tr.id).checked=false;
+
+  }
+
+  plekit_table_count_nodes();
+}
+
+}
+
+
+function plekit_table_select_filter () {
+  var table=$(slicetable_id);
+  var css='#'+slicetable_id+'>tbody';
+  var rows = $$(css)[0].rows;
+
+  var reg = /(^|\s)invisibleRow(\s|$)/;
+
+  var action = document.getElementById('onlyselected');
+  if (!action.checked)
+       plekit_table_reset_filter();
+
+  // scan rows, elaborate 'visible'
+  for (var row_index = 0; row_index < rows.length ; row_index++) {
+    var tr=rows[row_index];
+    var visible=true;
+
+    if (action.checked)
+    {
+         if(tr.className.search(reg) == -1) 
+              if(!document.getElementById("check"+tr.id).checked)
+                 visible=false;
+    }
+
+    if(tr.className.search(reg) != -1) 
+       visible=false;
+
+    plekit_table_row_visible(tr,visible);
+  }
+  
+  tablePaginater.init(slicetable_id);
+  plekit_table_count_nodes();
+}
+
+function plekit_table_select_filter2 () {
+  var table=$(slicetable_id);
+  var css='#'+slicetable_id+'>tbody';
+  var rows = $$(css)[0].rows;
+
+  var reg = /(^|\s)invisibleRow(\s|$)/;
+
+  var action = document.getElementById('onlyselected');
+
+  // scan rows, elaborate 'visible'
+  for (var row_index = 0; row_index < rows.length ; row_index++) {
+    var tr=rows[row_index];
+    var visible=true;
+
+    if (action.checked)
+    {
+         if(tr.className.search(reg) == -1) 
+              if(!document.getElementById("check"+tr.id).checked)
+                 visible=false;
+    }
+
+    if(tr.className.search(reg) != -1) 
+       visible=false;
+
+    plekit_table_row_visible(tr,visible);
+  }
+  
+  tablePaginater.init(slicetable_id);
+  plekit_table_count_nodes();
+}
+
+function CheckTopNodes(n) {
+  var table=$(slicetable_id);
+  var css='#'+slicetable_id+'>tbody';
+  var rows = $$(css)[0].rows;
+  var reg = /(^|\s)invisibleRow(\s|$)/;
+
+  var checked=0;
+
+  for (var row_index = 0; row_index < rows.length ; row_index++) {
+    var tr=rows[row_index];
+
+    if(tr.className.search(reg) == -1) {
+       if (checked<n)
+       {
+               document.getElementById("check"+tr.id).checked=true;
+               checked++;
+       }
+       else
+       {
+               document.getElementById("check"+tr.id).checked=false;
+       };
+    };
+   };
+};
+
+
+function CheckRandomNodes(n) {
+  var table=$(slicetable_id);
+  var css='#'+slicetable_id+'>tbody';
+  var rows = $$(css)[0].rows;
+  var reg = /(^|\s)invisibleRow(\s|$)/;
+
+  var r = n/plekit_table_visible_count();
+  var checked=0;
+
+  for (var row_index = 0; row_index < rows.length ; row_index++) {
+    var tr=rows[row_index];
+
+    if(tr.className.search(reg) == -1) {
+       if(Math.random() < r) {
+               document.getElementById("check"+tr.id).checked=true;
+               checked++;
+       };
+       if (checked>=n)
+               break;
+     };
+  };
+};
+
+
+function plekit_table_visible_count() {
+  var table=$(slicetable_id);
+  var css='#'+slicetable_id+'>tbody';
+  var rows = $$(css)[0].rows;
+  var reg = /(^|\s)invisibleRow(\s|$)/;
+  var v=0;
+
+  for (var row_index = 0; row_index < rows.length ; row_index++) {
+    var tr=rows[row_index];
+
+       if(tr.className.search(reg) == -1) 
+       {
+               v++;
+       }
+  }
+
+  return v;
+}
+
+
+function plekit_table_count_nodes() {
+  var table=$(slicetable_id);
+  var css='#'+slicetable_id+'>tbody';
+  var rows = $$(css)[0].rows;
+  var reg = /(^|\s)invisibleRow(\s|$)/;
+  var n=0;
+  var v=0;
+  var s=0;
+
+  for (var row_index = 0; row_index < rows.length ; row_index++) {
+    var tr=rows[row_index];
+
+       n++;
+       var ch = document.getElementById("check"+tr.id);
+
+       if(tr.className.search(reg) == -1) 
+               v++;
+       else
+       {
+               if (ch.checked)
+                       ch.checked=false;
+       };
+
+       if (ch.checked)
+               s++;
+       
+   };
+
+   var dd = document.getElementById('node_statistics');
+   dd.innerHTML = "Total: "+n+" - Shown: "+v+" - Selected: "+s;
+};
+
+function AutoSelect()
+{
+  var a = document.getElementById('automatic').value;
+  var n = parseInt(document.getElementById('no_nodes').value);
+
+  if (isNaN(n))
+       return;
+
+  if (a == "random")
+         CheckRandomNodes(n);
+  else if (a == "top")
+         CheckTopNodes(n);
+
+  plekit_table_select_filter2();
+  plekit_table_count_nodes();
+}
+
+*/