(*) slices list has links towards the slice page with the details area closed and...
authorThierry Parmentelat <thierry.parmentelat@sophia.inria.fr>
Fri, 13 Nov 2009 10:52:31 +0000 (10:52 +0000)
committerThierry Parmentelat <thierry.parmentelat@sophia.inria.fr>
Fri, 13 Nov 2009 10:52:31 +0000 (10:52 +0000)
(*) slice page reviewed for displaying node status and optional node tags
tags displayed are the ones for which the category matches 'node*/ui*'
the category may also convey tunings for rendering the column
(*) cosmetics in the slice page renewal area
(*) nodegroups : UI can now add/delete/update nodegroups
managing the contents of a nodegroup still can be tedious though
(*) tag page fixed, slice/sliver count was wrong, was not showing nodegroups

planetlab/common/actions.php
planetlab/css/my_slice.css
planetlab/includes/plc_functions.php
planetlab/slices/slice.php
planetlab/slices/slices.php
planetlab/tags/nodegroup.php
planetlab/tags/nodegroups.php
planetlab/tags/tag.php
planetlab/tags/tags.php

index a2dfd11..2717d1e 100644 (file)
@@ -108,6 +108,14 @@ $known_actions []= "delete-node-tags";
 $known_actions []= "delete-interface-tags";
 //     expects:        interface_id & interface_tag_ids
 
+//////////////////////////////////////// nodegroups
+$known_actions []= "update-nodegroup";
+//     expects nodegroup_id groupname value
+$known_actions []= "add-nodegroup";
+//     expects groupname, tag_type_id, value
+$known_actions []= 'delete-nodegroups';
+//     expects nodegroup_ids
+
 ////////////////////////////////////////////////////////////
 $interface_details= array ('method','type', 'ip', 'gateway', 'network', 
                           'broadcast', 'netmask', 'dns1', 'dns2', 
@@ -717,6 +725,7 @@ Our support team will be glad to answer any question that you might have.
    else 
      drupal_set_error ("Could not update tag type $tag_type_id\n".$api->error());
    plc_redirect(l_tag($tag_type_id));
+   break;
  }
 
  case 'add-tag-type': {
@@ -740,6 +749,7 @@ Our support team will be glad to answer any question that you might have.
    else
      drupal_set_error ("Could not create tag type $tagname");
    plc_redirect( l_tags());
+   break;
  }
 
  case 'delete-tag-types': {
@@ -853,6 +863,68 @@ Our support team will be glad to answer any question that you might have.
      plc_redirect(l_interface($_POST['interface_id']));
  }
 
+//////////////////////////////////////// nodegroups
+ case 'update-nodegroup': {
+   $nodegroup_id = $_POST['nodegroup_id'];
+   $groupname = $_POST['groupname'];
+   $value = $_POST['value'];
+
+   $fields=array();
+   $fields['groupname']=$groupname;
+   $fields['value']=$value;
+   if ( $api->UpdateNodeGroup($nodegroup_id,$fields) == 1) 
+     drupal_set_message ('Nodegroup updated');
+   else 
+     drupal_set_error ("Could not update nodegroup $groupname");
+   
+   plc_redirect(l_nodegroup($nodegroup_id));
+
+ }
+
+ case 'add-nodegroup': {
+   $groupname=$_POST['groupname'];
+   if ( ! $groupname ) {
+     drupal_set_error ('Empty groupname');
+     plc_redirect (l_nodegroups());
+   }
+   $tag_type_id=intval($_POST['tag_type_id']);
+   if ( ! $tag_type_id ) {
+     drupal_set_error ('You must select a tag in the dropdown list');
+     plc_redirect (l_nodegroups());
+   }
+   $value=$_POST['value'];
+   if ( ! $value ) {
+     drupal_set_message ("Empty value.. let's see ..");
+   }
+   if ( $api->AddNodeGroup ($groupname,$tag_type_id,$value) > 0) 
+     drupal_set_message ("Nodegroup $groupname created");
+   else
+     drupal_set_error ("Could not create nodegroup $groupname");
+
+   plc_redirect (l_nodegroups());
+   break;
+ }
+
+ case 'delete-nodegroups': {
+   $nodegroup_ids=$_POST['nodegroup_ids'];
+   if ( ! $nodegroup_ids ) {
+     drupal_set_message("action=delete-nodegroups - No group selected");
+     plc_redirect(l_nodegroups());
+   }
+   $success=true;
+   $counter=0;
+   foreach ($nodegroup_ids as $nodegroup_id) 
+     if ($api->DeleteNodeGroup(intval($nodegroup_id)) != 1) 
+       $success=false;
+     else
+       $counter++;
+   if ($success) 
+     drupal_set_message ("Deleted $counter group(s)");
+   else
+     drupal_set_error ("Could not delete all selected groups, only $counter were removed");
+   plc_redirect (l_nodegroups());
+   break;
+ }
 
 ////////////////////////////////////////
 
