r1911 + fix redirect for single IPv4 address match
[racktables] / inc / interface.php
index db4d75b47d17b4e05d67607fd6ab8f19be1a3b5a..2672e34274f6696cb630a53959d90da99cee66e0 100644 (file)
@@ -59,20 +59,25 @@ function renderIndex ()
 
 function renderRackspace ()
 {
-?>
-       <table border=0 cellpadding=10 cellpadding=1>
-<?php
+       $tagfilter = getTagFilter();
+       $tagfilter_str = getTagFilterStr ($tagfilter);
+       echo "<table class=objview border=0 width='100%'><tr><td class=pcleft>";
+       renderTagFilterPortlet ($tagfilter, 'rack');
+       echo '</td><td class=pcright>';
+       echo '<table border=0 cellpadding=10 cellpadding=1>';
        // generate thumb gallery
-       $rackrowList = getRackRowInfo();
+       $rackrowList = getRackspace ($tagfilter);
        global $root, $nextorder;
        $rackwidth = getConfigVar ('rtwidth_0') + getConfigVar ('rtwidth_1') + getConfigVar ('rtwidth_2');
        $order = 'odd';
        foreach ($rackrowList as $rackrow)
        {
-               echo "<tr class=row_${order}><th><a href='${root}?page=row&row_id=${rackrow['dict_key']}'>${rackrow['dict_value']}</a></th>";
-               $rackList = getRacksForRow ($rackrow['dict_key']);
+               echo "<tr class=row_${order}><th>";
+               echo "<a href='${root}?page=row&row_id=${rackrow['row_id']}${tagfilter_str}'>";
+               echo "${rackrow['row_name']}</a></th>";
+               $rackList = getRacksForRow ($rackrow['row_id'], $tagfilter);
                echo "<td><table border=0 cellspacing=5><tr>";
-               foreach ($rackList as $dummy => $rack)
+               foreach ($rackList as $rack)
                {
                        echo "<td align=center><a href='${root}?page=rack&rack_id=${rack['id']}'>";
                        echo "<img border=0 width=${rackwidth} height=";
@@ -85,6 +90,7 @@ function renderRackspace ()
                $order = $nextorder[$order];
        }
        echo "</table>\n";
+       echo "</td></tr></table>\n";
 }
 
 function renderRow ($row_id)
@@ -99,7 +105,8 @@ function renderRow ($row_id)
                showError ('getRackRowInfo() failed', __FUNCTION__);
                return;
        }
-       $rackList = getRacksForRow ($row_id);
+       $tagfilter = getTagFilter();
+       $rackList = getRacksForRow ($row_id, $tagfilter);
        // Main layout starts.
        echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
 
@@ -115,14 +122,14 @@ function renderRow ($row_id)
        echo "</table><br>\n";
        finishPortlet();
 
-       echo "</td><td class=pcright>";
+       echo "</td><td class=pcright rowspan=2>";
 
        global $root, $nextorder;
        $rackwidth = getConfigVar ('rtwidth_0') + getConfigVar ('rtwidth_1') + getConfigVar ('rtwidth_2');
        $order = 'odd';
        startPortlet ('Racks');
        echo "<table border=0 cellspacing=5 align='center'><tr>";
-       foreach ($rackList as $dummy => $rack)
+       foreach ($rackList as $rack)
        {
                echo "<td align=center class=row_${order}><a href='${root}?page=rack&rack_id=${rack['id']}'>";
                echo "<img border=0 width=" . $rackwidth * getConfigVar ('ROW_SCALE') . " height=";
@@ -134,7 +141,10 @@ function renderRow ($row_id)
        }
        echo "</tr></table>\n";
        finishPortlet();
+       echo "</td></tr>";
 
+       echo "<tr><td class=pcleft>";
+       renderTagFilterPortlet ($tagfilter, 'rack', 'row_id', $row_id);
        echo "</td></tr></table>";
 }
 
@@ -169,14 +179,23 @@ function renderRack ($rack_id = 0, $hl_obj_id = 0)
        markupObjectProblems ($rackData);
        $prev_id = getPrevIDforRack ($rackData['row_id'], $rack_id);
        $next_id = getNextIDforRack ($rackData['row_id'], $rack_id);
-       echo "<center>\n<h2><a href='${root}?page=row&row_id=${rackData['row_id']}'>${rackData['row_name']}</a> :";
+       echo "<center><table border=0><tr valign=middle>";
+       echo "<td><h2><a href='${root}?page=row&row_id=${rackData['row_id']}'>${rackData['row_name']}</a> :</h2></td>";
        // FIXME: use 'bypass'?
        if ($prev_id != NULL)
-               echo " <a href='${root}?page=rack&rack_id=${prev_id}'>&lt; &lt; &lt;</a>";
-       echo " <a href='${root}?page=rack&rack_id=${rackData['id']}'>${rackData['name']}</a>";
+       {
+               echo "<td><a href='${root}?page=rack&rack_id=${prev_id}'>";
+               printImageHREF ('prev', 'previous rack');
+               echo "</a></td>";
+       }
+       echo "<td><h2><a href='${root}?page=rack&rack_id=${rackData['id']}'>${rackData['name']}</a></h2></td>";
        if ($next_id != NULL)
-               echo " <a href='${root}?page=rack&rack_id=${next_id}'>&gt; &gt; &gt;</a>";
-       echo "</h2>\n";
+       {
+               echo "<td><a href='${root}?page=rack&rack_id=${next_id}'>";
+               printImageHREF ('next', 'next rack');
+               echo "</a></td>";
+       }
+       echo "</h2></td></tr></table>\n";
        if ($rackData['left_is_front'] == 'yes')
                $markup = array ('left' => 'Front', 'right' => 'Back');
        else
@@ -373,6 +392,7 @@ function renderEditObjectForm ($object_id)
        echo "<input type=hidden name=page value=${pageno}>";
        echo "<input type=hidden name=tab value=${tabno}>";
        echo "<input type=hidden name=object_id value=${object_id}>";
+       echo "<input type=hidden name=got_data value=1>";
        echo '<table border=0 align=center>';
        echo "<tr><th class=tdright>Type:</th><td class=tdleft>";
        printSelect (getObjectTypeList(), 'object_type_id', $object['objtype_id']);
@@ -387,7 +407,9 @@ function renderEditObjectForm ($object_id)
                echo ' checked';
        echo "></td></tr>\n";
        echo "<tr><td colspan=2><b>Comment:</b><br><textarea name=object_comment rows=10 cols=80>${object['comment']}</textarea></td></tr>";
-       echo "<tr><th class=submit colspan=2><input type=submit name=got_data value='Update'></td></tr>\n";
+       echo "<tr><th class=submit colspan=2>";
+       printImageHREF ('SAVE', 'Save changes', TRUE);
+       echo "</td></tr>\n";
        echo '</form></table><br>';
        finishPortlet();
        echo '</td>';
@@ -414,11 +436,11 @@ function renderEditObjectForm ($object_id)
                if (!empty ($record['value']))
                {
                        echo "<a href=${root}process.php?page=${pageno}&tab=${tabno}&op=del&object_id=${object_id}&attr_id=${record['id']}>";
-                       printImageHREF ('delete', 'Delete value');
+                       printImageHREF ('clear', 'Clear value');
                        echo '</a>';
                }
                else
-                       printImageHREF ('nodelete', 'Already empty');
+                       echo '&nbsp;';
                echo '</td>';
                echo "<td class=tdright>${record['name']}:</td><td class=tdleft>";
                switch ($record['type'])
@@ -437,7 +459,9 @@ function renderEditObjectForm ($object_id)
                echo "</td></tr>\n";
                $i++;
        }
-       echo "<tr><td colspan=3><input type=submit value='Update'></td></tr>\n";
+       echo "<tr><td colspan=3>";
+       printImageHREF ('SAVE', 'Save changes', TRUE);
+       echo "</td></tr>\n";
        echo "</form>";
        echo "</table>\n";
        finishPortlet();
@@ -522,6 +546,11 @@ function printSelect ($rowList, $select_name, $selected_id = 1)
                        $tmp = explode ('^', $dict_value, 2);
                        $optgroup[$tmp[0]][$dict_key] = $tmp[1];
                }
+               elseif (strpos ($dict_value, '&') !== FALSE)
+               {
+                       $tmp = explode ('&', $dict_value, 2);
+                       $optgroup[$tmp[0]][$dict_key] = $tmp[1];
+               }
                else
                        $other[$dict_key] = $dict_value;
        }
@@ -566,6 +595,27 @@ function printSelect ($rowList, $select_name, $selected_id = 1)
        echo "</select>";
 }
 