index b41ee06..fa07f33 100644 (file)
@@ -9,10 +9,19 @@ div.my-slice-renewal {
 
 /* internal toggles */
 div#toggle-container-my-slice-persons-current,
+div#toggle-container-my-slice-nodes-current {
+    background: #f0f0f0;
+}
+
 div#toggle-container-my-slice-persons-add,
-div#toggle-container-my-slice-nodes-current,
 div#toggle-container-my-slice-nodes-add {
-/*    background:#fcf7ff; */
-    background: #ccffc7; 
+    background: #f0e0e0;
 }
 
+/* qualify enough so that this wins over the even/odd row patterns in tables */
+tbody>tr>td.node-ok {
+    background: #a0f0e6;
+}
+tbody>tr>td.node-ko {
+    background: #ffa7a8;
+}
index 4f10e8c..53600ae 100644 (file)
@@ -105,6 +105,7 @@ function l_site_obj($site)          { return href (l_site($site['site_id']),$site['name'
 function l_slices ()                   { return "/db/slices/index.php"; }
 function l_slices_peer ($peer_id)      { return "/db/slices/index.php?peerscope=$peer_id"; }
 function l_slice ($slice_id)           { return "/db/slices/index.php?id=$slice_id"; }
+function l_slice_nodes ($slice_id)     { return "/db/slices/index.php?id=$slice_id&show_details=0&show_nodes=1&show_nodes_current=1&show_nodes_add=1"; }
 function l_slice_t ($slice_id,$text)   { return href (l_slice($slice_id),$text); }
 function l_slice_add ()                        { return "/db/slices/slice_add.php"; }
 function l_slices_site($site_id)       { return "/db/slices/index.php?site_id=$site_id"; }
@@ -134,6 +135,8 @@ function l_nodegroups ()            { return "/db/tags/nodegroups.php"; }
 function l_nodegroup ($nodegroup_id)   { return "/db/tags/nodegroup.php?id=$nodegroup_id"; }
 function l_nodegroup_t ($nodegroup_id,$text) { 
                                          return href(l_nodegroup($nodegroup_id),$text); }
+function l_nodegroup_obj ($nodegroup) { 
+                                         return href(l_nodegroup($nodegroup['nodegroup_id']),$nodegroup['groupname']); }
 
 function l_events ()                   { return "/db/events/index.php"; }
 function l_event ($type,$param,$id)    { return "/db/events/index.php?type=$type&$param=$id"; }
@@ -360,12 +363,15 @@ function truncate ($text,$numb,$etc = "...") {
   }
   return $text;
 }
-function html_div ($text,$class="") {
-  $html="<div";
+// generates <(atom) class=(class)> (text) </(atom)>
+function html_atom ($atom,$text,$class="") {
+  $html="<$atom";
   if ($class) $html .= " class='$class'";
-  $html .= ">$text</div>";
+  $html .= ">$text</$atom>";
   return $html;
 }
+function html_div ($text,$class="") { return html_atom ('div',$text,$class); }
+function html_span ($text,$class="") { return html_atom ('span',$text,$class); }
 
 // should use the same channel as the php errors..
 function plc_error_html ($text)                { return  html_div ($text,'plc-error'); }
@@ -391,10 +397,10 @@ function errors_display ($errors) {
   }
 }
 
-function plc_warning_html ($text)      { return "<span class='plc-warning'>" . $text . "</span>";}
+function plc_warning_html ($text)      { return html_span($text,'plc-warning'); }
 function plc_warning ($text)           { print plc_warning_html("Warning " . $text); }
 
-function bold_html ($text)             { return "<span class='bold'>$text</span>"; }
+function bold_html ($text)             { return html_span($text,'bold'); }
 
 // shows a php variable verbatim with a heading message
 function plc_debug ($message,$object) {
index 45afc94..9649136 100644 (file)
@@ -96,12 +96,14 @@ 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',"Renew this slice",
+    new PlekitToggle('renew',"Expires $current_text - Renew this slice",
                     array("bubble"=>
                           "Enter this zone if you wish to renew your slice",
                           'visible'=>$visible));
@@ -142,8 +144,9 @@ EOF;
 
     if ( empty( $selectors ) ) {
       print <<< EOF
-<div class='plc-warning renewal'>
-Slice cannot be renewed any further into the future, try again closer to expiration date.
+<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 {
@@ -373,9 +376,47 @@ if ($privileges) {
 }
 $toggle->end();
 
-//////////////////// nodes
+//////////////////////////////////////////////////////////// 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_columns = array('hostname','node_id','arch','peer_id','slice_ids_whitelist');
+$node_fixed_columns = array('hostname','node_id','peer_id','slice_ids_whitelist','run_level','boot_state');
+// scan tag types to find relevant additional columns
+$nodeui_tag_types = $api->GetTagTypes(array('category'=>'node*/ui*'));
+// extract tagname
+$node_tag_columns = array_map(create_function('$tt','return $tt["tagname"];'),$nodeui_tag_types);
+// build an ordered list of chunks {'tagname','header','rank','description'}
+$more_tags = array();
+foreach ($nodeui_tag_types as $tag_type) {
+  $tagname=$tag_type['tagname'];
+  $chunk=array();
+  $chunk['tagname']=$tagname;
+  $chunk['header']=$tagname;
+  $chunk['rank']=$tagname;
+  $chunk['type']='string';
+  $chunk['description']=$tag_type['description'];
+  $category_tokens=split('/',$tag_type['category']);
+  foreach ($category_tokens as $token) {
+    $assign=split('=',$token);
+    if (count($assign)==2) 
+      $chunk[$assign[0]]=$assign[1];
+  }
+  $more_tags []= $chunk;
+}
+
+function sort_chunks ($ch1, $ch2) { return strcmp($ch1['rank'],$ch2['rank']); }
+usort ($more_tags, sort_chunks);
+
+//plc_debug('ordered additional tags',$more_tags);
+
+$node_columns=array_merge($node_fixed_columns,$node_tag_columns);
 $nodes=$api->GetNodes(array('node_id'=>$slice['node_ids']),$node_columns);
 $potential_nodes=$api->GetNodes(array('~node_id'=>$slice['node_ids']),$node_columns);
 $count=count($nodes);
@@ -394,15 +435,22 @@ $toggle_nodes=new PlekitToggle('my-slice-nodes-current',
 $toggle_nodes->start();
 
 $headers=array();
+$notes=array();
 $headers['peer']='string';
 $headers['hostname']='string';
-$headers['arch']='string';
+$headers['S']='string';
+$notes[]='S = last known status';
+foreach ($more_tags as $chunk) {
+  $header=$chunk['header'];
+  $headers[$header]=$chunk['type'];
+  if ($header != $chunk['tagname']) $notes []= $header . ' = ' . $chunk['description'];
+}
 if ($privileges) $headers[plc_delete_icon()]="none";
 
-$table_options = array('notes_area'=>false,
+$table_options = array('notes'=>$notes,
                        'search_width'=>15,
                        'pagesize'=>20);
-$table=new PlekitTable('nodes',$headers,'0',$table_options);
+$table=new PlekitTable('nodes',$headers,'1',$table_options);
 
 $form=new PlekitForm(l_actions(),array('slice_id'=>$slice['slice_id']));
 $form->start();
@@ -411,7 +459,12 @@ if ($nodes) foreach ($nodes as $node) {
   $table->row_start();
   $peers->cell($table,$node['peer_id']);
   $table->cell(l_node_obj($node));
-  $table->cell($node['arch']);
+  $run_level=$node['run_level'];
+  if ( empty($run_level)) $run_level=$node['boot_state'];
+  $class=($run_level == 'boot') ? 'node-ok' : 'node-ko';
+  $table->cell($run_level,array('class'=>$class));
+  foreach ($more_tags as $chunk) $table->cell($node[$chunk['tagname']]);
+
   if ($privileges) $table->cell ($form->checkbox_html('node_ids[]',$node['node_id']));
   $table->row_end();
 }
@@ -443,7 +496,7 @@ if ($privileges) {
   $count=count($potential_nodes);
   $toggle_nodes=new PlekitToggle('my-slice-nodes-add',
                                 "$count more nodes available",
-                                array('visible'=>get_arg('show_persons_add',false)));
+                                array('visible'=>get_arg('show_nodes_add',false)));
   $toggle_nodes->start();
 
   if ( ! $potential_nodes ) {
@@ -451,9 +504,16 @@ if ($privileges) {
     echo "<p class='not-relevant'>No node to add</p>";
   } else {
     $headers=array();
+    $notes=array();
     $headers['peer']='string';
     $headers['hostname']='string';
-    $headers['arch']='string';
+    $headers['S']='string';
+    $notes[]='S = last known status';
+    foreach ($more_tags as $chunk) {
+      $header=$chunk['header'];
+      $headers[$header]=$chunk['type'];
+      if ($header != $chunk['tagname']) $notes []= $header . ' = ' . $chunk['description'];
+    }
     $headers['+']="none";
     
     $table=new PlekitTable('add_nodes',$headers,'1', $table_options);
@@ -465,7 +525,11 @@ if ($privileges) {
        $table->row_start();
        $peers->cell($table,$node['peer_id']);
        $table->cell(l_node_obj($node));
-       $table->cell($node['arch']);
+       $run_level=$node['run_level'];
+       if ( empty($run_level)) $run_level=$node['boot_state'];
+       $class=($run_level == 'boot') ? 'node-ok' : 'node-ko';
+       $table->cell($run_level,array('class'=>$class));
+       foreach ($more_tags as $chunk) $table->cell($node[$chunk['tagname']]);
        $table->cell ($form->checkbox_html('node_ids[]',$node['node_id']));
        $table->row_end();
       }
index bc5ecbb..63245e9 100644 (file)
@@ -141,7 +141,7 @@ foreach ($slices as $slice) {
   $table->row_start();
   if (plc_is_admin()) $table->cell (l_slice_t($slice_id,$slice_id));
   $peers->cell($table,$peer_id);
-  $table->cell (l_slice_obj($slice));
+  $table->cell (href(l_slice_nodes($slice_id),$slice['name']));
   $table->cell ($users);
   $table->cell(href(l_persons_slice($slice_id),count($slice['person_ids'])));
   $table->cell (href(l_nodes_slice($slice_id),count($slice['node_ids'])));
index cb97270..b29545a 100644 (file)
@@ -52,14 +52,22 @@ $tabs []= tab_nodes_local();
 drupal_set_title("Details for node group " . $nodegroup['groupname']);
 plekit_linetabs($tabs);
 
-$details=new PlekitDetails(false);
+$toggle=new PlekitToggle('details','Details');
+$toggle->start();
+$details=new PlekitDetails(plc_is_admin());
 $details->start();
-$details->th_td ("Node group name",$nodegroup['groupname']);
+$details->form_start(l_actions(),array("action"=>"update-nodegroup", "nodegroup_id"=>$nodegroup_id));
+$details->th_td ("Node group name",$nodegroup['groupname'],'groupname');
+// can't change the target tag
 $details->th_td ("Based on tag",href(l_tag($nodegroup['tag_type_id']),$tagname));
-$details->th_td("Matching value",$nodegroup['value']);
+$details->th_td("Matching value",$nodegroup['value'],'value');
 $details->th_td("# nodes",count($nodegroup['node_ids']));
+$details->tr_submit("submit","Update Nodegroup");
+$details->form_end();
 $details->end();
 
+$toggle->end();
+
 // xxx : add & delete buttons would make sense here too
 $toggle=new PlekitToggle('nodes',"Nodes");
 $toggle->start();
index 6917d6a..82bac8c 100644 (file)
@@ -17,6 +17,7 @@ include 'plc_header.php';
 require_once 'plc_functions.php';
 require_once 'linetabs.php';
 require_once 'table.php';
+require_once 'form.php';
 
 // -------------------- 
 // recognized URL arguments
@@ -55,27 +56,62 @@ if ( ! $nodegroups ) {
   return;
  }
   
+$headers=array();
+$notes=array();
 
-$headers = array ( "Name"=>"string",
-                  "Tag"=>"string",
-                  "Value"=>"string",
-                  "Nodes"=>"int");
+$headers['group name']='string';
+$headers['tag name']='string';
+$headers['tag value']='string';
+$headers['# N']='int';
+$notes []= '# N = number of nodes in the group';
 
+$headers["Id"]="int";
+if (plc_is_admin()) $headers[plc_delete_icon()]="none";
+
+$form=new PlekitForm(l_actions(),NULL);
+$form->start();
 # initial sort on groupname
-$table=new PlekitTable("nodegroups",$headers,0);
+$table=new PlekitTable("nodegroups",$headers,0,array('notes'=>$notes));
 $table->start();
 
 foreach ($nodegroups as $nodegroup) {
   $table->row_start();
-  $table->cell (href(l_nodegroup($nodegroup['nodegroup_id']),$nodegroup['groupname']));
+  $nodegroup_id=$nodegroup['nodegroup_id'];
+  $table->cell (href(l_nodegroup($nodegroup_id),$nodegroup['groupname']));
   // yes, a nodegroup is not a tag, but knows enough for this to work
   $table->cell (l_tag_obj($nodegroup));
   $table->cell ($nodegroup['value']);
   $table->cell (count($nodegroup['node_ids']));
+  $table->cell ($nodegroup_id);
+  $table->cell ($form->checkbox_html('nodegroup_ids[]',$nodegroup_id));
   $table->row_end();
 }
 
+$table->tfoot_start();
+
+$table->row_start();
+$table->cell($form->submit_html ("delete-nodegroups","Remove groups"),
+            array('hfill'=>true,'align'=>'right'));
+$table->row_end();
+
+// an inline area to add a tag type
+$table->row_start();
+
+// build the tagname selector
+$relevant_tags = $api->GetTagTypes( array("category"=>'*node*'));
+function selector_argument ($tt) { return array('display'=>$tt['tagname'],"value"=>$tt['tag_type_id']); }
+$selectors=array_map("selector_argument",$relevant_tags);
+$tagname_input=$form->select_html("tag_type_id",$selectors,array('label'=>"Tag Name"));
+
+
+$table->cell($form->text_html('groupname',''));
+$table->cell($tagname_input);
+$table->cell($form->text_html('value',''));
+$table->cell($form->submit_html("add-nodegroup","Add"),3);
+$table->row_end();
+
 $table->end();
+$form->end();
 
 //plekit_linetabs ($tabs,"bottom");
 
index 675594d..010150c 100644 (file)
@@ -47,13 +47,41 @@ $category=$tag_type['category'];
 $filter=array('tag_type_id'=>$tag_type_id);
 $node_tags=$api->GetNodeTags($filter);
 $interface_tags=$api->GetInterfaceTags($filter);
-$slice_tags=$api->GetSliceTags(array_merge($filter,array("node_id"=>array())));
-$sliver_tags=$api->GetSliceTags(array_merge($filter,array("~node_id"=>array())));
+// split slice tags into 3 families, whether this applies to the whole slice, or a nodegroup, or a node
+// using filters for this purpose does not work out very well, maybe a bug in the filter stuff
+// anyway this is more efficient, and we compute the related node(groups) in the same pass
+$slice_tags=$api->GetSliceTags(array_merge($filter));
+$count_slice=0;
+$count_nodegroup=0;
+$nodegroup_ids=array();
+$count_node=0;
+$node_ids=array();
+foreach ($slice_tags as $slice_tag) {
+  if ($slice_tag['node_id']) {
+    $node_ids []= $slice_tag['node_id'];
+    $count_node += 1;
+  } else if ($slice_tag['nodegroup_id']) {
+    $nodegroup_ids []= $slice_tag['nodegroup_id'];
+    $count_nodegroup += 1;
+  } else {
+    $count_slice += 1;
+  }
+}
+
+$nodes=$api->GetNodes($node_ids,array('hostname','node_id'));
+$node_hash=array();
+foreach ($nodes as $node) $node_hash[$node['node_id']]=$node;
+$nodegroups=$api->GetNodeGroups($nodegroup_ids,array('groupname','nodegroup_id'));
+$nodegroup_hash=array();
+foreach ($nodegroups as $nodegroup) $nodegroup_hash[$nodegroup['nodegroup_id']]=$nodegroup;
+
 
 drupal_set_title("Details for tag type $tagname");
 plekit_linetabs($tabs);
 
 // ----------
+$toggle = new PlekitToggle ('details','Details');
+$toggle->start();
 $can_update=plc_is_admin();
 $details=new PlekitDetails ($can_update);
 
@@ -61,8 +89,8 @@ $details->form_start(l_actions(),array("action"=>"update-tag-type",
                                       "tag_type_id"=>$tag_type_id));
 $details->start();
 $details->th_td("Name",$tagname,"tagname");
-$details->th_td("Category",$category,"category");
-$details->th_td("Description",$description,"description");
+$details->th_td("Category",$category,"category",array('width'=>30));
+$details->th_td("Description",$description,"description",array('width'=>40));
 
 if ($can_update) {
 // select the option corresponding with min_role_id
@@ -79,11 +107,13 @@ if ($can_update)
 $details->space();
 $details->th_td("Used in nodes",count($node_tags));
 $details->th_td("Used in interfaces",count($interface_tags));
-$details->th_td("Used in slices",count($slice_tags));
-$details->th_td("Used in slivers",count($sliver_tags));
+$details->th_td("Used in slices/node",$count_node);
+$details->th_td("Used in slices/nodegroup",$count_nodegroup);
+$details->th_td("Used in slices",$count_slice);
 
 $details->end();
 $details->form_end();
+$toggle->end();
 
 // common options for tables below
 $table_options=array('notes_area'=>false, 'pagesize_area'=>false, 'search_width'=>10);
@@ -119,23 +149,37 @@ if (count ($interface_tags)) {
   $toggle->end();
  }
 
-// grouping both kinds of slice tags 
-// xxx don't show hostnames yet
-$slice_tags = array_merge ($slice_tags,$sliver_tags);
 if (count ($slice_tags)) {
-  $toggle=new PlekitToggle('tag_slices',"Slice and sliver tags");
+  $toggle=new PlekitToggle('tag_slices',"Slice tags");
   $toggle->start();
-  $table=new PlekitTable ("tag_slices",array("Slice"=>"string","value"=>"string","Node id"=>"int"),0,$table_options);
+  $headers=array();
+  $headers["slice"]='string';
+  $headers["value"]='string';
+  $headers["node"]='string';
+  $headers["nodegroup"]='string';
+  $table=new PlekitTable ("tag_slices",$headers,0,$table_options);
   $table->start();
   foreach ($slice_tags as $slice_tag) {
     $table->row_start();
     $table->cell(href(l_slice($slice_tag['slice_id']),$slice_tag['name']));
     $table->cell($slice_tag['value']);
+
     $node_text="all";
-    // sliver tag
-    if ($slice_tag['node_id']) 
-      $node_text=l_node($slice_tag['node_id'],$slice_tag['node_id']);
+    if ($slice_tag['node_id']) {
+      $node_id=$slice_tag['node_id'];
+      $node=$node_hash[$node_id];
+      $node_text=l_node_obj($node);
+    }
     $table->cell($node_text);
+
+    $nodegroup_text="all";
+    if ($slice_tag['nodegroup_id']) {
+      $nodegroup_id=$slice_tag['nodegroup_id'];
+      $nodegroup=$nodegroup_hash[$nodegroup_id];
+      $nodegroup_text=l_nodegroup_obj($nodegroup);
+    }
+    $table->cell($nodegroup_text);
+
     $table->row_end();
   }
   $table->end();
index a8b218a..0f57723 100644 (file)
@@ -44,20 +44,24 @@ if ($pattern)
 $tag_types= $api->GetTagTypes($tag_type_filter, $tag_type_columns);
   
 $headers=array();
+$notes=array();
 // delete button
 $headers['Name']="string";
 $headers['Description']="string";
 $headers['Category']="string";
-$headers['Min role']="string";
-// xxx ref count would be helpful
-//if (plc_is_admin()) $headers['#']='int';
+$headers['MR']="string";
+$notes []= "MR: Min Role, needed to manage this tag";
+
+// xxx ref count would be helpful but seem too expensive to compute at this stage 
+// the individual tag page show those ref counts per type
+
 $headers["Id"]="int";
 if (plc_is_admin()) $headers[plc_delete_icon()]="none";
 
 $form=new PlekitForm(l_actions(),NULL);
 $form->start();
 
-$table = new PlekitTable("tags",$headers,0);
+$table = new PlekitTable("tags",$headers,0,array('notes'=>$notes));
 $table->start();
 
 $roles_hash=plc_role_global_hash($api);
@@ -102,7 +106,7 @@ if (plc_is_admin()) {
   $table->cell($form->textarea_html('description','',$description_width,2));
   $table->cell($form->text_html('category',''));
   $table->cell($role_input);
-  $table->cell($form->submit_html("add-tag-type","Add Type"),2);
+  $table->cell($form->submit_html("add-tag-type","Add"),2);
   $table->row_end();
  }