+// used by renderGridForm() and renderRackPage()
+function renderRackInfoPortlet ($rackData)
+{
+       startPortlet ('summary');
+       echo "<table border=0 cellspacing=0 cellpadding=3 width='100%'>\n";
+       echo "<tr><th width='50%' class=tdright>Rack row:</th><td class=tdleft>${rackData['row_name']}</td></tr>\n";
+       echo "<tr><th width='50%' class=tdright>Name:</th><td class=tdleft>${rackData['name']}</td></tr>\n";
+       echo "<tr><th width='50%' class=tdright>Height:</th><td class=tdleft>${rackData['height']}</td></tr>\n";
+       echo "<tr><th width='50%' class=tdright>Utilization:</th><td class=tdleft>";
+       renderProgressBar (getRSUforRack ($rackData));
+       echo "</td></tr>\n";
+       echo "<tr><th width='50%' class=tdright>Objects:</th><td class=tdleft>";
+       echo getObjectCount ($rackData);
+       echo "</td></tr>\n";
+       printTagTRs ("${root}?page=rackspace&");
+       if (!empty ($rackData['comment']))
+               echo "<tr><th width='50%' class=tdright>Comment:</th><td class=tdleft>${rackData['comment']}</td></tr>\n";
+       echo '</table>';
+       finishPortlet();
+}
+
 // This is a universal editor of rack design/waste.
 function renderGridForm ($rack_id = 0, $filter, $header, $submit, $state1, $state2)
 {
@@ -581,14 +631,17 @@ function renderGridForm ($rack_id = 0, $filter, $header, $submit, $state1, $stat
        }
 
        global $root, $pageno, $tabno;
-       $filter($rackData);
+       $filter ($rackData);
        markupObjectProblems ($rackData);
 
        // Process form submit.
        if (isset ($_REQUEST['do_update']))
        {
                $log[] = processGridForm ($rackData, $state1, $state2);
-               printLog($log);
+               printLog ($log);
+               $rackData = getRackData ($rack_id);
+               $filter ($rackData);
+               markupObjectProblems ($rackData);
        }
 
        // Render the result whatever it is.
@@ -598,15 +651,7 @@ function renderGridForm ($rack_id = 0, $filter, $header, $submit, $state1, $stat
 
        // Left column with information portlet.
        echo "<tr><td class=pcleft height='1%' width='50%'>";
-       startPortlet ('Rack information');
-       echo "<table border=0 cellspacing=0 cellpadding=3 width='100%'>\n";
-       echo "<tr><th width='50%' class=tdright>Rack name:</th><td class=tdleft>${rackData['name']}</td></tr>\n";
-       echo "<tr><th width='50%' class=tdright>Height:</th><td class=tdleft>${rackData['height']}</td></tr>\n";
-       echo "<tr><th width='50%' class=tdright>Rack row:</th><td class=tdleft>${rackData['row_name']}</td></tr>\n";
-       echo "<tr><th width='50%' class=tdright>Comment:</th><td class=tdleft>${rackData['comment']}</td></tr>\n";
-       echo "</table>\n";
-       finishPortlet();
-
+       renderRackInfoPortlet ($rackData);
        echo "</td>\n";
        echo "<td class=pcright>";
 
@@ -691,7 +736,8 @@ function renderRackObject ($object_id = 0)
                echo "<tr><th width='50%' class=tdright>Common name:</th><td class=tdleft>${info['name']}</td></tr>\n";
        elseif (in_array ($info['objtype_id'], explode (',', getConfigVar ('NAMEFUL_OBJTYPES'))))
                echo "<tr><td colspan=2 class=msg_error>Common name is missing.</td></tr>\n";
-       echo "<tr><th width='50%' class=tdright>Object type:</th><td class=tdleft>${info['objtype_name']}</td></tr>\n";
+       echo "<tr><th width='50%' class=tdright>Object type:</th>";
+       echo "<td class=tdleft><a href='${root}?page=objgroup&group_id=${info['objtype_id']}'>${info['objtype_name']}</a></td></tr>\n";
        if (!empty ($info['asset_no']))
                echo "<tr><th width='50%' class=tdright>Asset tag:</th><td class=tdleft>${info['asset_no']}</td></tr>\n";
        elseif (in_array ($info['objtype_id'], explode (',', getConfigVar ('REQUIRE_ASSET_TAG_FOR'))))
@@ -702,11 +748,10 @@ function renderRackObject ($object_id = 0)
                echo "<tr><th width='50%' class=tdright>Barcode:</th><td class=tdleft>${info['barcode']}</td></tr>\n";
        if ($info['has_problems'] == 'yes')
                echo "<tr><td colspan=2 class=msg_error>Has problems</td></tr>\n";
-       $attrs = getAttrValues ($object_id, TRUE);
        foreach (getAttrValues ($object_id, TRUE) as $record)
                if (!empty ($record['value']))
                        echo "<tr><th width='50%' class=opt_attr_th>${record['name']}:</th><td class=tdleft>${record['a_value']}</td></tr>\n";
-       printTagTRs();
+       printTagTRs ("${root}?page=objgroup&group_id=${info['objtype_id']}&");
        echo "</table><br>\n";
        finishPortlet();
 
@@ -843,7 +888,7 @@ function renderRackObject ($object_id = 0)
                finishPortlet();
        }
 
-       $forwards = getObjectForwards ($object_id);
+       $forwards = getNATv4ForObject ($object_id);
        if (count($forwards['in']) or count($forwards['out']))
        {
                startPortlet('NATv4');
@@ -1032,11 +1077,13 @@ function renderPortsForObject ($object_id = 0)
                        echo "</a> <input type=text name=reservation_comment>";
                        echo "</td>\n";
                }
-               echo "<td><input type='submit' value='OK'></td>";
-               echo "</form></tr>\n";
+               echo "<td>";
+               printImageHREF ('save', 'Save changes', TRUE);
+               echo "</td></form></tr>\n";
        }
-       echo "<form action='${root}process.php'><tr>";
-       echo "<td colspan=2><input type=text size=10 name=port_name tabindex=100></td>\n";
+       echo "<form action='${root}process.php'><tr><td>";
+       printImageHREF ('add', '', TRUE, 104);
+       echo "</td><td><input type=text size=8 name=port_name tabindex=100></td>\n";
        echo "<td><input type=text size=24 name=port_label tabindex=101></td>";
        echo "<input type=hidden name=op value=addPort>\n";
        echo "<input type=hidden name=object_id value='${object_id}'>\n";
@@ -1054,7 +1101,7 @@ function renderPortsForObject ($object_id = 0)
        }
        echo "</select></td>";
        echo "<td><input type=text name=port_l2address tabindex=103></td>\n";
-       echo "<td colspan=4><input type='submit' value='Add a new port' tabindex=104></td></tr></form>";
+       echo "<td colspan=4>&nbsp;</td></tr></form>";
        echo "</table><br>\n";
        finishPortlet();
 
@@ -1133,8 +1180,8 @@ function renderNetworkForObject ($object_id=0)
                echo "<tr class='$class'><td><a href='process.php?op=delAddrFObj&page=${pageno}&tab=${tabno}&ip=${addr['ip']}&object_id=$object_id'>";
                printImageHREF ('delete', 'Delete this IPv4 address');
                echo "</a></td>";
-               echo "<td><input type='text' name='bond_name' value='${addr['name']}' size=10></td>";
-               echo "<td><a href='${root}?page=ipaddress&ip=${addr['ip']}'>${addr['ip']}</a></td>";
+               echo "<td class=tdleft><input type='text' name='bond_name' value='${addr['name']}' size=10></td>";
+               echo "<td class=tdleft><a href='${root}?page=ipaddress&ip=${addr['ip']}'>${addr['ip']}</a></td>";
                echo "<td class='description'>$address_name</td>\n";
                echo "<td><select name='bond_type'>";
                foreach (array('regular'=>'Regular', 'virtual'=>'Virtual', 'shared'=>'Shared') as $n => $v)
@@ -1192,23 +1239,27 @@ function renderNetworkForObject ($object_id=0)
                        }
                }
 
-               echo "</td><td><input type=submit value='OK'></td></form></tr>\n";
+               echo "</td><td>";
+               printImageHREF ('save', 'Save changes', TRUE);
+               echo "</td></form></tr>\n";
        }
 
 
-       echo "<form action='${root}process.php'><tr><td colspan=2><input type='text' size='10' name='name' tabindex=100></td>\n";
+       echo "<form action='${root}process.php'><tr><td>";
+       printImageHREF ('add', 'Allocate new address', TRUE, 99);
+       echo "</td><td class=tdleft>";
+       echo "<input type='text' size='10' name='name' tabindex=100></td>\n";
        echo "<input type=hidden name=page value='${pageno}'>\n";
        echo "<input type=hidden name=tab value='${tabno}'>\n";
        echo "<input type=hidden name=op value=addAddrFObj>\n";
        echo "<input type=hidden name=object_id value='$object_id'>\n";
-
-       echo "<td><input type=text name='ip' tabindex=101>\n";
-       echo "</td><td><select name='type' tabindex=102>";
+       echo "<td class=tdleft><input type=text name='ip' tabindex=101>\n";
+       echo "</td><td>&nbsp;</td><td><select name='type' tabindex=102>";
        echo "<option value='regular'>Regular</option>";
        echo "<option value='virtual'>Virtual</option>";
        echo "<option value='shared'>Shared</option>";
        echo "</select>";
-       echo "</td><td colspan=3><input type='submit' value='Add a new interface' tabindex=103></td></tr></form>";
+       echo "</td><td colspan=2>&nbsp;</td></tr></form>";
        echo "</table><br>\n";
        finishPortlet();
 
@@ -1403,7 +1454,7 @@ function renderMolecule ($mdata, $object_id)
        // sort data out
        $rackpack = array();
        global $loclist;
-       foreach ($mdata as $dummy => $rua)
+       foreach ($mdata as $rua)
        {
                $rack_id = $rua['rack_id'];
                $unit_no = $rua['unit_no'];
@@ -1420,7 +1471,7 @@ function renderMolecule ($mdata, $object_id)
                $rackpack[$rack_id][$unit_no][$loclist[$atom]]['object_id'] = $object_id;
        }
        // now we have some racks to render
-       foreach ($rackpack as $dummy => $rackData)
+       foreach ($rackpack as $rackData)
        {
                markAllSpans ($rackData);
                echo "<table class=molecule cellspacing=0>\n";
@@ -1488,36 +1539,6 @@ function renderProblematicObjectsPortlet ()
        finishPortlet();
 }
 
-function renderObjectGroupSummary ()
-{
-       global $root;
-       $tagfilter = isset ($_REQUEST['tagfilter']) ? $_REQUEST['tagfilter'] : array();
-       $tagfilter = complementByKids ($tagfilter);
-       $summary = getObjectGroupInfo();
-       if ($summary === NULL)
-       {
-               showError ('getObjectGroupInfo() failed', __FUNCTION__);
-               return;
-       }
-       echo "<table border=0 class=objectview>\n";
-       echo "<tr><td class=pcleft width='25%'>";
-
-       startPortlet ('Summary');
-       foreach ($summary as $gi)
-               echo "<a href='${root}?page=objgroup&group_id=${gi['id']}'><b>${gi['name']}</b></a> <i>(${gi['count']})</i><br>";
-       finishPortlet();
-
-       echo '</td><td class=pcright>';
-       renderUnmountedObjectsPortlet();
-       echo '</td><td class=pcright>';
-       renderProblematicObjectsPortlet();
-       echo '</td><td class=pcright>';
-       startPortlet ('Tag filter');
-       renderTagFilterSelect ($tagfilter, 'object');
-       finishPortlet();
-       echo "</td></tr></table>\n";
-}
-
 function renderObjectSpace ()
 {
        global $root, $taglist, $tagtree;
@@ -1557,12 +1578,8 @@ function renderObjectGroup ()
        global $root, $pageno, $tabno, $nextorder, $taglist, $tagtree;
        assertUIntArg ('group_id', __FUNCTION__, TRUE);
        $group_id = $_REQUEST['group_id'];
-       $tagfilter = isset ($_REQUEST['tagfilter']) ? $_REQUEST['tagfilter'] : array();
-       $tagfilter_str = getStringFromTrail (getExplicitTagsOnly (buildTrailFromIds ($tagfilter)));
-       $tagfilter = complementByKids ($tagfilter);
-       echo "<form>\n";
-       echo "<input type=hidden name=page value=${pageno}>\n";
-       echo "<input type=hidden name=group_id value=${group_id}>\n";
+       $tagfilter = getTagFilter();
+       $tagfilter_str = getTagFilterStr ($tagfilter);
        echo "<table border=0 class=objectview>\n";
        echo "<tr><td class=pcleft width='25%'>";
        startPortlet ('change type');
@@ -1579,7 +1596,7 @@ function renderObjectGroup ()
                echo '<div align=left><ul>';
                foreach ($groupInfo as $gi)
                {
-                       echo "<li><a href='${root}?page=${pageno}&group_id=${gi['id']}&tagfilter[]=${tagfilter_str}'>";
+                       echo "<li><a href='${root}?page=${pageno}&group_id=${gi['id']}${tagfilter_str}'>";
                        if ($gi['id'] == $group_id)
                                echo '<strong>';
                        echo "${gi['name']}</a>";
@@ -1624,65 +1641,8 @@ function renderObjectGroup ()
 
        echo "</td><td class=pcright width='25%'>";
 
-       startPortlet ('change filter');
-       renderTagFilterSelect ($tagfilter, 'object');
-       echo "<input type=submit value='Apply'>\n";
-       finishPortlet();
+       renderTagFilterPortlet ($tagfilter, 'object', 'group_id', $group_id);
        echo "</td></tr></table>\n";
-       echo '</form>';
-}
-
-function renderObjectGroup_old ()
-{
-       global $root;
-       assertUIntArg ('group_id', __FUNCTION__);
-       $group_id = $_REQUEST['group_id'];
-       $summary = getObjectGroupInfo();
-       if ($summary == NULL)
-       {
-               showError ('getObjectGroupInfo() failed', __FUNCTION__);
-               return;
-       }
-       $objects = getObjectList ($group_id);
-       if ($objects === NULL)
-       {
-               showError ('getObjectList() failed', __FUNCTION__);
-               return;
-       }
-       echo "<table border=0 class=objectview>\n";
-       echo "<tr><td class=pcleft width='25%'>";
-
-       startPortlet ('All objects');
-       foreach ($summary as $gi)
-       {
-               echo "<a href='${root}?page=objgroup&group_id=${gi['id']}'><b>${gi['name']}</b></a> <i>(${gi['count']})</i><br>";
-       }
-       finishPortlet();
-
-       echo '</td><td class=pcright>';
-
-       startPortlet ('Object group');
-       echo '<br><br><table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
-       echo '<tr><th>Common name</th><th>Visible label</th><th>Asset tag</th><th>Barcode</th><th>Rack</th></tr>';
-       $order = 'odd';
-       global $nextorder;
-       foreach ($objects as $obj)
-       {
-               echo "<tr class=row_${order}><td><a href='${root}?page=object&object_id=${obj['id']}'>${obj['dname']}</a></td>";
-               echo "<td>${obj['label']}</td>";
-               echo "<td>${obj['asset_no']}</td>";
-               echo "<td>${obj['barcode']}</td>";
-               if ($obj['rack_id'])
-                       echo "<td><a href='${root}?page=rack&rack_id=${obj['rack_id']}'>${obj['Rack_name']}</a></td>";
-               else
-                       echo '<td>Unmounted</td>';
-               echo '</tr>';
-               $order = $nextorder[$order];
-       }
-       echo '</table>';
-       finishPortlet();
-
-       echo "</td></tr></table>";
 }
 
 function renderEmptyPortsSelect ($port_id, $type_id)
@@ -1841,8 +1801,7 @@ function renderAddressspace ()
 
        startPortlet ('Subnets');
        echo "<table class='widetable' border=0 cellpadding=10 cellspacing=0 align='center'>\n";
-       $tagfilter = isset ($_REQUEST['tagfilter']) ? $_REQUEST['tagfilter'] : array();
-       $tagfilter = complementByKids ($tagfilter);
+       $tagfilter = getTagFilter();
        $addrspaceList = getAddressspaceList ($tagfilter);
        echo "<tr><th>Subnet</th><th>Name</th><th>Utilization</th></tr>";
        foreach ($addrspaceList as $iprange)
@@ -1982,6 +1941,76 @@ function renderAddNewRange ()
 function renderIPRange ($id)
 {
        global $root, $pageno, $tabno;
+       $netmaskbylen = array
+       (
+               32 => '255.255.255.255',
+               31 => '255.255.255.254',
+               30 => '255.255.255.252',
+               29 => '255.255.255.248',
+               28 => '255.255.255.240',
+               27 => '255.255.255.224',
+               26 => '255.255.255.192',
+               25 => '255.255.255.128',
+               24 => '255.255.255.0',
+               23 => '255.255.254.0',
+               22 => '255.255.252.0',
+               21 => '255.255.248.0',
+               20 => '255.255.240.0',
+               19 => '255.255.224.0',
+               18 => '255.255.192.0',
+               17 => '255.255.128.0',
+               16 => '255.255.0.0',
+               15 => '255.254.0.0',
+               14 => '255.252.0.0',
+               13 => '255.248.0.0',
+               12 => '255.240.0.0',
+               11 => '255.224.0.0',
+               10 => '255.192.0.0',
+               9 => '255.128.0.0',
+               8 => '255.0.0.0',
+               7 => '254.0.0.0',
+               6 => '252.0.0.0',
+               5 => '248.0.0.0',
+               4 => '240.0.0.0',
+               3 => '224.0.0.0',
+               2 => '192.0.0.0',
+               1 => '128.0.0.0'
+       );
+       $wildcardbylen = array
+       (
+               32 => '0.0.0.0',
+               31 => '0.0.0.1',
+               30 => '0.0.0.3',
+               29 => '0.0.0.7',
+               28 => '0.0.0.15',
+               27 => '0.0.0.31',
+               26 => '0.0.0.63',
+               25 => '0.0.0.127',
+               24 => '0.0.0.255',
+               23 => '0.0.1.255',
+               22 => '0.0.3.255',
+               21 => '0.0.7.255',
+               20 => '0.0.15.255',
+               19 => '0.0.31.255',
+               18 => '0.0.63.255',
+               17 => '0.0.127.255',
+               16 => '0.0.255.25',
+               15 => '0.1.255.255',
+               14 => '0.3.255.255',
+               13 => '0.7.255.255',
+               12 => '0.15.255.255',
+               11 => '0.31.255.255',
+               10 => '0.63.255.255',
+               9 => '0.127.255.255',
+               8 => '0.255.255.255',
+               7 => '1.255.255.255',
+               6 => '3.255.255.255',
+               5 => '7.255.255.255',
+               4 => '15.255.255.255',
+               3 => '31.255.255.255',
+               2 => '63.255.255.255',
+               1 => '127.255.255.255'
+       );
        $maxperpage = getConfigVar ('IPV4_ADDRS_PER_PAGE');
        if (isset($_REQUEST['pg']))
                $page = $_REQUEST['pg'];
@@ -2000,7 +2029,13 @@ function renderIPRange ($id)
        echo "<tr><th width='50%' class=tdright>Utilization:</th><td class=tdleft>";
        renderProgressBar ($used/$total);
        echo "&nbsp;${used}/${total}</td></tr>\n";
-       printTagTRs();
+       echo "<tr><th width='50%' class=tdright>Netmask:</th><td class=tdleft>";
+       echo $netmaskbylen[$range['mask']];
+       echo "</td></tr>\n";
+       echo "<tr><th width='50%' class=tdright>Wildcard bits:</th><td class=tdleft>";
+       echo $wildcardbylen[$range['mask']];
+       echo "</td></tr>\n";
+       printTagTRs ("${root}?page=ipv4space&");
        echo "</table><br>\n";
        finishPortlet();
        echo "</td>\n";
@@ -2056,7 +2091,8 @@ function renderIPRange ($id)
                        else
                                echo "<tr>";
 
-                       echo "<td><a href='${root}?page=ipaddress&ip=${addr['ip']}'>${addr['ip']}</a></td><td>${addr['name']}</td><td>";
+                       echo "<td class=tdleft><a href='${root}?page=ipaddress&ip=${addr['ip']}'>${addr['ip']}</a></td>";
+                       echo "<td class=tdleft>${addr['name']}</td><td class=tdleft>";
                        $delim = '';
                        $prologue = '';
                        if ( $addr['reserved'] == 'yes')
@@ -2317,18 +2353,18 @@ function renderIPAddressAssignment ()
 
 }
 
-function renderIPAddressPortForwarding ($object_id=0)
+function renderNATv4ForObject ($object_id = 0)
 {
        global $pageno, $tabno, $root;
        
        $info = getObjectInfo ($object_id);
-       $forwards = getObjectForwards ($object_id);
+       $forwards = getNATv4ForObject ($object_id);
        $addresses = getObjectAddresses ($object_id);
        showMessageOrError();
        echo "<center><h2>locally performed NAT</h2></center>";
 
        echo "<table class='widetable' cesspadding=5 cellspacing=0 border=0 align='center'>\n";
-       echo "<tr><th></th><th>Match endpoint</th><th>Translate to</th><th>Target object</th><th>Comment</th></tr>\n";
+       echo "<tr><th></th><th>Match endpoint</th><th>Translate to</th><th>Target object</th><th>Comment</th><th>&nbsp;</th></tr>\n";
 
        foreach ($forwards['out'] as $pf)
        {
@@ -2343,7 +2379,7 @@ function renderIPAddressPortForwarding ($object_id=0)
                        }
 
                echo "<tr class='$class'>";
-               echo "<td><a href='process.php?op=delPortForwarding&localip=${pf['localip']}&localport=${pf['localport']}&remoteip=${pf['remoteip']}&remoteport=${pf['remoteport']}&proto=${pf['proto_bin']}&object_id=$object_id&page=${pageno}&tab=${tabno}'>";
+               echo "<td><a href='process.php?op=delPortForwarding&localip=${pf['localip']}&localport=${pf['localport']}&remoteip=${pf['remoteip']}&remoteport=${pf['remoteport']}&proto=${pf['proto']}&object_id=$object_id&page=${pageno}&tab=${tabno}'>";
                printImageHREF ('delete', 'Delete NAT rule');
                echo "</a></td>";
                echo "<td>${pf['proto']}/${name}: <a href='${root}?page=ipaddress&tab=default&ip=${pf['localip']}'>${pf['localip']}</a>:${pf['localport']}";
@@ -2360,15 +2396,23 @@ function renderIPAddressPortForwarding ($object_id=0)
                                echo "<a href='${root}?page=object&tab=default&object_id=${bond['object_id']}'>${bond['object_name']}(${bond['name']})</a> ";
                elseif (!empty ($pf['remote_addr_name']))
                        echo '(' . $pf['remote_addr_name'] . ')';
-               echo "</td><form action='process.php'><input type='hidden' name='op' value='updPortForwarding'><input type=hidden name=page value='${pageno}'><input type=hidden name=tab value='${tabno}'><input type='hidden' name='object_id' value='$object_id'><input type='hidden' name='localip' value='${pf['localip']}'><input type='hidden' name='localport' value='${pf['localport']}'><input type='hidden' name='remoteip' value='${pf['remoteip']}'><input type='hidden' name='remoteport' value='${pf['remoteport']}'><input type='hidden' name='proto' value='${pf['proto_bin']}'><td class='description'><input type='text' name='description' value='${pf['description']}'> <input type='submit' value='OK'></td></form>";
-               echo "</tr>";
+               echo "</td><form action='process.php'><input type='hidden' name='op' value='updPortForwarding'><input type=hidden name=page value='${pageno}'>";
+               echo "<input type=hidden name=tab value='${tabno}'><input type='hidden' name='object_id' value='$object_id'>";
+               echo "<input type='hidden' name='localip' value='${pf['localip']}'><input type='hidden' name='localport' value='${pf['localport']}'>";
+               echo "<input type='hidden' name='remoteip' value='${pf['remoteip']}'><input type='hidden' name='remoteport' value='${pf['remoteport']}'>";
+               echo "<input type='hidden' name='proto' value='${pf['proto']}'><td class='description'>";
+               echo "<input type='text' name='description' value='${pf['description']}'></td><td>";
+               printImageHREF ('save', 'Save changes', TRUE);
+               echo "</td></form></tr>";
        }
        echo "<form action='process.php'><input type='hidden' name='op' value='forwardPorts'>";
        echo "<input type='hidden' name='object_id' value='$object_id'>";
        echo "<input type=hidden name=page value='${pageno}'>\n";
        echo "<input type=hidden name=tab value='${tabno}'>\n";
-       echo "<tr align='center'><td colspan=2>";
-       printSelect (readChapter ('Protocols'), 'proto');
+       echo "<tr align='center'><td>";
+       printImageHREF ('add', 'Add new NAT rule', TRUE);
+       echo '</td><td>';
+       printSelect (array ('TCP' => 'TCP', 'UDP' => 'UDP'), 'proto');
        echo "<select name='localip' tabindex=1>";
 
        foreach ($addresses as $addr)
@@ -2381,21 +2425,18 @@ function renderIPAddressPortForwarding ($object_id=0)
        printImageHREF ('find', 'Find object');
        echo "</a>";
        echo ":<input type='text' name='remoteport' size='4' tabindex=4></td><td></td>";
-       echo "<td colspan=1><input type='text' name='description' size='20' tabindex=5> <input type='submit' value='Create Forwarding' tabindex=6></td></tr>";
+       echo "<td colspan=1><input type='text' name='description' size='20' tabindex=5></td><td>&nbsp;</td></tr>";
        echo "</form>";
 
        echo "</table><br><br>";
 
-
        echo "<center><h2>arriving NAT connections</h2></center>";
        echo "<table class='widetable' cesspadding=5 cellspacing=0 border=0 align='center'>\n";
        echo "<tr><th></th><th>Source</th><th>Source objects</th><th>Target</th><th>Description</th></tr>\n";
 
        foreach ($forwards['in'] as $pf)
        {
-               echo "<tr>";
-
-               echo "<td><a href='process.php?op=delPortForwarding&localip=${pf['localip']}&localport=${pf['localport']}&remoteip=${pf['remoteip']}&remoteport=${pf['remoteport']}&proto=${pf['proto_bin']}&object_id=${pf['object_id']}&page=${pageno}&tab=${tabno}'>";
+               echo "<tr><td><a href='process.php?op=delPortForwarding&localip=${pf['localip']}&localport=${pf['localport']}&remoteip=${pf['remoteip']}&remoteport=${pf['remoteport']}&proto=${pf['proto']}&object_id=${pf['object_id']}&page=${pageno}&tab=${tabno}'>";
                printImageHREF ('delete', 'Delete NAT rule');
                echo "</a></td>";
                echo "<td>${pf['proto']}/<a href='${root}?page=ipaddress&tab=default&ip=${pf['localip']}'>${pf['localip']}</a>:${pf['localport']}</td>";
@@ -2404,19 +2445,7 @@ function renderIPAddressPortForwarding ($object_id=0)
                echo "<td class='description'>${pf['description']}</td></tr>";
        }
 
-//     echo "<form action='process.php'><input type='hidden' name='op' value='forwardPorts'>";
-//     echo "<input type='hidden' name='object_id' value='$object_id'>";
-//     echo "<input type=hidden name=page value='${pageno}'>\n";
-//     echo "<input type=hidden name=tab value='${tabno}'>\n";
-//     echo "<tr align='center'><td colspan=2><select name='proto'><option value='1'>TCP</option><option value='2'>UDP</option><input type='text' name='localip' size='10'>:<input type='text' name='localport' size='4'></td><td><select name='localip'>";
-//     foreach ($addresses as $addr)
-//             echo "<option value='${addr['ip']}'>${addr['ip']}</option>";
-//
-//     echo "</select>:<input type='text' name='remoteport' size='4'></td><td><input type='text' name='description' size='20'></td><td><input type='submit' value='Create Forwarding'></td></tr>";
-//     echo "</form>";
        echo "</table><br><br>";
-
-
 }
 
 
@@ -2627,6 +2656,20 @@ function renderSearchResults ()
                        $lasthit = 'ipv4network';
                        $summary['ipv4network'] = $tmp;
                }
+               $tmp = getIPv4RSPoolSearchResult ($terms);
+               if (count ($tmp))
+               {
+                       $nhits += count ($tmp);
+                       $lasthit = 'ipv4rspool';
+                       $summary['ipv4rspool'] = $tmp;
+               }
+               $tmp = getIPv4VServiceSearchResult ($terms);
+               if (count ($tmp))
+               {
+                       $nhits += count ($tmp);
+                       $lasthit = 'ipv4vs';
+                       $summary['ipv4vs'] = $tmp;
+               }
        }
        if ($nhits == 0)
                echo "<center><h2>Nothing found for '${terms}'</h2></center>";
@@ -2647,7 +2690,7 @@ function renderSearchResults ()
                                break;
                        case 'ipv4address2':
                                echo "<script language='Javascript'>document.location='${root}?page=ipaddress";
-                               echo "&ip=${record}";
+                               echo "&ip=${record['ip']}";
                                echo "';//</script>";
                                break;
                        case 'ipv4network':
@@ -2658,6 +2701,12 @@ function renderSearchResults ()
                        case 'object':
                                echo "<script language='Javascript'>document.location='${root}?page=object&object_id=${record['id']}';//</script>";
                                break;
+                       case 'ipv4rspool':
+                               echo "<script language='Javascript'>document.location='${root}?page=rspool&pool_id=${record['pool_id']}';//</script>";
+                               break;
+                       case 'ipv4vs':
+                               echo "<script language='Javascript'>document.location='${root}?page=vservice&id=${record['id']}';//</script>";
+                               break;
                }
                return;
        }
@@ -2670,7 +2719,7 @@ function renderSearchResults ()
                        switch ($where)
                        {
                                case 'object':
-                                       startPortlet ('Objects');
+                                       startPortlet ("<a href='${root}?page=objects'>Objects</a>");
                                        echo '<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
                                        echo '<tr><th>Common name</th><th>Visible label</th><th>Asset tag</th><th>barcode</th></tr>';
                                        foreach ($what as $obj)
@@ -2685,7 +2734,7 @@ function renderSearchResults ()
                                        finishPortlet();
                                        break;
                                case 'ipv4network':
-                                       startPortlet ('IPv4 networks');
+                                       startPortlet ("<a href='${root}?page=ipv4space'>IPv4 networks</a>");
                                        echo '<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
                                        echo '<tr><th>Network</th><th>Descritpion</th></tr>';
                                        foreach ($what as $net)
@@ -2704,7 +2753,7 @@ function renderSearchResults ()
                                        echo '<tr><th>Address</th><th>Descritpion</th></tr>';
                                        foreach ($what as $addr)
                                        {
-                                               echo "<tr class=row_${order}><td class=tdleft><a href='${root}?page=ipaddress&id=${addr['ip']}'>";
+                                               echo "<tr class=row_${order}><td class=tdleft><a href='${root}?page=ipaddress&ip=${addr['ip']}'>";
                                                echo "${addr['ip']}</a></td>";
                                                echo "<td class=tdleft>${addr['name']}</td></tr>";
                                                $order = $nextorder[$order];
@@ -2712,6 +2761,33 @@ function renderSearchResults ()
                                        echo '</table>';
                                        finishPortlet();
                                        break;
+                               case 'ipv4rspool':
+                                       startPortlet ("<a href='${root}?page=rspools'>RS pools</a>");
+                                       echo '<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
+                                       foreach ($what as $rspool)
+                                       {
+                                               echo "<tr class=row_${order}><td class=tdleft><a href='${root}?page=rspool&pool_id=${rspool['pool_id']}'>";
+                                               echo buildRSPoolName ($rspool);
+                                               echo "</a></td></tr>";
+                                               $order = $nextorder[$order];
+                                       }
+                                       echo '</table>';
+                                       finishPortlet();
+                                       break;
+                               case 'ipv4vs':
+                                       startPortlet ("<a href='${root}?page=vservices'>Virtual services</a>");
+                                       echo '<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
+                                       echo '<tr><th>VS</th><th>Descritpion</th></tr>';
+                                       foreach ($what as $vs)
+                                       {
+                                               echo "<tr class=row_${order}><td class=tdleft><a href='${root}?page=vservice&id=${vs['id']}'>";
+                                               echo buildVServiceName ($vs);
+                                               echo "</a></td><td>${vs['name']}</td></tr>";
+                                               $order = $nextorder[$order];
+                                       }
+                                       echo '</table>';
+                                       finishPortlet();
+                                       break;
                        }
        }
 }
@@ -2816,18 +2892,19 @@ function renderAccountsEditForm ()
                echo "</td>";
                echo "<td><input type=text name=username value='${account['user_name']}' size=16></td>";
                echo "<td><input type=text name=realname value='${account['user_realname']}' size=24></td>";
-               echo "<td><input type=password name=password value='${account['user_password_hash']}' size=64></td>";
-               echo "<td><input type='submit' value='OK'></td>";
-               echo "</form></tr>\n";
+               echo "<td><input type=password name=password value='${account['user_password_hash']}' size=64></td><td>";
+               printImageHREF ('save', 'Save changes', TRUE);
+               echo "</td></form></tr>\n";
        }
        echo "<form action='${root}process.php' method=post><tr>";
        echo "<input type=hidden name=op value=createAccount>\n";
        echo "<input type=hidden name=page value='${pageno}'>\n";
        echo "<input type=hidden name=tab value='${tabno}'>\n";
-       echo "<td colspan=2><input type=text size=16 name=username tabindex=100></td>\n";
+       echo "<td>&nbsp;</td><td><input type=text size=16 name=username tabindex=100></td>\n";
        echo "<td><input type=text size=24 name=realname tabindex=101></td>";
-       echo "<td><input type=password size=64 name=password tabindex=102></td>";
-       echo "<td colspan=4><input type=submit value='Create account' tabindex=103></td></tr></form>";
+       echo "<td><input type=password size=64 name=password tabindex=102></td><td>";
+       printImageHREF ('create', 'Add new account', TRUE, 103);
+       echo "</td></tr></form>";
        echo "</table><br>\n";
        finishPortlet();
 }
@@ -2993,22 +3070,7 @@ function renderRackPage ($rack_id)
 
        // Left column with information.
        echo "<td class=pcleft>";
-       startPortlet ('Rack information');
-       echo "<table border=0 cellspacing=0 cellpadding=3 width='100%'>\n";
-       echo "<tr><th width='50%' class=tdright>Rack row:</th><td class=tdleft>${rackData['row_name']}</td></tr>\n";
-       echo "<tr><th width='50%' class=tdright>Name:</th><td class=tdleft>${rackData['name']}</td></tr>\n";
-       echo "<tr><th width='50%' class=tdright>Height:</th><td class=tdleft>${rackData['height']}</td></tr>\n";
-       echo "<tr><th width='50%' class=tdright>Utilization:</th><td class=tdleft>";
-       renderProgressBar (getRSUforRack ($rackData));
-       echo "</td></tr>\n";
-       echo "<tr><th width='50%' class=tdright>Objects:</th><td class=tdleft>";
-       echo getObjectCount ($rackData);
-       echo "</td></tr>\n";
-       printTagTRs();
-       if (!empty ($rackData['comment']))
-               echo "<tr><th width='50%' class=tdright>Comment:</th><td class=tdleft>${rackData['comment']}</td></tr>\n";
-       echo '</table>';
-       finishPortlet();
+       renderRackInfoPortlet ($rackData);
        echo '</td>';
 
        // Right column with rendered rack.
@@ -3326,35 +3388,53 @@ function printImageHREF ($tag, $title = '', $do_input = FALSE, $tabindex = 0)
        $image['useup']['height'] = 16;
        $image['blockuser'] = $image['reserve'];
        $image['unblockuser'] = $image['useup'];
-       $image['link']['path'] = 'pix/link.png';
-       $image['link']['width'] = 24;
-       $image['link']['height'] = 24;
-       $image['unlink']['path'] = 'pix/unlink.png';
-       $image['unlink']['width'] = 24;
-       $image['unlink']['height'] = 24;
-       $image['add']['path'] = 'pix/greenplus.png';
+       $image['link']['path'] = 'pix/tango-network-wired.png';
+       $image['link']['width'] = 16;
+       $image['link']['height'] = 16;
+       $image['unlink']['path'] = 'pix/tango-edit-clear.png';
+       $image['unlink']['width'] = 16;
+       $image['unlink']['height'] = 16;
+       $image['add']['path'] = 'pix/tango-list-add.png';
        $image['add']['width'] = 16;
        $image['add']['height'] = 16;
-       $image['delete']['path'] = 'pix/delete_s.gif';
+       $image['delete']['path'] = 'pix/tango-list-remove.png';
        $image['delete']['width'] = 16;
        $image['delete']['height'] = 16;
-       $image['nodelete']['path'] = 'pix/delete_g.png';
+       $image['nodelete']['path'] = 'pix/tango-list-remove-shadow.png';
        $image['nodelete']['width'] = 16;
        $image['nodelete']['height'] = 16;
        $image['grant'] = $image['add'];
        $image['revoke'] = $image['delete'];
-       $image['inservice']['path'] = 'pix/go.png';
+       $image['inservice']['path'] = 'pix/tango-emblem-system.png';
        $image['inservice']['width'] = 16;
        $image['inservice']['height'] = 16;
-       $image['notinservice']['path'] = 'pix/stop.png';
+       $image['notinservice']['path'] = 'pix/tango-dialog-error.png';
        $image['notinservice']['width'] = 16;
        $image['notinservice']['height'] = 16;
-       $image['find']['path'] = 'pix/find.png';
+       $image['find']['path'] = 'pix/tango-system-search.png';
        $image['find']['width'] = 16;
        $image['find']['height'] = 16;
        $image['spacer']['path'] = 'pix/pixel.png';
        $image['spacer']['width'] = 16;
        $image['spacer']['height'] = 16;
+       $image['next']['path'] = 'pix/tango-go-next.png';
+       $image['next']['width'] = 32;
+       $image['next']['height'] = 32;
+       $image['prev']['path'] = 'pix/tango-go-previous.png';
+       $image['prev']['width'] = 32;
+       $image['prev']['height'] = 32;
+       $image['clear']['path'] = 'pix/tango-edit-clear.png';
+       $image['clear']['width'] = 16;
+       $image['clear']['height'] = 16;
+       $image['save']['path'] = 'pix/tango-document-save.png';
+       $image['save']['width'] = 16;
+       $image['save']['height'] = 16;
+       $image['SAVE']['path'] = 'pix/tango-document-save-big.png';
+       $image['SAVE']['width'] = 32;
+       $image['SAVE']['height'] = 32;
+       $image['create']['path'] = 'pix/tango-document-new.png';
+       $image['create']['width'] = 16;
+       $image['create']['height'] = 16;
        if (!isset ($image[$tag]))
                $tag = 'error';
        $img = $image[$tag];
@@ -3408,11 +3488,11 @@ function renderReportSummary ()
 
        echo "</td><td class=pcright>\n";
 
-       startPortlet ("Here be dragons");
-       dragon();
-       dragon();
-       dragon();
-       echo 'ASCII art &copy; Daniel C. Au';
+       startPortlet ("Tag popularity");
+       echo "<table>\n";
+       foreach (getTagStats() as $header => $data)
+               echo "<tr><th class=tdright>${header}:</th><td class=tdleft>${data}</td></tr>\n";
+       echo "</table>\n";
        finishPortlet();
        echo "</td></tr>\n";
        echo "</table>\n";
@@ -3420,6 +3500,7 @@ function renderReportSummary ()
 
 function dragon ()
 {
+       startPortlet ('Here be dragons');
 ?>
 <div class=dragon><pre><font color="#00ff33">
                  \||/
@@ -3436,6 +3517,7 @@ function dragon ()
 
 </font></pre></div>
 <?php
+       finishPortlet();
 }
 
 function renderUIConfig ()
@@ -3597,6 +3679,11 @@ function renderVLANMembership ($object_id = 0)
                        echo "<input type=hidden name=vlanid_${portno} value='trunk'>";
                        echo "<select disabled multiple='multiple' size=1><option>TRUNK</option></select>";
                }
+               elseif ($port['vlanid'] == 'routed')
+               {
+                       echo "<input type=hidden name=vlanid_${portno} value='routed'>";
+                       echo "<select disabled multiple='multiple' size=1><option>ROUTED</option></select>";
+               }
                else
                {
                        echo "<select name=vlanid_${portno}>";
@@ -3712,12 +3799,12 @@ function renderSNMPPortFinder ($object_id = 0)
                $hwtype[527] = 210;
                $ciscomodel[561] = 'WS-C2970G-24TS (24 Ethernet 10/100/1000 ports and 4 10/100/1000 SFP uplinks)';
                $hwtype[561] = 115;
-#              $ciscomodel[633] = 'WS-C3560-24TS (24 Ethernet 10/100 ports and 2 10/100/1000 SFP uplinks)';
-#              $hwtype[633] = 169;
+               $ciscomodel[633] = 'WS-C3560-24TS (24 Ethernet 10/100 ports and 2 10/100/1000 SFP uplinks)';
+               $hwtype[633] = 169;
                $ciscomodel[634] = 'WS-C3560-48TS (48 Ethernet 10/100 ports and 4 10/100/1000 SFP uplinks)';
                $hwtype[634] = 170;
-#              $ciscomodel[563] = 'WS-C3560-24PS (24 Ethernet 10/100 POE ports and 2 10/100/1000 SFP uplinks)';
-#              $hwtype[563] = 171;
+               $ciscomodel[563] = 'WS-C3560-24PS (24 Ethernet 10/100 POE ports and 2 10/100/1000 SFP uplinks)';
+               $hwtype[563] = 171;
                $ciscomodel[564] = 'WS-C3560-48PS (48 Ethernet 10/100 POE ports and 4 10/100/1000 SFP uplinks)';
                $hwtype[564] = 172;
                $ciscomodel[614] = 'WS-C3560G-24PS (24 Ethernet 10/100/1000 POE ports and 4 10/100/1000 SFP uplinks)';
@@ -3884,6 +3971,27 @@ function renderSNMPPortFinder ($object_id = 0)
                                                $log[] = array ('code' => 'error', 'message' => 'Failed to add port ' . $label . ': ' . $error);
                                }
                                break;
+                       case '563': // WS-C3560-24PS
+                       case '633': // WS-C3560-24TS
+                               for ($i = 1; $i <= 24; $i++)
+                               {
+                                       $label = "${i}X";
+                                       $error = commitAddPort ($object_id, 'fa0/' . $i, 19, $label, $ifList2["FastEthernet0/${i}"]['phyad']);
+                                       if ($error == '')
+                                               $newports++;
+                                       else
+                                               $log[] = array ('code' => 'error', 'message' => 'Failed to add port ' . $label . ': ' . $error);
+                               }
+                               for ($i = 1; $i <= 2; $i++)
+                               {
+                                       $label = "${i}";
+                                       $error = commitAddPort ($object_id, 'gi0/' . $i, 24, $label, $ifList2["GigabitEthernet0/${i}"]['phyad']);
+                                       if ($error == '')
+                                               $newports++;
+                                       else
+                                               $log[] = array ('code' => 'error', 'message' => 'Failed to add port ' . $label . ': ' . $error);
+                               }
+                               break;
                        case '564': // WS-C3560-48PS
                        case '634': // WS-C3560-48TS
                                for ($i = 1; $i <= 48; $i++)
@@ -4132,7 +4240,7 @@ function renderVirtualService ()
        echo "<tr><th width='50%' class=tdright>Protocol:</th><td class=tdleft>${vsinfo['proto']}</td></tr>\n";
        echo "<tr><th width='50%' class=tdright>Virtual IP address:</th><td class=tdleft><a href='${root}?page=ipaddress&tab=default&ip=${vsinfo['vip']}'>${vsinfo['vip']}</a></td></tr>\n";
        echo "<tr><th width='50%' class=tdright>Virtual port:</th><td class=tdleft>${vsinfo['vport']}</td></tr>\n";
-       printTagTRs();
+       printTagTRs ("${root}?page=vservices&");
        if (!empty ($vsinfo['vsconfig']))
        {
                echo "<tr><th width='50%' class=tdright>VS configuration:</th><td class=tdleft>&nbsp;</td></tr>\n";
@@ -4235,9 +4343,9 @@ function renderRSPoolServerForm ($pool_id = 0)
                        printImageHREF ('delete', 'Delete this real server');
                        echo "</td><td><input type=text name=rsip value='${rs['rsip']}'></td>";
                        echo "<td><input type=text name=rsport size=5 value='${rs['rsport']}'></td>";
-                       echo "<td><textarea name=rsconfig>${rs['rsconfig']}</textarea></td>";
-                       echo "<td><input type=submit value='OK'></td>";
-                       echo "</tr></form>\n";
+                       echo "<td><textarea name=rsconfig>${rs['rsconfig']}</textarea></td><td>";
+                       printImageHREF ('save', 'Save changes', TRUE);
+                       echo "</td></tr></form>\n";
                        $order = $nextorder[$order];
                }
                echo "</table>\n";
@@ -4257,16 +4365,16 @@ function renderRSPoolServerForm ($pool_id = 0)
                printImageHREF ('inservice', 'in service');
        else
                printImageHREF ('notinservice', 'NOT in service');
-       echo "</td><td><input type=text name=remoteip id=remoteip tabindex=1>";
+       echo "</td><td><input type=text name=remoteip id=remoteip tabindex=1> ";
        echo "<a href='javascript:;' onclick='window.open(\"${root}find_object_ip_helper.php\", \"findobjectip\", \"height=700, width=400, location=no, menubar=no, resizable=yes, scrollbars=no, status=no, titlebar=no, toolbar=no\");'>";
        printImageHREF ('find', 'pick address');
        echo "</a></td>";
        $default_port = getConfigVar ('DEFAULT_SLB_RS_PORT');
        if ($default_port == 0)
                $default_port = '';
-       echo "<td><input type=text name=rsport size=5 value='${default_port}'  tabindex=2></td>";
-       echo "<td><input type=submit value='OK' tabindex=3></tr>\n";
-       echo "<tr><th colspan=4>configuration</th></tr>";
+       echo "<td><input type=text name=rsport size=5 value='${default_port}'  tabindex=2></td><td>";
+       printImageHREF ('add', 'Add new', TRUE, 3);
+       echo "</td></tr><tr><th colspan=4>configuration</th></tr>";
        echo "<tr><td colspan=4><textarea name=rsconfig rows=10 cols=80 tabindex=4></textarea></td></tr>";
        echo "</form></table>\n";
        finishPortlet();
@@ -4333,8 +4441,9 @@ function renderRSPoolLBForm ($pool_id = 0)
                                if (!empty ($vsinfo['name']))
                                        echo " (${vsinfo['name']})";
                                echo "<td><textarea name=vsconfig>${configs['vsconfig']}</textarea></td>";
-                               echo "<td><textarea name=rsconfig>${configs['rsconfig']}</textarea></td>";
-                               echo "<td><input type=submit value=OK></td></tr></form>\n";
+                               echo "<td><textarea name=rsconfig>${configs['rsconfig']}</textarea></td><td>";
+                               printImageHREF ('save', 'Save changes', TRUE);
+                               echo "</td></tr></form>\n";
                                $order = $nextorder[$order];
                        }
                echo "</table>\n";
@@ -4357,7 +4466,9 @@ function renderRSPoolLBForm ($pool_id = 0)
        }
        echo "</select> ";
        printSelect ($vs_list, 'vs_id');
-       echo "</td><td><input type=submit value=OK tabindex=2></td></tr>\n";
+       echo "</td><td>";
+       printImageHREF ('add', 'Configure LB', TRUE, 2);
+       echo "</td></tr>\n";
        echo "<tr><th>VS config</th><td colspan=2><textarea name=vsconfig rows=10 cols=80></textarea></td></tr>";
        echo "<tr><th>RS config</th><td colspan=2><textarea name=rsconfig rows=10 cols=80></textarea></td></tr>";
        echo "</form></table>\n";
@@ -4390,7 +4501,7 @@ function renderRSPool ($pool_id = 0)
                echo "<tr><th width='50%' class=tdright>Pool name:</th><td class=tdleft>${poolInfo['name']}</td></tr>\n";
        echo "<tr><th width='50%' class=tdright>Real servers:</th><td class=tdleft>" . count ($poolInfo['rslist']) . "</td></tr>\n";
        echo "<tr><th width='50%' class=tdright>Load balancers:</th><td class=tdleft>" . count ($poolInfo['lblist']) . "</td></tr>\n";
-       printTagTRs();
+       printTagTRs ("${root}?page=rspools&");
        echo "</table>";
        finishPortlet();
 
@@ -4435,13 +4546,18 @@ function renderRSPool ($pool_id = 0)
 function renderVSList ()
 {
        global $root, $nextorder;
-       $vslist = getVSList();
+       $tagfilter = getTagFilter();
+       $vslist = getVSList ($tagfilter);
+       echo "<table border=0 class=objectview>\n";
+       echo "<tr><td class=pcleft>";
+
+       startPortlet ('Virtual services');
        echo "<table class=widetable border=0 cellpadding=10 cellspacing=0 align=center>\n";
        echo "<tr><th>endpoint</th><th>name</th><th>VS configuration</th><th>RS configuration</th></tr>";
        $order = 'odd';
        foreach ($vslist as $vsid => $vsinfo)
        {
-               echo "<tr valign=top class=row_${order}><td class=tdleft><a href='${root}?page=vservice&id=${vsid}'>" . buildVServiceName ($vsinfo);
+               echo "<tr align=left valign=top class=row_${order}><td class=tdleft><a href='${root}?page=vservice&id=${vsid}'>" . buildVServiceName ($vsinfo);
                echo "</a></td>";
                echo "<td class=tdleft>${vsinfo['name']}</td>";
                echo "<td><pre>${vsinfo['vsconfig']}</pre></td>";
@@ -4450,6 +4566,10 @@ function renderVSList ()
                $order = $nextorder[$order];
        }
        echo "</table>";
+       finishPortlet();
+       echo '</td><td class=pcright>';
+       renderTagFilterPortlet ($tagfilter, 'ipv4vs');
+       echo '</td></tr></table>';
 }
 
 function renderVSListEditForm ()
@@ -4522,12 +4642,16 @@ function renderVSListEditForm ()
 function renderRSPoolList ()
 {
        global $root, $nextorder;
-       $pool_list = getRSPoolList();
+       $tagfilter = getTagFilter();
+       $pool_list = getRSPoolList ($tagfilter);
        if ($pool_list === NULL)
        {
                showError ('getRSPoolList() failed', __FUNCTION__);
                return;
        }
+       echo "<table border=0 class=objectview>\n";
+       echo "<tr><td class=pcleft>";
+       startPortlet ('RS pools');
        echo "<table class=widetable border=0 cellpadding=10 cellspacing=0 align=center>\n";
        echo "<tr><th>refcnt</th><th>name</th><th>VS configuration</th><th>RS configuration</th></tr>";
        $order = 'odd';
@@ -4542,6 +4666,10 @@ function renderRSPoolList ()
                $order = $nextorder[$order];
        }
        echo "</table>";
+       finishPortlet ();
+       echo '</td><td class=pcright>';
+       renderTagFilterPortlet ($tagfilter, 'ipv4rspool');
+       echo '</td></tr></table>';
 }
 
 function editRSPools ()
@@ -4560,13 +4688,13 @@ function editRSPools ()
                echo "<input type=hidden name=page value=${pageno}>\n";
                echo "<input type=hidden name=tab value=${tabno}>\n";
                echo "<input type=hidden name=op value=upd>\n";
-               echo "<input type=hidden name=id value=${pool_id}>\n";
+               echo "<input type=hidden name=pool_id value=${pool_id}>\n";
                echo "<tr valign=top class=row_${order}><td>";
                if ($pool_info['refcnt'])
                        printImageHREF ('nodelete', 'RS pool is used ' . $pool_info['refcnt'] . ' time(s)');
                else
                {
-                       echo "<a href='${root}process.php?page=${pageno}&tab=${tabno}&op=del&id=${pool_id}'>";
+                       echo "<a href='${root}process.php?page=${pageno}&tab=${tabno}&op=del&pool_id=${pool_id}'>";
                        printImageHREF ('delete', 'delete real server pool');
                        echo '</a>';
                }
@@ -4674,8 +4802,9 @@ function renderRSPoolRSInServiceForm ($pool_id = 0)
                echo "</tr>";
                $recno++;
        }
-       echo "<tr><td colspan=4 align=center><input type=submit value=OK tabindex=${recno}></td></tr>";
-       echo "</table>\n</form>";
+       echo "<tr><td colspan=4 align=center>";
+       printImageHREF ('SAVE', 'Save changes', TRUE, $recno);
+       echo "</td></tr></table>\n</form>";
 }
 
 function renderLivePTR ($id = 0)
@@ -4764,7 +4893,7 @@ function renderLivePTR ($id = 0)
                        $cnt_mismatch++;
                }
                echo "><td class='tdleft";
-               if ($addr['reserved'] == 'yes')
+               if ($addr['reserved'] == 'yes' or count ($range['addrlist'][$ip]['references']))
                        echo ' trbusy';
                echo "'><a href='${root}?page=ipaddress&ip=${straddr}'>${straddr}</a></td>";
                echo "<td class=tdleft>${addr['name']}</td><td class=tdleft>${ptrname}</td><td>";
@@ -4819,17 +4948,15 @@ function renderAutoPortsForm ($object_id = 0)
        echo "</table>";
 }
 
-function renderTagRowForViewer ($taginfo, $realm, $level = 0)
+function renderTagRowForViewer ($taginfo, $level = 0)
 {
        echo '<tr><td align=left>';
        for ($i = 0; $i < $level; $i++)
                printImageHREF ('spacer');
        echo $taginfo['tag'];
-       if ($realm != '' && isset ($taginfo['refcnt'][$realm]))
-               echo ' (' . $taginfo['refcnt'][$realm] . ')';
        echo "</td></tr>\n";
        foreach ($taginfo['kids'] as $kid)
-               renderTagRowForViewer ($kid, $realm, $level + 1);
+               renderTagRowForViewer ($kid, $level + 1);
 }
 
 function renderTagRowForCloud ($taginfo, $realm, $level = 0)
@@ -4849,15 +4976,32 @@ function renderTagRowForCloud ($taginfo, $realm, $level = 0)
 
 function renderTagRowForEditor ($taginfo, $level = 0)
 {
-       global $root, $pageno, $tabno;
+       global $root, $pageno, $tabno, $taglist;
        echo '<tr><td>';
-       // FIXME: check [supplied] refcnt for each tag
-       echo "<a href='${root}process.php?page=${pageno}&tab=${tabno}&op=destroyTag&id=${taginfo['id']}'>";
-       printImageHREF ('delete', 'Destroy tag');
-       echo "</a></td>\n<td>";
-       for ($i = 0; $i < $level; $i++)
-               printImageHREF ('spacer');
-       echo $taginfo['tag'] . "</td><td>&nbsp;</td></tr>\n";
+       $nrefs = 0;
+       foreach ($taginfo['refcnt'] as $part)
+               $nrefs += $part;
+       if ($nrefs > 0 or count ($taginfo['kids']) > 0)
+               printImageHREF ('nodelete', "${nrefs} references, " . count ($taginfo['kids']) . ' sub-tags');
+       else
+       {
+               echo "<a href='${root}process.php?page=${pageno}&tab=${tabno}&op=destroyTag&tag_id=${taginfo['id']}'>";
+               printImageHREF ('delete', 'Delete tag');
+               echo "</a>";
+       }
+       echo "</td>\n<td>";
+       echo "<form method=post action='${root}process.php?page=${pageno}&tab=${tabno}&op=updateTag'>";
+       echo "<input type=hidden name=tag_id value=${taginfo['id']}><input type=text name=tag_name ";
+       echo "value='${taginfo['tag']}'></td><td><select name=parent_id>";
+       echo "<option value=0>-- NONE --</option>\n";
+       foreach ($taglist as $tlinfo)
+       {
+               echo "<option value=${tlinfo['id']}" . ($tlinfo['id'] == $taginfo['parent_id'] ? ' selected' : '');
+               echo ">${tlinfo['tag']}</option>";
+       }
+       echo "</select></td><td>";
+       printImageHREF ('save', 'Save changes', TRUE);
+       echo "</form></td></tr>\n";
        foreach ($taginfo['kids'] as $kid)
                renderTagRowForEditor ($kid, $level + 1);
 }
@@ -4865,14 +5009,14 @@ function renderTagRowForEditor ($taginfo, $level = 0)
 function renderTagTree ()
 {
        global $tagtree;
-       echo '<table>';
+       echo '<center><table>';
        foreach ($tagtree as $taginfo)
        {
                echo '<tr>';
-               renderTagRowForViewer ($taginfo, $realm);
+               renderTagRowForViewer ($taginfo);
                echo "</tr>\n";
        }
-       echo '</table>';
+       echo '</table></center>';
 }
 
 function renderTagCloud ($realm = '')
@@ -4892,8 +5036,10 @@ function renderTagTreeEditor ()
 {
        global $root, $pageno, $tabno, $taglist, $tagtree;
        showMessageOrError();
+       echo "<table class=objview border=0 width='100%'><tr><td class=pcleft>";
+       startPortlet ('tag tree');
        echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
-       echo "<tr><th>&nbsp;</th><th>tag</th><th>&nbsp;</th></tr>\n";
+       echo "<tr><th>&nbsp;</th><th>tag</th><th>parent</th><th>&nbsp;</th></tr>\n";
        foreach ($tagtree as $taginfo)
        {
                renderTagRowForEditor ($taginfo, TRUE);
@@ -4903,14 +5049,41 @@ function renderTagTreeEditor ()
        echo "<input type=hidden name=tab value='${tabno}'>";
        echo "<input type=hidden name=op value='createTag'>";
        echo "<tr><td>";
-       printImageHREF ('grant', 'Create tag', TRUE);
-       echo '</td><td><input type=text name=tagname> under <select name=parent_id>';
+       printImageHREF ('grant', 'Create tag', TRUE, 102);
+       echo '</td><td><input type=text name=tag_name tabindex=100></td><td><select name=parent_id tabindex=101>';
        echo "<option value=0>-- NONE --</option>\n";
        foreach ($taglist as $taginfo)
                echo "<option value=${taginfo['id']}>${taginfo['tag']}</option>";
        echo "</select></td><td>&nbsp;</td></tr>";
        echo "</form>\n";
        echo '</table>';
+       finishPortlet();
+
+       echo "</td><td><td class=pcright>";
+
+       startPortlet ('fallen leaves');
+       echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
+       echo "<tr><th>tag</th><th>parent</th><th>&nbsp;</th></tr>\n";
+       foreach (getOrphanedTags() as $taginfo)
+       {
+               echo '<tr><td>';
+               echo "<form method=post action='${root}process.php?page=${pageno}&tab=${tabno}&op=updateTag'>";
+               echo "<input type=hidden name=tag_id value=${taginfo['id']}>";
+               echo "<input type=hidden name=tag_name value=${taginfo['tag']}>";
+               echo "${taginfo['tag']}</td><td><select name=parent_id>";
+               echo "<option value=0>-- NONE --</option>\n";
+               foreach ($taglist as $tlinfo)
+               {
+                       echo "<option value=${tlinfo['id']}" . ($tlinfo['id'] == $taglist[$taginfo['id']]['parent_id'] ? ' selected' : '');
+                       echo ">${tlinfo['tag']}</option>";
+               }
+               echo "</select></td><td>";
+               printImageHREF ('save', 'Save changes', TRUE);
+               echo "</form></td></tr>\n";
+       }
+       echo '</table>';
+       finishPortlet();
+       echo "</td></tr></table>";
 }
 
 // Output a sequence of OPTION elements, selecting those, which are present on the
@@ -4975,7 +5148,7 @@ function renderIPv4VSTags ($id)
 
 function renderIPv4RSPoolTags ($id)
 {
-       renderEntityTags ('ip4rspool', 'id', $id);
+       renderEntityTags ('ip4rspool', 'pool_id', $id);
 }
 
 function renderEntityTags ($entity_realm = '', $bypass_name, $entity_id = 0)
@@ -4998,22 +5171,23 @@ function renderEntityTags ($entity_realm = '', $bypass_name, $entity_id = 0)
        foreach ($tagtree as $taginfo)
                renderTagOption ($taginfo);
        echo '</select><br>';
-       echo "<input type=submit value='Save'></form>\n";
+       printImageHREF ('SAVE', 'Save changes', TRUE);
+       echo "</form>\n";
        finishPortlet();
 }
 
-function printTagTRs()
+function printTagTRs ($baseurl = '')
 {
        global $expl_tags, $impl_tags, $auto_tags;
        if (getConfigVar ('SHOW_EXPLICIT_TAGS') == 'yes' and count ($expl_tags))
        {
                echo "<tr><th width='50%' class=tag_list_th>Explicit tags:</th><td class=tdleft>";
-               echo serializeTags ($expl_tags) . "</td></tr>\n";
+               echo serializeTags ($expl_tags, $baseurl) . "</td></tr>\n";
        }
        if (getConfigVar ('SHOW_IMPLICIT_TAGS') == 'yes' and count ($impl_tags))
        {
                echo "<tr><th width='50%' class=tag_list_th>Implicit tags:</th><td class=tdleft>";
-               echo serializeTags ($impl_tags) . "</td></tr>\n";
+               echo serializeTags ($impl_tags, $baseurl) . "</td></tr>\n";
        }
        if (getConfigVar ('SHOW_AUTOMATIC_TAGS') == 'yes' and count ($auto_tags))
        {
@@ -5022,40 +5196,31 @@ function printTagTRs()
        }
 }
 
-function renderTagFilterPortlet ($tagfilter, $realm)
+// Output a portlet, with currently selected tags and prepare a form for update.
+function renderTagFilterPortlet ($tagfilter, $realm, $bypass_name = '', $bypass_value = '')
 {
        global $pageno, $tabno, $taglist, $tagtree;
+       $objectivetags = getObjectiveTagTree ($tagtree, $realm);
        startPortlet ('Tag filter');
-       if (!count ($taglist))
+       if (!count ($objectivetags))
        {
-               echo "No tags defined";
+               echo "None defined for current realm.<br>";
                return;
        }
        echo "<form method=get>\n";
        echo "<input type=hidden name=page value=${pageno}>\n";
        echo "<input type=hidden name=tab value=${tabno}>\n";
+       if ($bypass_name != '')
+               echo "<input type=hidden name=${bypass_name} value='${bypass_value}'>\n";
        echo '<select name=tagfilter[] multiple>';
-       foreach (getObjectiveTagTree ($tagtree, $realm) as $taginfo)
+       foreach ($objectivetags as $taginfo)
                renderTagOptionForFilter ($taginfo, $tagfilter, $realm);
        echo '</select><br>';
        echo "<input type=submit value='Apply'></form>\n";
        finishPortlet();
 }
 
-function renderTagFilterSelect ($tagfilter, $realm)
-{
-       global $taglist, $tagtree;
-       if (!count ($taglist))
-       {
-               echo "No tags defined";
-               return;
-       }
-       echo '<select name=tagfilter[] multiple>';
-       foreach (getObjectiveTagTree ($tagtree, $realm) as $taginfo)
-               renderTagOptionForFilter ($taginfo, $tagfilter, $realm);
-       echo '</select><br>';
-}
-
+// Dump all tags in a single SELECT element.
 function renderTagSelect ()
 {
        global $taglist, $tagtree;
@@ -5070,4 +5235,36 @@ function renderTagSelect ()
        echo '</select><br>';
 }
 
+function renderTagRollerForRow ()
+{
+       dragon();
+}
+
+function renderObjectSLB ()
+{
+       dragon();
+}
+
+function renderEditRSPool ()
+{
+       dragon();
+}
+
+function renderEditVService ()
+{
+       dragon();
+}
+
+function renderEditLBsForVService ()
+{
+       dragon();
+}
+
+function dump ($var)
+{
+       echo '<pre>';
+       print_r ($var);
+       echo '</pre>';
+}
+
 ?>