r1871 + implement IPv4 VS tag filtering
[racktables] / inc / interface.php
index a2adc7763868b917b394236c8cd550053186288e..8f028d3d246dafa33298cc1d794fb6086c497c93 100644 (file)
@@ -45,8 +45,8 @@ function renderIndex ()
                                                <?php printImageHREF ('reports'); ?></a></h1>
                                        </td>
                                        <td>
-                                               <h1><a href='<?php echo $root; ?>?page=help'>Help<br>
-                                               <?php printImageHREF ('help'); ?></a></h1>
+                                               <h1><a href='<?php echo $root; ?>?page=ipv4slb'>IPv4 SLB<br>
+                                               <?php printImageHREF ('ipv4slb'); ?></a></h1>
                                        </td>
                                </tr>
                        </table>
@@ -169,14 +169,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
@@ -244,11 +253,11 @@ function renderNewObjectForm ()
        if (isset ($_REQUEST['got_data']))
        {
                $log = array();
-               assertUIntArg ('object_type_id');
-               assertStringArg ('object_name', TRUE);
-               assertStringArg ('object_label', TRUE);
-               assertStringArg ('object_barcode', TRUE);
-               assertStringArg ('object_asset_no', TRUE);
+               assertUIntArg ('object_type_id', __FUNCTION__);
+               assertStringArg ('object_name', __FUNCTION__, TRUE);
+               assertStringArg ('object_label', __FUNCTION__, TRUE);
+               assertStringArg ('object_barcode', __FUNCTION__, TRUE);
+               assertStringArg ('object_asset_no', __FUNCTION__, TRUE);
                $type_id = $_REQUEST['object_type_id'];
                $name = $_REQUEST['object_name'];
                $label = $_REQUEST['object_label'];
@@ -269,7 +278,9 @@ function renderNewObjectForm ()
        echo "<input type=hidden name=tab value=${tabno}>";
        echo '<table border=0 align=center>';
        echo "<tr><th class=tdright>Type:</th><td class=tdleft>";
-       printSelect (getObjectTypeList(), 'object_type_id');
+       $typelist = getObjectTypeList();
+       $typelist[0] = 'select type...';
+       printSelect ($typelist, 'object_type_id', getConfigVar ('DEFAULT_OBJECT_TYPE'));
        echo "</td></tr>\n";
        echo "<tr><th class=tdright>Common name:</th><td class=tdleft><input type=text name=object_name></td></tr>\n";
        echo "<tr><th class=tdright>Visible label:</th><td class=tdleft><input type=text name=object_label></td></tr>\n";
@@ -288,9 +299,9 @@ function renderNewRackForm ($row_id)
        if (isset ($_REQUEST['got_data']))
        {
                $log = array();
-               assertStringArg ('rack_name');
-               assertUIntArg ('rack_height');
-               assertStringArg ('rack_comment', TRUE);
+               assertStringArg ('rack_name', __FUNCTION__);
+               assertUIntArg ('rack_height', __FUNCTION__);
+               assertStringArg ('rack_comment', __FUNCTION__, TRUE);
                $name = $_REQUEST['rack_name'];
                $height = $_REQUEST['rack_height'];
                $comment = $_REQUEST['rack_comment'];
@@ -328,11 +339,11 @@ function renderEditObjectForm ($object_id)
        {
                $log = array();
                // object_id is already verified by page handler
-               assertUIntArg ('object_type_id');
-               assertStringArg ('object_name', TRUE);
-               assertStringArg ('object_label', TRUE);
-               assertStringArg ('object_barcode', TRUE);
-               assertStringArg ('object_asset_no', TRUE);
+               assertUIntArg ('object_type_id', __FUNCTION__);
+               assertStringArg ('object_name', __FUNCTION__, TRUE);
+               assertStringArg ('object_label', __FUNCTION__, TRUE);
+               assertStringArg ('object_barcode', __FUNCTION__, TRUE);
+               assertStringArg ('object_asset_no', __FUNCTION__, TRUE);
                $type_id = $_REQUEST['object_type_id'];
                if (isset ($_REQUEST['object_has_problems']) and $_REQUEST['object_has_problems'] == 'on')
                        $has_problems = 'yes';
@@ -367,7 +378,7 @@ function renderEditObjectForm ($object_id)
 
        echo '<td class=pcleft>';
        startPortlet ('Static attributes');
-       echo '<form>';
+       echo '<form method=post>';
        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}>";
@@ -408,9 +419,16 @@ function renderEditObjectForm ($object_id)
        foreach ($values as $record)
        {
                echo "<input type=hidden name=${i}_attr_id value=${record['id']}>";
-               echo "<tr><td><a href=${root}process.php?page=${pageno}&tab=${tabno}&op=del&object_id=${object_id}&attr_id=${record['id']}>";
-               printImageHREF ('delete', 'Delete value');
-               echo "</a></td>";
+               echo '<tr><td>';
+               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 ('clear', 'Clear value');
+                       echo '</a>';
+               }
+               else
+                       echo '&nbsp;';
+               echo '</td>';
                echo "<td class=tdright>${record['name']}:</td><td class=tdleft>";
                switch ($record['type'])
                {
@@ -452,10 +470,10 @@ function renderEditRackForm ($rack_id)
        if (isset ($_REQUEST['got_data']))
        {
                $log = array();
-               assertUIntArg ('rack_row_id');
-               assertUIntArg ('rack_height');
-               assertStringArg ('rack_name');
-               assertStringArg ('rack_comment', TRUE);
+               assertUIntArg ('rack_row_id', __FUNCTION__);
+               assertUIntArg ('rack_height', __FUNCTION__);
+               assertStringArg ('rack_name', __FUNCTION__);
+               assertStringArg ('rack_comment', __FUNCTION__, TRUE);
                $row_id = $_REQUEST['rack_row_id'];
                $height = $_REQUEST['rack_height'];
                $name = $_REQUEST['rack_name'];
@@ -502,13 +520,62 @@ function renderEditRackForm ($rack_id)
 // This is a helper for creators and editors.
 function printSelect ($rowList, $select_name, $selected_id = 1)
 {
-       echo "<select name=${select_name}>";
+       // First collect all data for OPTGROUPs, then ouput it and dump
+       // the rest of records as is.
+       $optgroup = array();
+       $other = array();
        foreach ($rowList as $dict_key => $dict_value)
        {
-               echo "<option value=${dict_key}";
-               if ($dict_key == $selected_id)
-                       echo ' selected';
-               echo ">${dict_value}</option>";
+               if (strpos ($dict_value, '^') !== FALSE)
+               {
+                       $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;
+       }
+       echo "<select name=${select_name}>";
+       if (!count ($optgroup))
+       {
+               foreach ($other as $dict_key => $dict_value)
+               {
+                       echo "<option value=${dict_key}";
+                       if ($dict_key == $selected_id)
+                               echo ' selected';
+                       echo ">${dict_value}</option>";
+               }
+       }
+       else
+       {
+               foreach ($optgroup as $groupname => $groupdata)
+               {
+                       echo "<optgroup label='${groupname}'>";
+                       foreach ($groupdata as $dict_key => $dict_value)
+                       {
+                               echo "<option value=${dict_key}";
+                               if ($dict_key == $selected_id)
+                                       echo ' selected';
+                               echo ">${dict_value}</option>";
+                       }
+                       echo "</optgroup>\n";
+               }
+               if (count ($other))
+               {
+                       echo "<optgroup label='other'>\n";
+                       foreach ($other as $dict_key => $dict_value)
+                       {
+                               echo "<option value=${dict_key}";
+                               if ($dict_key == $selected_id)
+                                       echo ' selected';
+                               echo ">${dict_value}</option>";
+                       }
+                       echo "</optgroup>\n";
+               }
        }
        echo "</select>";
 }
@@ -563,7 +630,7 @@ function renderGridForm ($rack_id = 0, $filter, $header, $submit, $state1, $stat
        echo "<table class=rack border=0 cellspacing=0 cellpadding=1>\n";
        echo "<tr><th width='10%'>&nbsp;</th><th width='20%'>Front</th>";
        echo "<th width='50%'>Interior</th><th width='20%'>Back</th></tr>\n";
-       echo "<form action='${root}?'>\n";
+       echo "<form method=post action='${root}?'>\n";
        echo "<input type=hidden name=page value=${pageno}>\n";
        echo "<input type=hidden name=tab value=${tabno}>\n";
        echo "<input type=hidden name=rack_id value=${rack_id}>\n";
@@ -649,16 +716,17 @@ 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";
-       foreach (getAttrValues ($object_id) as $record)
+       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();
        echo "</table><br>\n";
        finishPortlet();
 
        if (!empty ($info['comment']))
        {
                startPortlet ('Comment');
-               echo '<div class=commentblock>' . $info['comment'] . '</div>';
+               echo '<div class=commentblock>' . string_insert_hrefs ($info['comment']) . '</div>';
                finishPortlet ();
        }
 
@@ -672,7 +740,7 @@ function renderRackObject ($object_id = 0)
                        $hl_port_id = 0;
                        if (isset ($_REQUEST['hl_port_id']))
                        {
-                               assertUIntArg ('hl_port_id');
+                               assertUIntArg ('hl_port_id', __FUNCTION__);
                                $hl_port_id = $_REQUEST['hl_port_id'];
                        }
                        echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
@@ -723,15 +791,15 @@ function renderRackObject ($object_id = 0)
                        $notvirtnum = countRefsOfType($addr['references'], 'virtual', 'neq');
 
                        if ($addr['address_reserved']=='yes')
-                               $class='trwarning';
+                               $class='trerror';
                        elseif ($addr['type']!='virtual' && $regnum>0)
-                               $class='trwarning';
+                               $class='trerror';
                        elseif ($addr['type']=='regular' && $sharednum>0)
-                               $class='trwarning';
+                               $class='trerror';
                        else 
                                $class='';
 
-                       echo "<tr class='$class'><td>${addr['name']}</td><td><a href='${root}?page=ipaddress&ip=${addr['ip']}'>${addr['ip']}</a></td><td class='description'>$address_name</td><td>\n";
+                       echo "<tr class='$class'><td class=tdleft>${addr['name']}</td><td class=tdleft><a href='${root}?page=ipaddress&ip=${addr['ip']}'>${addr['ip']}</a></td><td class='description'>$address_name</td><td class=tdleft>\n";
 
                        if ($addr['address_reserved']=='yes')
                                echo "<b>Reserved;</b> ";
@@ -803,7 +871,7 @@ function renderRackObject ($object_id = 0)
 
                        foreach ($forwards['out'] as $pf)
                        {
-                               $class='trwarning';
+                               $class='trerrorg';
                                $name='';
                                foreach ($addresses as $addr)
                                        if ($addr['ip'] == $pf['localip'])
@@ -870,7 +938,7 @@ function renderRackObject ($object_id = 0)
                        echo '</a>';
                        if (!empty ($info['name']))
                                echo " (${info['name']})";
-                       echo "</td><td class=tdleft><a href='${root}?page=rspool&id=${info['pool_id']}'>";
+                       echo "</td><td class=tdleft><a href='${root}?page=rspool&pool_id=${info['pool_id']}'>";
                        echo (empty ($info['pool_name']) ? 'ANONYMOUS' : $info['pool_name']);
                        echo '</a></td><td class=tdleft>' . $info['rscount'] . '</td>';
                        echo "<td class=tdleft><pre>${info['vsconfig']}</pre></td>";
@@ -980,8 +1048,9 @@ function renderPortsForObject ($object_id = 0)
                echo "<td><input type='submit' value='OK'></td>";
                echo "</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";
@@ -999,7 +1068,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();
 
@@ -1061,11 +1130,11 @@ function renderNetworkForObject ($object_id=0)
                $notvirtnum = countRefsOfType($addr['references'], 'virtual', 'neq');
 
                if ($addr['address_reserved']=='yes')
-                       $class='trwarning';
+                       $class='trerror';
                elseif ($addr['type']!='virtual' && $regnum>0)
-                       $class='trwarning';
+                       $class='trerror';
                elseif ($addr['type']=='regular' && $sharednum>0)
-                       $class='trwarning';
+                       $class='trerror';
                else 
                        $class='';
 
@@ -1278,6 +1347,22 @@ function renderRackSpaceForObject ($object_id = 0)
        echo "<td class=pcleft height='1%'>";
        startPortlet ('Racks');
        $allRacksData = getRacksForRow();
+       if (count ($allRacksData) <= getConfigVar ('RACK_PRESELECT_THRESHOLD'))
+       {
+               foreach (array_keys ($allRacksData) as $rack_id)
+               {
+                       $rackData = getRackData ($rack_id);
+                       if ($rackData == NULL)
+                       {
+                               showError ('getRackData() failed', __FUNCTION__);
+                               return NULL;
+                       }
+                       $workingRacksData[$rack_id] = $rackData;
+               }
+               foreach ($workingRacksData as &$rackData)
+                       applyObjectMountMask ($rackData, $object_id);
+               unset ($rackData);
+       }
        renderRackMultiSelect ('rackmulti[]', $allRacksData, array_keys ($workingRacksData));
        echo "<br>";
        echo "<br>";
@@ -1420,6 +1505,8 @@ function renderProblematicObjectsPortlet ()
 function renderObjectGroupSummary ()
 {
        global $root;
+       $tagfilter = isset ($_REQUEST['tagfilter']) ? $_REQUEST['tagfilter'] : array();
+       $tagfilter = complementByKids ($tagfilter);
        $summary = getObjectGroupInfo();
        if ($summary === NULL)
        {
@@ -1431,21 +1518,139 @@ function renderObjectGroupSummary ()
 
        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;
+       echo "<table border=0 class=objectview>\n";
+       echo "<tr><td class=pcleft width='50%'>";
+       startPortlet ('View all by type');
+       $groupInfo = getObjectGroupInfo();
+       if ($groupInfo === NULL)
+       {
+               showError ('getObjectGroupInfo() failed', __FUNCTION__);
+               return;
+       }
+       if (count ($groupInfo) == 0)
+               echo "No objects exist in DB";
+       else
+       {
+               echo '<div align=left><ul>';
+               foreach ($groupInfo as $gi)
+                       echo "<li><a href='${root}?page=objgroup&group_id=${gi['id']}'>${gi['name']}</a> (${gi['count']})</li>";
+               echo '</ul></div>';
+       }
+       finishPortlet();
+
+       echo '</td><td class=pcright>';
+
+       startPortlet ('View all by tag');
+       if (count ($taglist) == 0)
+               echo "No tags exist in DB";
+       else
+               renderTagCloud ('object');
+       finishPortlet();
+       echo "</td></tr></table>\n";
+}
+
+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";
+       echo "<table border=0 class=objectview>\n";
+       echo "<tr><td class=pcleft width='25%'>";
+       startPortlet ('change type');
+       $groupInfo = getObjectGroupInfo();
+       if ($groupInfo === NULL)
+       {
+               showError ('getObjectGroupInfo() failed', __FUNCTION__);
+               return;
+       }
+       if (count ($groupInfo) == 0)
+               echo "No objects exist in DB";
+       else
+       {
+               echo '<div align=left><ul>';
+               foreach ($groupInfo as $gi)
+               {
+                       echo "<li><a href='${root}?page=${pageno}&group_id=${gi['id']}&tagfilter[]=${tagfilter_str}'>";
+                       if ($gi['id'] == $group_id)
+                               echo '<strong>';
+                       echo "${gi['name']}</a>";
+                       if ($gi['id'] == $group_id)
+                               echo '</strong>';
+                       echo " (${gi['count']})";
+                       if ($gi['id'] == $group_id)
+                               echo ' &larr;';
+                       echo "</li>";
+               }
+               echo '</ul></div>';
+       }
+       finishPortlet();
+
+       echo '</td><td class=pcleft>';
+
+       startPortlet ('Objects');
+       $objects = getObjectList ($group_id, $tagfilter);
+       if ($objects === NULL)
+       {
+               showError ('getObjectList() failed', __FUNCTION__);
+               return;
+       }
+       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';
+       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><td class=pcright width='25%'>";
+
+       startPortlet ('change filter');
+       renderTagFilterSelect ($tagfilter, 'object');
+       echo "<input type=submit value='Apply'>\n";
+       finishPortlet();
        echo "</td></tr></table>\n";
+       echo '</form>';
 }
 
-function renderObjectGroup ($group_id = 0)
+function renderObjectGroup_old ()
 {
        global $root;
+       assertUIntArg ('group_id', __FUNCTION__);
+       $group_id = $_REQUEST['group_id'];
        $summary = getObjectGroupInfo();
        if ($summary == NULL)
        {
@@ -1644,11 +1849,15 @@ function renderRackspaceHistory ()
 function renderAddressspace ()
 {
        global $root, $page;
-       echo '<table border=0 class=objectview cellspacing=0 cellpadding=0><tr><td class=pcleft>';
+
+       echo "<table border=0 class=objectview>\n";
+       echo "<tr><td class=pcleft>";
 
        startPortlet ('Subnets');
        echo "<table class='widetable' border=0 cellpadding=10 cellspacing=0 align='center'>\n";
-       $addrspaceList = getAddressspaceList();
+       $tagfilter = isset ($_REQUEST['tagfilter']) ? $_REQUEST['tagfilter'] : array();
+       $tagfilter = complementByKids ($tagfilter);
+       $addrspaceList = getAddressspaceList ($tagfilter);
        echo "<tr><th>Subnet</th><th>Name</th><th>Utilization</th></tr>";
        foreach ($addrspaceList as $iprange)
        {
@@ -1662,8 +1871,14 @@ function renderAddressspace ()
        }
        echo "</table>\n";
        finishPortlet();
+       echo '</td><td class=pcright>';
+       renderTagFilterPortlet ($tagfilter, 'ipv4net');
+       echo "</td></tr></table>\n";
+}
 
-       echo "</td>\n<td class=pcright>";
+function renderIPv4SLB ()
+{
+       global $root, $page, $nextorder;
 
        startPortlet ('SLB configuration');
        echo "<table border=0 width='100%'><tr>";
@@ -1690,6 +1905,7 @@ function renderAddressspace ()
                echo 'none configured';
        else
        {
+               $order = 'odd';
                echo "<table class='widetable' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
                echo "<tr><th>VS&nbsp;&darr; LB&nbsp;&rarr;</th>";
                foreach ($lblist as $lb_object_id)
@@ -1697,7 +1913,7 @@ function renderAddressspace ()
                echo "</tr>\n";
                foreach ($summary as $vsid => $vsdata)
                {
-                       echo "<tr><td class=tdleft><a href='$root?page=vservice&tab=default&id=${vsid}'>";
+                       echo "<tr class=row_${order}><td class=tdleft><a href='$root?page=vservice&tab=default&id=${vsid}'>";
                        echo buildVServiceName ($vsdata);
                        echo '</a>';
                        if (!empty ($vsdata['name']))
@@ -1711,19 +1927,18 @@ function renderAddressspace ()
                                else
                                {
                                        echo $vsdata['lblist'][$lb_object_id]['size'];
-                                       echo " (<a href='${root}?page=rspool&id=";
+                                       echo " (<a href='${root}?page=rspool&pool_id=";
                                        echo $vsdata['lblist'][$lb_object_id]['id'] . "'>";
                                        echo $vsdata['lblist'][$lb_object_id]['name'] . '</a>)';
                                }
                                echo '</td>';
                        }
                        echo "</tr>\n";
+                       $order = $nextorder[$order];
                }
                echo "</table>\n";
        }
        finishPortlet ();
-
-       echo '</td></tr></table>';
 }
 
 function renderAddNewRange ()
@@ -1731,20 +1946,25 @@ function renderAddNewRange ()
        global $root, $pageno, $tabno;
        showMessageOrError();
 
-       echo "<center><h2>Add New</h2></center>\n";
+       startPortlet ("Add New");
        echo "<table class='widetable' border=0 cellpadding=10 align='center'>\n";
-       echo "<tr><th>Address range</th><th>Name</th><th>C&gt;*</th><th>&nbsp;</th></tr>\n";
+       echo "<tr><th>Address range</th><th>Name</th><th>connected network</th><th>assign tags</th><th>&nbsp;</th></tr>\n";
        echo "<form name='add_new_range' action='process.php'>\n";
        echo "<input type=hidden name=op value=addRange>\n";
        echo "<input type=hidden name=page value='${pageno}'>\n";
        echo "<input type=hidden name=tab value='${tabno}'>\n";
-       echo "<tr><td class='tdcenter'><input type=text name='range' size=18 class='live-validate' tabindex=1></td>\n";
+       echo "<tr valign=top><td class='tdcenter'><input type=text name='range' size=18 class='live-validate' tabindex=1></td>\n";
        echo "<td class='tdcenter'><input type=text name='name' size='20' tabindex=2></td>\n";
        echo "<td class='tdcenter'><input type=checkbox name='is_bcast' tabindex=3 checked></td>\n";
-       echo "<td class='tdcenter'><input type=submit value='Add a new range' tabindex=4></td></tr>\n";
+       echo "<td>\n";
+       renderTagSelect();
+       echo "</td>\n";
+       echo "<td class='tdcenter'><input type=submit value='Add a new range' tabindex=4></td>\n";
+       echo "</td></tr>\n";
        echo "</form></table><br><br>\n";
+       finishPortlet();
 
-       echo "<center><h2>Manage Existing</h2></center>\n";
+       startPortlet ("Manage Existing");
        echo "<table class='widetable' border=0 cellpadding=10 align='center'>\n";
        $addrspaceList = getAddressspaceList();
        echo "<tr><th>&nbsp;</th><th>Address range</th><th>Name</th><th>Utilization</th></tr>";
@@ -1753,36 +1973,130 @@ function renderAddNewRange ()
                $range = getIPRange($iprange['id']);
                $usedips = count ($range['addrlist']);
                $totalips = ($iprange['ip_bin'] | $iprange['mask_bin_inv']) - ($iprange['ip_bin'] & $iprange['mask_bin']) + 1;
-               echo "<tr>";
+               echo "<tr><td>";
                if ($usedips == 0)
                {
-                       echo "<td><a href='process.php?op=delRange&page=${pageno}&tab=${tabno}&id=${iprange['id']}'>";
+                       echo "<a href='process.php?op=delRange&page=${pageno}&tab=${tabno}&id=${iprange['id']}'>";
                        printImageHREF ('delete', 'Delete this IP range');
-                       echo "</a></td>\n";
+                       echo "</a>";
                }
                else
-                       echo "<td>&nbsp</td>";
-               echo "<td><a href='${root}?page=iprange&id=${iprange['id']}'>${iprange['ip']}/${iprange['mask']}</a></td><td>${iprange['name']}</td><td class=tdleft>";
+                       printImageHREF ('nodelete', 'There are IP addresses allocated or reserved');
+               echo "</td>\n<td class=tdleft><a href='${root}?page=iprange&id=${iprange['id']}'>";
+               echo "${iprange['ip']}/${iprange['mask']}</a></td><td class=tdleft>${iprange['name']}";
+               echo "</td><td class=tdleft>";
                renderProgressBar ($usedips / $totalips);
                echo " ${usedips}/${totalips}";
                echo "</td></tr>";
        }
        echo "</table>";
+       finishPortlet();
 }
 
-function renderIPRange ()
+function renderIPRange ($id)
 {
-       global $root;
+       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');
-       $id = $_REQUEST['id'];
        if (isset($_REQUEST['pg']))
                $page = $_REQUEST['pg'];
        else
                $page=0;
 
        $range = getIPRange($id);
-       echo "<center><h1>${range['ip']}/${range['mask']}</h1><h2>${range['name']}</h2></center>\n";
+       echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
+       echo "<tr><td colspan=2 align=center><h1>${range['ip']}/${range['mask']}</h1><h2>${range['name']}</h2></td></tr>\n";
+
+       echo "<tr><td class=pcleft width='50%'>";
+       startPortlet ('summary');
+       $total = ($range['ip_bin'] | $range['mask_bin_inv']) - ($range['ip_bin'] & $range['mask_bin']) + 1;
+       $used = count ($range['addrlist']);
+       echo "<table border=0 cellspacing=0 cellpadding=3 width='100%'>\n";
+       echo "<tr><th width='50%' class=tdright>Utilization:</th><td class=tdleft>";
+       renderProgressBar ($used/$total);
+       echo "&nbsp;${used}/${total}</td></tr>\n";
+       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();
+       echo "</table><br>\n";
+       finishPortlet();
+       echo "</td>\n";
 
+       echo "<td class=pcright>";
+       startPortlet ('details');
        $startip = $range['ip_bin'] & $range['mask_bin'];
        $endip = $range['ip_bin'] | $range['mask_bin_inv'];
        $realstartip = $startip;
@@ -1802,7 +2116,7 @@ function renderIPRange ()
                if ($i == $page)
                        echo "<b>$i</b> ";
                else
-                       echo "<a href='${root}?page=iprange&id=$id&pg=$i'>$i</a> ";
+                       echo "<a href='${root}?page=${pageno}&tab=${tabno}&id=$id&pg=$i'>$i</a> ";
        }
        echo "</center>";
 
@@ -1817,27 +2131,62 @@ function renderIPRange ()
                        $numshared = countRefsOfType($range['addrlist'][$ip]['references'], 'shared', 'eq');
                        $numreg = countRefsOfType($range['addrlist'][$ip]['references'], 'regular', 'eq');
                        $numvirt = countRefsOfType($range['addrlist'][$ip]['references'], 'virtual', 'eq');
+                       $numlb = count ($range['addrlist'][$ip]['lbrefs']);
+                       $numrs = count ($range['addrlist'][$ip]['rsrefs']);
                        
                        $addr = $range['addrlist'][$ip];
                        if ( ($numshared > 0 && $numreg > 0) || $numreg > 1 )
-                               echo "<tr class='trwarning'>";
-                       elseif ( $addr['reserved'] == 'yes' and $numshared+$numreg+$numvirt > 0)
-                               echo "<tr class='trwarning'>";
+                               echo "<tr class='trerror'>";
+                       elseif ( $addr['reserved'] == 'yes' and $numshared+$numreg+$numvirt+$numlb+$numrs > 0)
+                               echo "<tr class='trerror'>";
                        elseif ( $addr['reserved'] == 'yes')
                                echo "<tr class='trbusy'>";
-                       elseif ( $numshared > 0 || $numreg > 0)
+                       elseif ( $numshared > 0 || $numreg > 0 || $numlb > 0 || $numrs > 0)
                                echo "<tr class='trbusy'>";
                        else
                                echo "<tr>";
 
                        echo "<td><a href='${root}?page=ipaddress&ip=${addr['ip']}'>${addr['ip']}</a></td><td>${addr['name']}</td><td>";
+                       $delim = '';
+                       $prologue = '';
                        if ( $addr['reserved'] == 'yes')
-                               echo "<b>Reserved;</b> ";
+                       {
+                               echo "<b>Reserved</b> ";
+                               $delim = '; ';
+                       }
                        foreach ($range['addrlist'][$ip]['references'] as $ref)
                        {
-                               echo "<a href='${root}?page=object&object_id=${ref['object_id']}'>";
+                               echo "${delim}<a href='${root}?page=object&object_id=${ref['object_id']}'>";
                                echo $ref['name'] . (empty ($ref['name']) ? '' : '@');
-                               echo "${ref['object_name']}</a>; ";
+                               echo "${ref['object_name']}</a>";
+                               $delim = '; ';
+                       }
+                       if ($delim != '')
+                       {
+                               $delim = '';
+                               $prologue = '<br>';
+                       }
+                       foreach ($range['addrlist'][$ip]['lbrefs'] as $ref)
+                       {
+                               echo $prologue;
+                               $prologue = '';
+                               echo "${delim}<a href='${root}?page=object&object_id=${ref['object_id']}'>";
+                               echo "${ref['object_name']}</a>:<a href='${root}?page=vservice&id=${ref['vs_id']}'>";
+                               echo "${ref['vport']}/${ref['proto']}</a>&rarr;";
+                               $delim = '; ';
+                       }
+                       if ($delim != '')
+                       {
+                               $delim = '';
+                               $prologue = '<br>';
+                       }
+                       foreach ($range['addrlist'][$ip]['rsrefs'] as $ref)
+                       {
+                               echo $prologue;
+                               $prologue = '';
+                               echo "${delim}&rarr;${ref['rsport']}@<a href='${root}?page=rspool&pool_id=${ref['rspool_id']}'>";
+                               echo "${ref['rspool_name']}</a>";
+                               $delim = '; ';
                        }
                        echo "</td></tr>\n";
                }
@@ -1848,13 +2197,13 @@ function renderIPRange ()
        }
 
        echo "</table>";
-       
+       finishPortlet();
+       echo "</td></tr></table>\n";
 }
 
-function renderIPRangeProperties ()
+function renderIPRangeProperties ($id)
 {
        global $pageno, $tabno;
-       $id = $_REQUEST['id'];
        showMessageOrError();
        $range = getIPRange($id);
        echo "<center><h1>${range['ip']}/${range['mask']}</h1></center>\n";
@@ -1881,45 +2230,79 @@ function renderIPAddress ()
 //     echo "<table width='100%' cesspadding=5 cellspacing=0 border=0 align='center'>";
 //     echo "<tr valign='top'><td>";
 
-       startPortlet ('Address assignment');
-       echo "<table class='widetable' cesspadding=5 cellspacing=0 border=0 align='center'>\n";
-       echo "<tr><th>Object name</th><th>Interface name</th><th>Interface type</th></tr>\n";
-
        $numshared = countRefsOfType($address['bonds'], 'shared', 'eq');
        $numreg = countRefsOfType($address['bonds'], 'regular', 'eq');
        $numvirt = countRefsOfType($address['bonds'], 'virtual', 'eq');
 
-       
-       if ( ($numshared > 0 && $numreg > 0) || $numreg > 1 )
-               $class='trwarning';
-       elseif ( $address['reserved'] == 'yes' and $numshared+$numreg+$numvirt > 0)
-               $class='trwarning';
-       else
-               $class='';
+       if ($address['reserved'] == 'yes' or ($numshared + $numreg + $numvirt) > 0)
+       {
+               startPortlet ('Allocation');
+               echo "<table class='widetable' cesspadding=5 cellspacing=0 border=0 align='center'>\n";
+               echo "<tr><th>Object name</th><th>Interface name</th><th>Interface type</th></tr>\n";
+               if ( ($numshared > 0 && $numreg > 0) || $numreg > 1 )
+                       $class='trerror';
+               elseif ( $address['reserved'] == 'yes' and $numshared+$numreg+$numvirt > 0)
+                       $class='trerror';
+               else
+                       $class='';
 
+               if ($address['reserved'] == 'yes')
+                       echo "<tr class='$class'><td colspan='3'><b>RESERVED</b></td></tr>";
+               foreach ($address['bonds'] as $bond)
+               {
+                       echo "<tr class='$class'><td><a href='${root}?page=object&object_id=${bond['object_id']}'>${bond['object_name']}</td><td>${bond['name']}</td><td><b>";
+                       switch ($bond['type'])
+                       {
+                               case 'virtual':
+                                       echo "Virtual";
+                                       break;
+                               case 'shared':
+                                       echo "Shared";
+                                       break;
+                               case 'regular':
+                                       echo "Regular";
+                                       break;
+                       }
+                       echo "</b></td></tr>\n";
+               }
+               echo "</table><br><br>";
+               finishPortlet();
+       }
 
+       if (count ($address['vslist']))
+       {
+               startPortlet ('Virtual services (' . count ($address['vslist']) . ')');
+               echo "<table class='widetable' cesspadding=5 cellspacing=0 border=0 align='center'>\n";
+               echo "<tr><th>VS</th><th>name</th></tr>\n";
+               foreach ($address['vslist'] as $vsinfo)
+               {
+                       echo "<tr><td class=tdleft><a href='${root}?page=vservice&id=${vsinfo['id']}'>";
+                       echo buildVServiceName ($vsinfo) . "</a></td><td class=tdleft>";
+                       echo $vsinfo['name'] . "</td></tr>\n";
+               }
+               echo "</table><br><br>";
+               finishPortlet();
+       }
 
-       if ($address['reserved'] == 'yes')
-               echo "<tr class='$class'><td colspan='3'><b>RESERVED</b></td></tr>";
-       foreach ($address['bonds'] as $bond)
+       if (count ($address['rslist']))
        {
-               echo "<tr class='$class'><td><a href='${root}?page=object&object_id=${bond['object_id']}'>${bond['object_name']}</td><td>${bond['name']}</td><td><b>";
-               switch ($bond['type'])
+               startPortlet ('Real servers (' . count ($address['rslist']) . ')');
+               echo "<table class='widetable' cesspadding=5 cellspacing=0 border=0 align='center'>\n";
+               echo "<tr><th>&nbsp;</th><th>port</th><th>RS pool</th></tr>\n";
+               foreach ($address['rslist'] as $rsinfo)
                {
-                       case 'virtual':
-                               echo "Virtual";
-                               break;
-                       case 'shared':
-                               echo "Shared";
-                               break;
-                       case 'regular':
-                               echo "Regular";
-                               break;
+                       echo "<tr><td>";
+                       if ($rsinfo['inservice'] == 'yes')
+                               printImageHREF ('inservice', 'in service');
+                       else
+                               printImageHREF ('notinservice', 'NOT in service');
+                       echo "</td><td class=tdleft>${rsinfo['rsport']}</td><td class=tdleft><a href='${root}?page=rspool&pool_id=${rsinfo['pool_id']}'>";
+                       echo $rsinfo['poolname'] . "</a></td></tr>\n";
                }
-               echo "</b></td></tr>\n";
+               echo "</table><br><br>";
+               finishPortlet();
        }
-       echo "</table><br><br>";
-       finishPortlet();
+
 
 //     echo "</td><td>";
 //     echo "</td></tr></table>";
@@ -1963,9 +2346,9 @@ function renderIPAddressAssignment ()
 
        
        if ( ($numshared > 0 && $numreg > 0) || $numreg > 1 )
-               $class='trwarning';
+               $class='trerror';
        elseif ( $address['reserved'] == 'yes' and $numshared+$numreg+$numvirt > 0)
-               $class='trwarning';
+               $class='trerror';
        else
                $class='';
 
@@ -2039,7 +2422,7 @@ function renderIPAddressPortForwarding ($object_id=0)
 
        foreach ($forwards['out'] as $pf)
        {
-               $class='trwarning';
+               $class='trerror';
                $name='';
                foreach ($addresses as $addr)
                        if ($addr['ip'] == $pf['localip'])
@@ -2050,7 +2433,9 @@ 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}'><img src='${root}/pix/delete_s.gif' title='Delete port forwarding' border=0 width=16 height=16></a></td>";
+               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}'>";
+               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']}";
                if (!empty ($pf['local_addr_name']))
                        echo ' (' . $pf['local_addr_name'] . ')';
@@ -2082,7 +2467,9 @@ function renderIPAddressPortForwarding ($object_id=0)
 
        echo "</select>:<input type='text' name='localport' size='4' tabindex=2></td>";
        echo "<td><input type='text' name='remoteip' id='remoteip' size='10' tabindex=3>";
-       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\");'><img src='${root}/pix/find.png' title='Find object' border=0 height=16 width=16></a>";
+       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', '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 "</form>";
@@ -2098,7 +2485,9 @@ function renderIPAddressPortForwarding ($object_id=0)
        {
                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}'><img src='${root}/pix/delete_s.gif' title='Delete port forwarding' border=0 width=16 height=16></a></td>";
+               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}'>";
+               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>";
                echo "<td class='description'><a href='${root}?page=object&tab=default&object_id=${pf['object_id']}'>${pf['object_name']}</a>";
                echo "</td><td><a href='${root}?page=ipaddress&tab=default&ip=${pf['remoteip']}'>${pf['remoteip']}</a>:${pf['remoteport']}</td>";
@@ -2123,7 +2512,7 @@ function renderIPAddressPortForwarding ($object_id=0)
 
 function renderAddMultipleObjectsForm ()
 {
-       global $pageno, $tabno, $nextorder;
+       global $root, $pageno, $tabno, $nextorder;
 
        $type_id = array();
        $global_type_id = 0;
@@ -2143,11 +2532,11 @@ function renderAddMultipleObjectsForm ()
                                $log[] = array ('code' => 'error', 'message' => "Submitted form is invalid at line " . $i + 1);
                                break;
                        }
-                       assertUIntArg ("${i}_object_type_id", TRUE);
-                       assertStringArg ("${i}_object_name", TRUE);
-                       assertStringArg ("${i}_object_label", TRUE);
-                       assertStringArg ("${i}_object_asset_no", TRUE);
-                       assertStringArg ("${i}_object_barcode", TRUE);
+                       assertUIntArg ("${i}_object_type_id", __FUNCTION__, TRUE);
+                       assertStringArg ("${i}_object_name", __FUNCTION__, TRUE);
+                       assertStringArg ("${i}_object_label", __FUNCTION__, TRUE);
+                       assertStringArg ("${i}_object_asset_no", __FUNCTION__, TRUE);
+                       assertStringArg ("${i}_object_barcode", __FUNCTION__, TRUE);
                        $type_id[$i] = $_REQUEST["${i}_object_type_id"];
                        // Save user input for possible rendering.
                        $name[$i] = $_REQUEST["${i}_object_name"];
@@ -2167,8 +2556,8 @@ function renderAddMultipleObjectsForm ()
        elseif (isset ($_REQUEST['got_very_fast_data']))
        {
                $keepvalues = TRUE;
-               assertUIntArg ('global_type_id', TRUE);
-               assertStringArg ('namelist', TRUE);
+               assertUIntArg ('global_type_id', __FUNCTION__, TRUE);
+               assertStringArg ('namelist', __FUNCTION__, TRUE);
                $global_type_id = $_REQUEST['global_type_id'];
                if ($global_type_id == 0)
                {
@@ -2205,9 +2594,7 @@ function renderAddMultipleObjectsForm ()
        $typelist[0] = 'select type...';
 
        startPortlet ('Fast way');
-       echo '<form>';
-       echo "<input type=hidden name=page value=${pageno}>";
-       echo "<input type=hidden name=tab value=${tabno}>";
+       echo "<form name=fastform method=post action='${root}?page=${pageno}&tab=${tabno}'>";
        echo '<table border=0 align=center>';
        echo "<tr><th>Object type</th><th>Common name</th><th>Visible label</th><th>Asset tag</th><th>Barcode</th></tr>\n";
        // If a user forgot to select object type on input, we keep his
@@ -2216,6 +2603,7 @@ function renderAddMultipleObjectsForm ()
        for ($i = 0; $i < $max; $i++)
        {
                echo '<tr><td>';
+               // Don't employ DEFAULT_OBJECT_TYPE to avoid creating ghost records for pre-selected empty rows.
                printSelect ($typelist, "${i}_object_type_id", 0);
                echo '</td>';
                echo "<td><input type=text size=30 name=${i}_object_name";
@@ -2241,11 +2629,9 @@ function renderAddMultipleObjectsForm ()
        finishPortlet();
 
        startPortlet ('Very fast way');
-       echo '<form>';
-       echo "<input type=hidden name=page value=${pageno}>";
-       echo "<input type=hidden name=tab value=${tabno}>";
+       echo "<form name=veryfastform method=post action='${root}?page=${pageno}&tab=${tabno}'>";
        echo 'For each line shown below create an object of type ';
-       printSelect ($typelist, "global_type_id", 0);
+       printSelect ($typelist, "global_type_id", getConfigVar ('DEFAULT_OBJECT_TYPE'));
        echo " <input type=submit name=got_very_fast_data value='Go!'><br>\n";
        echo "<textarea name=namelist cols=40 rows=25>\n";
        if ($keepvalues and $global_type_id == 0)
@@ -2275,38 +2661,26 @@ function renderSearchResults ()
                showError ('You are not authorized for viewing information about objects.', __FUNCTION__);
                return;
        }
+       $nhits = 0;
        // If we search for L2 address, we can either find one or find none.
        if
        (
                preg_match ('/^[0-9a-f][0-9a-f]?:[0-9a-f][0-9a-f]?:[0-9a-f][0-9a-f]?:[0-9a-f][0-9a-f]?:[0-9a-f][0-9a-f]?:[0-9a-f][0-9a-f]?$/i', $terms) or
                preg_match ('/^[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]$/i', $terms) or
-               preg_match ('/^[0-9a-f][0-9a-f][0-9a-f][0-9a-f].[0-9a-f][0-9a-f][0-9a-f][0-9a-f].[0-9a-f][0-9a-f][0-9a-f][0-9a-f]$/i', $terms)
+               preg_match ('/^[0-9a-f][0-9a-f][0-9a-f][0-9a-f].[0-9a-f][0-9a-f][0-9a-f][0-9a-f].[0-9a-f][0-9a-f][0-9a-f][0-9a-f]$/i', $terms) or
+               // STP bridge ID: bridge priotity + port MAC address. Cut off first 4 chars and look for MAC address.
+               preg_match ('/^[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]$/i', $terms)
        )
        // Search for L2 address.
        {
+               $terms = substr ($terms, -12);
                $result = searchByl2address ($terms);
                if ($result !== NULL)
                {
-                       echo "<script language='Javascript'>document.location='${root}?page=object";
-                       echo "&hl_port_id=${result['port_id']}";
-                       echo "&object_id=${result['object_id']}';//</script>";
-               }
-               else
-                       echo "L2 address '${terms}' not found!";
-       }
-       elseif (preg_match ('/^[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]$/i', $terms))
-       // STP bridge ID: bridge priotity + port MAC address. Cut off first 4 chars and look for MAC address.
-       {
-               $terms = substr ($terms, 4);
-               $result = searchByl2address ($terms);
-               if ($result !== NULL)
-               {
-                       echo "<script language='Javascript'>document.location='${root}?page=object";
-                       echo "&hl_port_id=${result['port_id']}";
-                       echo "&object_id=${result['object_id']}';//</script>";
+                       $nhits++;
+                       $lasthit = 'port';
+                       $summary['port'][] = $result;
                }
-               else
-                       echo "L2 address '${terms}' not found!";
        }
        elseif (preg_match ('/^[0-9][0-9]?[0-9]?\.[0-9]?[0-9]?[0-9]?\.[0-9][0-9]?[0-9]?\.[0-9]?[0-9]?[0-9]?$/i', $terms))
        // Search for IP address.
@@ -2314,41 +2688,121 @@ function renderSearchResults ()
                $result = getRangeByIp ($terms);
                if ($result !== NULL)
                {
-                       echo "<script language='Javascript'>document.location='${root}?page=ipaddress";
-                       echo "&ip=${terms}";
-                       echo "';//</script>";
+                       $nhits++;
+                       $lasthit = 'ipv4address1';
+                       $summary['ipv4address1'][] = $terms;
                }
-               else
-                       echo "IP address '${terms}' not found!";
-               return;
        }
        else
-       // Search for objects.
+       // Search for objects, addresses, networks, virtual services and RS pools by their description.
        {
-               $objects = getSearchResults ($terms);
-               if (count ($objects) == 1)
+               $tmp = getObjectSearchResults ($terms);
+               if (count ($tmp))
+               {
+                       $nhits += count ($tmp);
+                       $lasthit = 'object';
+                       $summary['object'] = $tmp;
+               }
+               $tmp = getIPv4AddressSearchResult ($terms);
+               if (count ($tmp))
                {
-                       $obj = current ($objects);
-                       echo "<script language='Javascript'>document.location='${root}?page=object&object_id=${obj['id']}';//</script>";
+                       $nhits += count ($tmp);
+                       $lasthit = 'ipv4address2';
+                       $summary['ipv4address2'] = $tmp;
                }
-               elseif (count ($objects) > 1)
+               $tmp = getIPv4PrefixSearchResult ($terms);
+               if (count ($tmp))
                {
-                       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></tr>';
-                       $order = 'odd';
-                       global $nextorder;
-                       foreach ($objects as $obj)
+                       $nhits += count ($tmp);
+                       $lasthit = 'ipv4network';
+                       $summary['ipv4network'] = $tmp;
+               }
+       }
+       if ($nhits == 0)
+               echo "<center><h2>Nothing found for '${terms}'</h2></center>";
+       elseif ($nhits == 1)
+       {
+               $record = current ($summary[$lasthit]);
+               switch ($lasthit)
+               {
+                       case 'port':
+                               echo "<script language='Javascript'>document.location='${root}?page=object";
+                               echo "&hl_port_id=" . $record['port_id'];
+                               echo "&object_id=" . $record['object_id'] . "';//</script>";
+                               break;
+                       case 'ipv4address1':
+                               echo "<script language='Javascript'>document.location='${root}?page=ipaddress";
+                               echo "&ip=${record}";
+                               echo "';//</script>";
+                               break;
+                       case 'ipv4address2':
+                               echo "<script language='Javascript'>document.location='${root}?page=ipaddress";
+                               echo "&ip=${record}";
+                               echo "';//</script>";
+                               break;
+                       case 'ipv4network':
+                               echo "<script language='Javascript'>document.location='${root}?page=iprange";
+                               echo "&id=${record['id']}";
+                               echo "';//</script>";
+                               break;
+                       case 'object':
+                               echo "<script language='Javascript'>document.location='${root}?page=object&object_id=${record['id']}';//</script>";
+                               break;
+               }
+               return;
+       }
+       else
+       {
+               global $nextorder;
+               $order = 'odd';
+               echo "<center><h2>${nhits} result(s) found for '${terms}'</h2></center>";
+               foreach ($summary as $where => $what)
+                       switch ($where)
                        {
-                               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></tr>";
-                               $order = $nextorder[$order];
+                               case 'object':
+                                       startPortlet ('Objects');
+                                       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)
+                                       {
+                                               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></tr>";
+                                               $order = $nextorder[$order];
+                                       }
+                                       echo '</table>';
+                                       finishPortlet();
+                                       break;
+                               case 'ipv4network':
+                                       startPortlet ('IPv4 networks');
+                                       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)
+                                       {
+                                               echo "<tr class=row_${order}><td class=tdleft><a href='${root}?page=iprange&id=${net['id']}'>${net['ip']}";
+                                               echo '/' . $net['mask'] . '</a></td>';
+                                               echo "<td class=tdleft>${net['name']}</td></tr>";
+                                               $order = $nextorder[$order];
+                                       }
+                                       echo '</table>';
+                                       finishPortlet();
+                                       break;
+                               case 'ipv4address2':
+                                       startPortlet ('IPv4 addresses');
+                                       echo '<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
+                                       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&ip=${addr['ip']}'>";
+                                               echo "${addr['ip']}</a></td>";
+                                               echo "<td class=tdleft>${addr['name']}</td></tr>";
+                                               $order = $nextorder[$order];
+                                       }
+                                       echo '</table>';
+                                       finishPortlet();
+                                       break;
                        }
-                       echo '</table>';
-               }
-               else
-                       echo "Object '${terms}' not found!";
        }
 }
 
@@ -2412,7 +2866,9 @@ function renderAccounts ()
        $order = 'odd';
        foreach ($accounts as $user)
        {
-               echo "<tr class=row_${order}><td class=tdleft>${user['user_name']}</td><td class=tdleft>${user['user_realname']}</td></li>";
+               echo "<tr class=row_${order}><td class=tdleft><div title='\$userid_${user['user_id']}'>";
+               echo "${user['user_name']}</div></td>";
+               echo "<td class=tdleft>${user['user_realname']}</td></li>";
                $order = $nextorder[$order];
        }
        echo '</table>';
@@ -2545,11 +3001,21 @@ function renderPermissionsEditForm ()
        finishPortlet();
 }
 
-function renderPortMap ($editable = FALSE)
+function renderPortMapViewer ()
 {
-       global $nextorder, $root, $pageno, $tabno;
-       showMessageOrError();
-       startPortlet ("Port compatibility map");
+       renderPortMap (FALSE);
+}
+
+function renderPortMapEditor ()
+{
+       renderPortMap (TRUE);
+}
+
+function renderPortMap ($editable = FALSE)
+{
+       global $nextorder, $root, $pageno, $tabno;
+       showMessageOrError();
+       startPortlet ("Port compatibility map");
        $ptlist = getPortTypes();
        $pclist = getPortCompat();
        $pctable = buildPortCompatMatrixFromList ($ptlist, $pclist);
@@ -2628,6 +3094,7 @@ function renderRackPage ($rack_id)
        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>';
@@ -2647,7 +3114,7 @@ function renderRackPage ($rack_id)
 function renderDictionary ()
 {
        global $nextorder;
-       $dict = getDict();
+       $dict = getDict (TRUE);
        echo "<br><table class=cooltable border=0 cellpadding=5 cellspacing=0 align=center>\n";
        foreach ($dict as $chapter_no => $chapter)
        {
@@ -2673,7 +3140,7 @@ function renderDictionary ()
                        }
                }
        }
-       echo "</table>\n";
+       echo "</table>\n<br>";
 }
 
 function renderDictionaryEditor ()
@@ -2685,7 +3152,7 @@ function renderDictionaryEditor ()
        foreach ($dict as $chapter_no => $chapter)
        {
                $order = 'odd';
-               echo "<tr><th>Chapter</th><th>refs</th><th>Word</th><th>&nbsp;</th></tr>\n";
+               echo "<tr><th>Chapter</th><th>&nbsp;</th><th>Word</th><th>&nbsp;</th></tr>\n";
                $wc = count ($chapter['word']);
                // One extra span for the new record per each chapter block.
                echo "<tr class=row_${order}><td class=tdleft" . ($wc ? ' rowspan = ' . ($wc + 1) : '');
@@ -2699,6 +3166,7 @@ function renderDictionaryEditor ()
                echo "<td class=tdright><input type=text name=dict_value size=32></td>";
                echo "<td><input type=submit value='Add new'></td>";
                echo '</tr></form>';
+               $order = $nextorder[$order];
                foreach ($chapter['word'] as $key => $value)
                {
                        echo "<form action='${root}process.php' method=post>";
@@ -2710,7 +3178,7 @@ function renderDictionaryEditor ()
                        echo "<tr class=row_${order}><td>";
                        // Prevent deleting words currently used somewhere.
                        if ($chapter['refcnt'][$key])
-                               echo $chapter['refcnt'][$key];
+                               printImageHREF ('nodelete', 'referenced ' . $chapter['refcnt'][$key] . ' time(s)');
                        else
                        {
                                echo "<a href='${root}process.php?page=${pageno}&tab=${tabno}&op=del&chapter_no=${chapter['no']}&dict_key=${key}'>";
@@ -2747,8 +3215,10 @@ function renderChaptersEditor ()
                echo "<input type=hidden name=chapter_no value='${chapter['no']}'>";
                echo '<tr>';
                echo '<td>';
-               if ($sticky or $wordcount > 0)
-                       echo '&nbsp;';
+               if ($sticky)
+                       printImageHREF ('nodelete', 'system chapter');
+               elseif ($wordcount > 0)
+                       printImageHREF ('nodelete', 'contains ' . $wordcount . ' word(s)');
                else
                {
                        echo "<a href='${root}process.php?page=${pageno}&tab=${tabno}&op=del&chapter_no=${chapter['no']}'>";
@@ -2929,15 +3399,15 @@ function printImageHREF ($tag, $title = '', $do_input = FALSE, $tabindex = 0)
        $image['ipv4space']['path'] = 'pix/addressspace.png';
        $image['ipv4space']['width'] = 218;
        $image['ipv4space']['height'] = 200;
+       $image['ipv4slb']['path'] = 'pix/slb.png';
+       $image['ipv4slb']['width'] = 218;
+       $image['ipv4slb']['height'] = 200;
        $image['config']['path'] = 'pix/configuration.png';
        $image['config']['width'] = 218;
        $image['config']['height'] = 200;
        $image['reports']['path'] = 'pix/report.png';
        $image['reports']['width'] = 218;
        $image['reports']['height'] = 200;
-       $image['help']['path'] = 'pix/help.png';
-       $image['help']['width'] = 218;
-       $image['help']['height'] = 200;
        $image['reserve']['path'] = 'pix/stop.png';
        $image['reserve']['width'] = 16;
        $image['reserve']['height'] = 16;
@@ -2946,29 +3416,44 @@ 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']['width'] = 16;
+       $image['nodelete']['height'] = 16;
        $image['grant'] = $image['add'];
        $image['revoke'] = $image['delete'];
-       $image['helphint']['path'] = 'pix/helphint.png';
-       $image['helphint']['width'] = 24;
-       $image['helphint']['height'] = 24;
-       $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/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;
        if (!isset ($image[$tag]))
                $tag = 'error';
        $img = $image[$tag];
@@ -3004,9 +3489,19 @@ function renderReportSummary ()
        startPortlet ("Dictionary/objects stats");
        echo "<table>\n";
        foreach (getDictStats() as $header => $data)
-       {
-               echo "<tr><th>${header}:</th><td>${data}</td></tr>\n";
-       }
+               echo "<tr><th class=tdright>${header}:</th><td class=tdleft>${data}</td></tr>\n";
+       echo "</table>\n";
+       finishPortlet();
+       startPortlet ('IPv4 stats');
+       echo "<table>\n";
+       foreach (getIPv4Stats() as $header => $data)
+               echo "<tr><th class=tdright>${header}:</th><td class=tdleft>${data}</td></tr>\n";
+       echo "</table>\n";
+       finishPortlet();
+       startPortlet ('Rackspace stats');
+       echo "<table>\n";
+       foreach (getRackspaceStats() as $header => $data)
+               echo "<tr><th class=tdright>${header}:</th><td class=tdleft>${data}</td></tr>\n";
        echo "</table>\n";
        finishPortlet();
 
@@ -3118,7 +3613,7 @@ function renderVLANMembership ($object_id = 0)
                // included. The gateway is expected to filter unnecessary changes silently
                // and to provide a list of responses with either error or success message
                // for each of the rest.
-               assertUIntArg ('portcount');
+               assertUIntArg ('portcount', __FUNCTION__);
                $nports = $_REQUEST['portcount'];
                $prefix = 'set ';
                $log = array();
@@ -3130,7 +3625,7 @@ function renderVLANMembership ($object_id = 0)
                                !isset ($_REQUEST['vlanid_' . $i]) ||
                                $_REQUEST['portname_' . $i] != $portlist[$i]['portname']
                        )
-                               $log[] = array ('code' => 'error', 'message' => "Ignoring mailformed record #${i} in form submit");
+                               $log[] = array ('code' => 'error', 'message' => "Ignoring malformed record #${i} in form submit");
                        elseif
                        (
                                $_REQUEST['vlanid_' . $i] == $portlist[$i]['vlanid'] ||
@@ -3267,7 +3762,8 @@ function renderVLANMembership ($object_id = 0)
                        foreach ($portdata as $vlanid => $addrgroup)
                                foreach ($addrgroup as $addr)
                                {
-                                       echo "<tr class=row_${order}><td>$portname</td><td>$vlanid</td><td>$addr</td></tr>\n";
+                                       echo "<tr class=row_${order}><td class=tdleft>$portname</td><td class=tdleft>$vlanid</td>";
+                                       echo "<td class=tdleft>$addr</td></tr>\n";
                                        $order = $nextorder[$order];
                                }
                echo '</table>';
@@ -3315,12 +3811,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)';
@@ -3343,7 +3839,7 @@ function renderSNMPPortFinder ($object_id = 0)
                $hwtype[626] = 147;
                $ciscomodel[659] = 'WS-C4948-10GE (48 Ethernet 10/100/1000 ports and 2 10Gb X2 uplinks)';
                $hwtype[659] = 377;
-               assertStringArg ('community');
+               assertStringArg ('community', __FUNCTION__);
                $community = $_REQUEST['community'];
                $objectInfo = getObjectInfo ($object_id);
                $endpoints = findAllEndpoints ($object_id, $objectInfo['name']);
@@ -3384,14 +3880,23 @@ function renderSNMPPortFinder ($object_id = 0)
                                $log[] = array ('code' => 'error', 'message' => 'Failed settig SW version: ' . $error);
                }
 
-               if (empty ($attrs[4]['value']) and substr ($IOSversion, 0, 4) == '12.2') // switch OS type
-               {
-                       $error = commitUpdateAttrValue ($object_id, 4, 252);
-                       if ($error == TRUE)
-                               $log[] = array ('code' => 'success', 'message' => 'Switch OS type set to Cisco IOS 12.2');
-                       else
-                               $log[] = array ('code' => 'error', 'message' => 'Failed settig Switch OS type: ' . $error);
-               }
+               if (empty ($attrs[4]['value'])) // switch OS type
+                       switch (substr ($IOSversion, 0, 4))
+                       {
+                               case '12.2':
+                                       $error = commitUpdateAttrValue ($object_id, 4, 252);
+                                       break;
+                               case '12.1':
+                                       $error = commitUpdateAttrValue ($object_id, 4, 251);
+                                       break;
+                               case '12.0':
+                                       $error = commitUpdateAttrValue ($object_id, 4, 244);
+                                       break;
+                       }
+               if ($error == TRUE)
+                       $log[] = array ('code' => 'success', 'message' => 'Switch OS type set to Cisco IOS ' . substr ($IOSversion, 0, 4));
+               else
+                       $log[] = array ('code' => 'error', 'message' => 'Failed settig Switch OS type');
 
                $sysObjectID = snmpget ($endpoints[0], $community, 'sysObjectID.0');
                // Transform OID
@@ -3478,6 +3983,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++)
@@ -3609,7 +4135,7 @@ function renderUIResetForm()
        echo "<input type=hidden name=page value=${pageno}>";
        echo "<input type=hidden name=tab value=${tabno}>";
        echo "<input type=hidden name=op value=go>";
-       echo "This button will reset user interface configuration to its defaults (except organization name): ";
+       echo "This button will reset user interface configuration to its defaults (except organization name and auth source): ";
        echo "<input type=submit value='proceed'>";
        echo "</form>";
 }
@@ -3634,18 +4160,9 @@ Dictionary edit page in Configuration section.
 <?php
 }
 
-// Perform substitutions and return resulting string
-function apply_macros ($macros, $subject)
-{
-       $ret = $subject;
-       foreach ($macros as $search => $replace)
-               $ret = str_replace ($search, $replace, $ret);
-       return $ret;
-}
-
 function renderLVSConfig ($object_id = 0)
 {
-       global $pageno, $tabno;
+       global $root, $pageno, $tabno;
        if ($object_id <= 0)
        {
                showError ('Invalid object_id', __FUNCTION__);
@@ -3670,23 +4187,35 @@ function renderLVSConfig ($object_id = 0)
                        '%VNAME%' =>  $vsinfo['vs_name'],
                        '%RSPOOLNAME%' => $vsinfo['pool_name']
                );
-               $vsconfig = apply_macros ($macros, $vsinfo['vs_vsconfig'] . $vsinfo['lb_vsconfig'] . $vsinfo['pool_vsconfig']);
                $newconfig .=  "virtual_server ${vsinfo['vip']} ${vsinfo['vport']} {\n";
-               $newconfig .=  "${vsconfig}\n";
+               $newconfig .=  "\tprotocol ${vsinfo['proto']}\n";
+               $newconfig .= apply_macros
+               (
+                       $macros,
+                       lf_wrap ($vsinfo['vs_vsconfig']) .
+                       lf_wrap ($vsinfo['lb_vsconfig']) .
+                       lf_wrap ($vsinfo['pool_vsconfig'])
+               );
                foreach ($vsinfo['rslist'] as $rs)
                {
                        $macros['%RSIP%'] = $rs['rsip'];
                        $macros['%RSPORT%'] = $rs['rsport'];
-                       $rsconfig = apply_macros ($macros, $vsinfo['vs_rsconfig'] . $vsinfo['lb_rsconfig'] . $vsinfo['pool_rsconfig'] . $rs['rs_rsconfig']);
                        $newconfig .=  "\treal_server ${rs['rsip']} ${rs['rsport']} {\n";
-                       $newconfig .=  "\t${rsconfig}\n";
+                       $newconfig .= apply_macros
+                       (
+                               $macros,
+                               lf_wrap ($vsinfo['vs_rsconfig']) .
+                               lf_wrap ($vsinfo['lb_rsconfig']) .
+                               lf_wrap ($vsinfo['pool_rsconfig']) .
+                               lf_wrap ($rs['rs_rsconfig'])
+                       );
                        $newconfig .=  "\t}\n";
                }
                $newconfig .=  "}\n\n\n";
        }
        if (isset ($_REQUEST['do_activate']))
        {
-               printLog (activateSLBConfig ($object_id, html_entity_decode ($newconfig)));
+               printLog (activateSLBConfig ($object_id, html_entity_decode ($newconfig, ENT_QUOTES, 'UTF-8')));
        }
        echo "<form method=post action=${root}>";
        echo "<input type=hidden name=page value=${pageno}>";
@@ -3701,7 +4230,7 @@ function renderLVSConfig ($object_id = 0)
 
 function renderVirtualService ()
 {
-       assertUIntArg ('id');
+       assertUIntArg ('id', __FUNCTION__);
        $vsid = $_REQUEST['id'];
        global $root, $nextorder;
        if ($vsid <= 0)
@@ -3723,6 +4252,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();
        if (!empty ($vsinfo['vsconfig']))
        {
                echo "<tr><th width='50%' class=tdright>VS configuration:</th><td class=tdleft>&nbsp;</td></tr>\n";
@@ -3747,7 +4277,7 @@ function renderVirtualService ()
                echo "<tr class=row_${order}><td class=tdleft>";
                // Pool info
                echo '<table width=100%>';
-               echo "<tr><td colspan=2><a href='${root}?page=rspool&id=${pool_id}'>";
+               echo "<tr><td colspan=2><a href='${root}?page=rspool&pool_id=${pool_id}'>";
                if (!empty ($poolInfo['name']))
                        echo $poolInfo['name'];
                else
@@ -3806,48 +4336,58 @@ function renderRSPoolServerForm ($pool_id = 0)
        showMessageOrError();
        $poolInfo = getRSPoolInfo ($pool_id);
 
-       $rsc = count ($poolInfo['rslist']);
-       startPortlet ("Manage existing (${rsc})");
-       echo "<table cellspacing=0 cellpadding=5 align=center class=cooltable>\n";
-       echo "<tr><th>&nbsp;</th><th>Address</th><th>Port</th><th>configuration</th><th>&nbsp;</th></tr>\n";
-       $order = 'odd';
-       foreach ($poolInfo['rslist'] as $rsid => $rs)
+       if (($rsc = count ($poolInfo['rslist'])))
        {
-               echo "<form action='${root}process.php'>";
-               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=updRS>";
-               echo "<input type=hidden name=rs_id value='${rsid}'>";
-               echo "<input type=hidden name=id value='${pool_id}'>";
-               echo "<tr valign=top class=row_${order}><td><a href='${root}process.php?page=${pageno}&tab=${tabno}";
-               echo "&op=delRS&pool_id=${pool_id}&id=${rsid}'>";
-               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";
-               $order = $nextorder[$order];
+               startPortlet ("Manage existing (${rsc})");
+               echo "<table cellspacing=0 cellpadding=5 align=center class=cooltable>\n";
+               echo "<tr><th>&nbsp;</th><th>Address</th><th>Port</th><th>configuration</th><th>&nbsp;</th></tr>\n";
+               $order = 'odd';
+               foreach ($poolInfo['rslist'] as $rsid => $rs)
+               {
+                       echo "<form action='${root}process.php'>";
+                       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=updRS>";
+                       echo "<input type=hidden name=rs_id value='${rsid}'>";
+                       echo "<input type=hidden name=pool_id value='${pool_id}'>";
+                       echo "<tr valign=top class=row_${order}><td><a href='${root}process.php?page=${pageno}&tab=${tabno}";
+                       echo "&op=delRS&pool_id=${pool_id}&id=${rsid}'>";
+                       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";
+                       $order = $nextorder[$order];
+               }
+               echo "</table>\n";
+               finishPortlet();
        }
-       echo "</table>\n";
-       finishPortlet();
 
        startPortlet ('Add one');
        echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
-       echo "<tr><th>Address</th><th>Port</th><th>&nbsp;</th></tr>\n";
+       echo "<tr><th>in service</th><th>Address</th><th>Port</th><th>&nbsp;</th></tr>\n";
        echo "<form name=addone action='${root}process.php'>";
        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=addRS>";
-       echo "<input type=hidden name=id value='${pool_id}'>";
-       echo "<tr><td><input type=text name=rsip tabindex=1></td>";
+       echo "<input type=hidden name=pool_id value='${pool_id}'>";
+       echo "<tr><td>";
+       if (getConfigVar ('DEFAULT_IPV4_RS_INSERVICE') == 'yes')
+               printImageHREF ('inservice', 'in service');
+       else
+               printImageHREF ('notinservice', 'NOT in service');
+       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=3>configuration</th></tr>";
-       echo "<tr><td colspan=3><textarea name=rsconfig rows=10 cols=80 tabindex=4></textarea></td></tr>";
+       echo "<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();
 
@@ -3856,12 +4396,22 @@ function renderRSPoolServerForm ($pool_id = 0)
        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=addMany>";
-       echo "<input type=hidden name=id value='${pool_id}'>";
-       echo "<table border=0 align=center>\n<tr><td>Format: ";
-       $formats = array ('ipvs_2' => 'ipvsadm -l -n (address and port)', 'ipvs_3' => 'ipvsadm -l -n (address, port and weight)');
+       echo "<input type=hidden name=pool_id value='${pool_id}'>";
+       echo "<table border=0 align=center>\n<tr><td>";
+       if (getConfigVar ('DEFAULT_IPV4_RS_INSERVICE') == 'yes')
+               printImageHREF ('inservice', 'in service');
+       else
+               printImageHREF ('notinservice', 'NOT in service');
+       echo "</td><td>Format: ";
+       $formats = array
+       (
+               'ipvs_2' => 'ipvsadm -l -n (address and port)',
+               'ipvs_3' => 'ipvsadm -l -n (address, port and weight)',
+               'ssv_2' => 'SSV: &lt;IP address&gt; &lt;port&gt;'
+       );
        printSelect ($formats, 'format');
        echo "</td><td><input type=submit value=Parse></td></tr>\n";
-       echo "<tr><td colspan=2><textarea name=rawtext cols=100 rows=50></textarea></td></tr>\n";
+       echo "<tr><td colspan=3><textarea name=rawtext cols=100 rows=50></textarea></td></tr>\n";
        echo "</table>\n";
        finishPortlet();
 }
@@ -3875,37 +4425,41 @@ function renderRSPoolLBForm ($pool_id = 0)
        $vs_list = array ();
        foreach (getVSList() as $vsid => $vsinfo)
                $vs_list[$vsid] = buildVServiceName ($vsinfo) . (empty ($vsinfo['name']) ? '' : " (${vsinfo['name']})");
-       startPortlet ('Manage existing (' . count ($poolInfo['lblist']) . ')');
-       echo "<table cellspacing=0 cellpadding=5 align=center class=cooltable>\n";
-       echo "<tr><th>&nbsp;</th><th>LB</th><th>VS</th><th>VS config</th><th>RS config</th><th>&nbsp;</th></tr>\n";
-       $order = 'odd';
-       foreach ($poolInfo['lblist'] as $object_id => $vslist)
-               foreach ($vslist as $vs_id => $configs)
-               {
-                       $oi = getObjectInfo ($object_id);
-                       echo "<form action='${root}process.php'>";
-                       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=updLB>";
-                       echo "<input type=hidden name=pool_id value='${pool_id}'>";
-                       echo "<input type=hidden name=vs_id value='${vs_id}'>";
-                       echo "<input type=hidden name=object_id value='${object_id}'>";
-                       echo "<tr valign=top class=row_${order}><td><a href='${root}process.php?page=${pageno}&tab=${tabno}&op=delLB&pool_id=${pool_id}&object_id=${object_id}&vs_id=${vs_id}'>";
-                       printImageHREF ('delete', 'Unconfigure');
-                       echo "</a></td>";
-                       echo "<td class=tdleft><a href='${root}?page=object&object_id=${object_id}'>${oi['dname']}</a></td>";
-                       echo "<td class=tdleft><a href='${root}?page=vservice&id=${vs_id}'>";
-                       $vsinfo = getVServiceInfo ($vs_id);
-                       echo buildVServiceName ($vsinfo) . '</a>';
-                       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";
-                       $order = $nextorder[$order];
-               }
-       echo "</table>\n";
-       finishPortlet();
+
+       if (count ($poolInfo['lblist']))
+       {
+               startPortlet ('Manage existing (' . count ($poolInfo['lblist']) . ')');
+               echo "<table cellspacing=0 cellpadding=5 align=center class=cooltable>\n";
+               echo "<tr><th>&nbsp;</th><th>LB</th><th>VS</th><th>VS config</th><th>RS config</th><th>&nbsp;</th></tr>\n";
+               $order = 'odd';
+               foreach ($poolInfo['lblist'] as $object_id => $vslist)
+                       foreach ($vslist as $vs_id => $configs)
+                       {
+                               $oi = getObjectInfo ($object_id);
+                               echo "<form action='${root}process.php'>";
+                               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=updLB>";
+                               echo "<input type=hidden name=pool_id value='${pool_id}'>";
+                               echo "<input type=hidden name=vs_id value='${vs_id}'>";
+                               echo "<input type=hidden name=object_id value='${object_id}'>";
+                               echo "<tr valign=top class=row_${order}><td><a href='${root}process.php?page=${pageno}&tab=${tabno}&op=delLB&pool_id=${pool_id}&object_id=${object_id}&vs_id=${vs_id}'>";
+                               printImageHREF ('delete', 'Unconfigure');
+                               echo "</a></td>";
+                               echo "<td class=tdleft><a href='${root}?page=object&object_id=${object_id}'>${oi['dname']}</a></td>";
+                               echo "<td class=tdleft><a href='${root}?page=vservice&id=${vs_id}'>";
+                               $vsinfo = getVServiceInfo ($vs_id);
+                               echo buildVServiceName ($vsinfo) . '</a>';
+                               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";
+                               $order = $nextorder[$order];
+                       }
+               echo "</table>\n";
+               finishPortlet();
+       }
 
        startPortlet ('Add new');
        echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
@@ -3956,6 +4510,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();
        echo "</table>";
        finishPortlet();
 
@@ -3999,20 +4554,32 @@ function renderRSPool ($pool_id = 0)
 
 function renderVSList ()
 {
-       global $root;
-       $vslist = getVSList();
+       global $root, $nextorder;
+       $tagfilter = isset ($_REQUEST['tagfilter']) ? $_REQUEST['tagfilter'] : array();
+       $tagfilter = complementByKids ($tagfilter);
+       $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><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>";
                echo "<td><pre>${vsinfo['rsconfig']}</pre></td>";
                echo "</tr>\n";
+               $order = $nextorder[$order];
        }
        echo "</table>";
+       finishPortlet();
+       echo '</td><td class=pcright>';
+       renderTagFilterPortlet ($tagfilter, 'ipv4vs');
+       echo '</td></tr></table>';
 }
 
 function renderVSListEditForm ()
@@ -4035,7 +4602,7 @@ function renderVSListEditForm ()
                echo "<input type=hidden name=id value=${vsid}>\n";
                echo "<tr valign=top class=row_${order}><td>";
                if ($vsinfo['poolcount'])
-                       echo '&nbsp;';
+                       printImageHREF ('nodelete', 'there are ' . $vsinfo['poolcount'] . ' RS pools configured');
                else
                {
                        echo "<a href='${root}process.php?page=${pageno}&tab=${tabno}&op=del&id=${vsid}'>";
@@ -4084,7 +4651,7 @@ function renderVSListEditForm ()
 
 function renderRSPoolList ()
 {
-       global $root;
+       global $root, $nextorder;
        $pool_list = getRSPoolList();
        if ($pool_list === NULL)
        {
@@ -4093,14 +4660,16 @@ function renderRSPoolList ()
        }
        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';
        foreach ($pool_list as $pool_id => $pool_info)
        {
-               echo '<tr><td>' . ($pool_info['refcnt'] ? $pool_info['refcnt'] : '&nbsp;') . '</td>';
-               echo "<td><a href='${root}?page=rspool&id=${pool_id}'>";
+               echo "<tr valign=top class=row_${order}><td>" . ($pool_info['refcnt'] ? $pool_info['refcnt'] : '&nbsp;') . '</td>';
+               echo "<td><a href='${root}?page=rspool&pool_id=${pool_id}'>";
                echo (empty ($pool_info['name']) ? 'ANONYMOUS' : $pool_info['name']) . '</a></td>';
                echo "<td><pre>${pool_info['vsconfig']}</pre></td>";
                echo "<td><pre>${pool_info['rsconfig']}</pre></td>";
                echo "</tr>\n";
+               $order = $nextorder[$order];
        }
        echo "</table>";
 }
@@ -4124,7 +4693,7 @@ function editRSPools ()
                echo "<input type=hidden name=id value=${pool_id}>\n";
                echo "<tr valign=top class=row_${order}><td>";
                if ($pool_info['refcnt'])
-                       echo '&nbsp;';
+                       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}'>";
@@ -4159,14 +4728,21 @@ function editRSPools ()
 
 function renderRealServerList ()
 {
-       global $root;
+       global $root, $nextorder;
        $rslist = getRSList ();
        $pool_list = getRSPoolList ();
        echo "<table class=widetable border=0 cellpadding=10 cellspacing=0 align=center>\n";
        echo "<tr><th>RS pool</th><th>in service</th><th>real IP address</th><th>real port</th><th>RS configuration</th></tr>";
+       $order = 'even';
+       $last_pool_id = 0;
        foreach ($rslist as $rsinfo)
        {
-               echo "<tr valign=top><td><a href='${root}?page=rspool&id=${rsinfo['rspool_id']}'>";
+               if ($last_pool_id != $rsinfo['rspool_id'])
+               {
+                       $order = $nextorder[$order];
+                       $last_pool_id = $rsinfo['rspool_id'];
+               }
+               echo "<tr valign=top class=row_${order}><td><a href='${root}?page=rspool&pool_id=${rsinfo['rspool_id']}'>";
                echo empty ($pool_list[$rsinfo['rspool_id']]['name']) ? 'ANONYMOUS' : $pool_list[$rsinfo['rspool_id']]['name'];
                echo '</a></td><td align=center>';
                if ($rsinfo['inservice'] == 'yes')
@@ -4183,17 +4759,19 @@ function renderRealServerList ()
 
 function renderLBList ()
 {
-       global $root;
+       global $root, $nextorder;
        echo "<table class=widetable border=0 cellpadding=10 cellspacing=0 align=center>\n";
        echo "<tr><th>Object</th><th>RS pools configured</th></tr>";
        $oicache = array();
+       $order = 'odd';
        foreach (getLBList() as $object_id => $poolcount)
        {
                if (!isset ($oicache[$object_id]))
                        $oicache[$object_id] = getObjectInfo ($object_id);
-               echo "<tr valign=top><td><a href='${root}?page=object&object_id=${object_id}'>";
+               echo "<tr valign=top class=row_${order}><td><a href='${root}?page=object&object_id=${object_id}'>";
                echo $oicache[$object_id]['dname'] . '</a></td>';
                echo "<td>${poolcount}</td></tr>";
+               $order = $nextorder[$order];
        }
        echo "</table>";
 }
@@ -4213,7 +4791,7 @@ function renderRSPoolRSInServiceForm ($pool_id = 0)
        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 "<input type=hidden name=rscount value=${rscount}>\n";
        echo "<table class=widetable border=0 cellpadding=10 cellspacing=0 align=center>\n";
        echo "<tr><th>RS address</th><th>RS port</th><th>RS configuration</th><th>in service</th></tr>\n";
@@ -4230,4 +4808,408 @@ function renderRSPoolRSInServiceForm ($pool_id = 0)
        echo "</table>\n</form>";
 }
 
+function renderLivePTR ($id = 0)
+{
+       if ($id == 0)
+       {
+               showError ("Invalid argument", __FUNCTION__);
+               return;
+       }
+       showMessageOrError();
+       if (isset($_REQUEST['pg']))
+               $page = $_REQUEST['pg'];
+       else
+               $page=0;
+       global $root, $pageno, $tabno;
+       $maxperpage = getConfigVar ('IPV4_ADDRS_PER_PAGE');
+       $range = getIPRange ($id);
+       echo "<center><h1>${range['ip']}/${range['mask']}</h1><h2>${range['name']}</h2></center>\n";
+
+       echo "<table class=objview border=0 width='100%'><tr><td class=pcleft>";
+       startPortlet ('current records');
+       $startip = $range['ip_bin'] & $range['mask_bin'];
+       $endip = $range['ip_bin'] | $range['mask_bin_inv'];
+       $realstartip = $startip;
+       $realendip = $endip;
+       $numpages = 0;
+       if ($endip - $startip > $maxperpage)
+       {
+               $numpages = ($endip - $startip) / $maxperpage;
+               $startip = $startip + $page * $maxperpage;
+               $endip = $startip + $maxperpage - 1;
+       }
+       echo "<center>";
+       if ($numpages)
+               echo '<h3>' . long2ip ($startip) . ' ~ ' . long2ip ($endip) . '</h3>';
+       for ($i=0; $i<$numpages; $i++)
+               if ($i == $page)
+                       echo "<b>$i</b> ";
+               else
+                       echo "<a href='${root}?page=${pageno}&tab=${tabno}&id=$id&pg=$i'>$i</a> ";
+       echo "</center>";
+
+       echo "<form method=post action=${root}process.php>";
+       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=import>\n";
+       echo "<input type=hidden name=id value=${id}>\n";
+       echo '<input type=hidden name=addrcount value=' . ($endip - $startip + 1) . ">\n";
+
+       echo "<table class='widetable' border=0 cellspacing=0 cellpadding=5 align='center'>\n";
+       echo "<tr><th>address</th><th>current name</th><th>DNS data</th><th>import</th></tr>\n";
+       $idx = 1;
+       $cnt_match = $cnt_mismatch = $cnt_missing = 0;
+       for ($ip = $startip; $ip <= $endip; $ip++)
+       {
+               // Find the (optional) DB name and the (optional) DNS record, then
+               // compare values and produce a table row depending on the result.
+               $addr = isset ($range['addrlist'][$ip]) ? $range['addrlist'][$ip] : array ('name' => '', 'reserved' => 'no');
+               $straddr = long2ip ($ip);
+               $ptrname = gethostbyaddr ($straddr);
+               if ($ptrname == $straddr)
+                       $ptrname = '';
+               echo "<input type=hidden name=addr_${idx} value=${straddr}>\n";
+               echo "<input type=hidden name=descr_${idx} value=${ptrname}>\n";
+               echo "<input type=hidden name=rsvd_${idx} value=${addr['reserved']}>\n";
+               echo '<tr';
+               $print_cbox = FALSE;
+               if ($addr['name'] == $ptrname)
+               {
+                       if (!empty ($ptrname))
+                       {
+                               echo ' class=trok';
+                               $cnt_match++;
+                       }
+               }
+               elseif (empty ($addr['name']) or empty ($ptrname))
+               {
+                       echo ' class=trwarning';
+                       $print_cbox = TRUE;
+                       $cnt_missing++;
+               }
+               else
+               {
+                       echo ' class=trerror';
+                       $print_cbox = TRUE;
+                       $cnt_mismatch++;
+               }
+               echo "><td class='tdleft";
+               if ($addr['reserved'] == 'yes')
+                       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>";
+               if ($print_cbox)
+                       echo "<input type=checkbox name=import_${idx} tabindex=${idx}>";
+               else
+                       echo '&nbsp;';
+               echo "</td></tr>\n";
+               $idx++;
+       }
+       echo "<tr><td colspan=4 align=center><input type=submit value='Import selected records'></td></tr>";
+       echo "</table>";
+       echo "</form>";
+       finishPortlet();
+
+       echo "</td><td class=pcright>";
+
+       startPortlet ('stats');
+       echo "<table border=0 width='100%' cellspacing=0 cellpadding=2>";
+       echo "<tr class=trok><th class=tdright>Exact matches:</th><td class=tdleft>${cnt_match}</td></tr>\n";
+       echo "<tr class=trwarning><th class=tdright>Missing from DB/DNS:</th><td class=tdleft>${cnt_missing}</td></tr>\n";
+       echo "<tr class=trerror><th class=tdright>Mismatches:</th><td class=tdleft>${cnt_mismatch}</td></tr>\n";
+       echo "</table>\n";
+       finishPortlet();
+
+       echo "</td></tr></table>\n";
+}
+
+function renderAutoPortsForm ($object_id = 0)
+{
+       global $root, $pageno, $tabno;
+       if ($object_id <= 0)
+       {
+               showError ('Invalid object_id', __FUNCTION__);
+               return;
+       }
+       $info = getObjectInfo ($object_id);
+       $ptlist = readChapter ('PortType');
+       echo "<table class='widetable' border=0 cellspacing=0 cellpadding=5 align='center'>\n";
+       echo "<caption>The following ports can be quickly added:</caption>";
+       echo "<tr><th>type</th><th>name</th></tr>";
+       foreach (getAutoPorts ($info['objtype_id']) as $autoport)
+               echo "<tr><td>" . $ptlist[$autoport['type']] . "</td><td>${autoport['name']}</td></tr>";
+       echo "<form method=post action='${root}process.php'>\n";
+       echo "<input type=hidden name=page value=${pageno}>\n";
+       echo "<input type=hidden name=tab value=${tabno}>\n";
+       echo "<input type=hidden name=object_id value=${object_id}>\n";
+       echo "<input type=hidden name=op value=generate>\n";
+       echo "<tr><td colspan=2 align=center>";
+       echo "<input type=submit value='Generate'>";
+       echo "</td></tr>";
+       echo "</table>";
+}
+
+function renderTagRowForViewer ($taginfo, $realm, $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);
+}
+
+function renderTagRowForCloud ($taginfo, $realm, $level = 0)
+{
+       global $root;
+       echo '<tr><td align=left>';
+       for ($i = 0; $i < $level; $i++)
+               printImageHREF ('spacer');
+       echo "<a href='${root}?page=objgroup&group_id=0&tagfilter[]=${taginfo['id']}'>";
+       echo $taginfo['tag'] . '</a>';
+       if (isset ($taginfo['refcnt'][$realm]))
+               echo ' (' . $taginfo['refcnt'][$realm] . ')';
+       echo "</td></tr>\n";
+       foreach ($taginfo['kids'] as $kid)
+               renderTagRowForCloud ($kid, $realm, $level + 1);
+}
+
+function renderTagRowForEditor ($taginfo, $level = 0)
+{
+       global $root, $pageno, $tabno;
+       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";
+       foreach ($taginfo['kids'] as $kid)
+               renderTagRowForEditor ($kid, $level + 1);
+}
+
+function renderTagTree ()
+{
+       global $tagtree;
+       echo '<table>';
+       foreach ($tagtree as $taginfo)
+       {
+               echo '<tr>';
+               renderTagRowForViewer ($taginfo, $realm);
+               echo "</tr>\n";
+       }
+       echo '</table>';
+}
+
+function renderTagCloud ($realm = '')
+{
+       global $taglist, $tagtree;
+       echo '<table>';
+       foreach (getObjectiveTagTree ($tagtree, $realm) as $taginfo)
+       {
+               echo '<tr>';
+               renderTagRowForCloud ($taginfo, $realm);
+               echo "</tr>\n";
+       }
+       echo '</table>';
+}
+
+function renderTagTreeEditor ()
+{
+       global $root, $pageno, $tabno, $taglist, $tagtree;
+       showMessageOrError();
+       echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
+       echo "<tr><th>&nbsp;</th><th>tag</th><th>&nbsp;</th></tr>\n";
+       foreach ($tagtree as $taginfo)
+       {
+               renderTagRowForEditor ($taginfo, TRUE);
+       }
+       echo "<form action='${root}process.php' method=post>";
+       echo "<input type=hidden name=page value='${pageno}'>";
+       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>';
+       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>';
+}
+
+// Output a sequence of OPTION elements, selecting those, which are present on the
+// explicit tags list.
+function renderTagOption ($taginfo, $level = 0)
+{
+       global $expl_tags;
+       $selected = '';
+       foreach ($expl_tags as $etaginfo)
+               if ($taginfo['id'] == $etaginfo['id'])
+               {
+                       $selected = ' selected';
+                       break;
+               }
+       echo '<option value=' . $taginfo['id'] . "${selected}>";
+       for ($i = 0; $i < $level; $i++)
+               echo '-- ';
+       echo $taginfo['tag'] . "</option>\n";
+       foreach ($taginfo['kids'] as $kid)
+               renderTagOption ($kid, $level + 1);
+}
+
+// Idem, but select those, which are shown on the $_REQUEST['tagfiler'] array.
+// Ignore tag ids, which can't be found on the tree.
+function renderTagOptionForFilter ($taginfo, $tagfilter, $realm, $level = 0)
+{
+       echo $level;
+       $selected = '';
+       foreach ($tagfilter as $filter_id)
+               if ($taginfo['id'] == $filter_id)
+               {
+                       $selected = ' selected';
+                       break;
+               }
+       echo '<option value=' . $taginfo['id'] . "${selected}>";
+       for ($i = 0; $i < $level; $i++)
+               echo '-- ';
+       echo $taginfo['tag'] . (isset ($taginfo['refcnt'][$realm]) ? ' (' . $taginfo['refcnt'][$realm] . ')' : '') . "</option>\n";
+       foreach ($taginfo['kids'] as $kid)
+               renderTagOptionForFilter ($kid, $tagfilter, $realm, $level + 1);
+}
+
+function renderObjectTags ($id)
+{
+       renderEntityTags ('object', 'object_id', $id);
+}
+
+function renderIPv4PrefixTags ($id)
+{
+       renderEntityTags ('ipv4net', 'id', $id);
+}
+
+function renderRackTags ($id)
+{
+       renderEntityTags ('rack', 'rack_id', $id);
+}
+
+function renderIPv4VSTags ($id)
+{
+       renderEntityTags ('ip4vs', 'id', $id);
+}
+
+function renderIPv4RSPoolTags ($id)
+{
+       renderEntityTags ('ip4rspool', 'id', $id);
+}
+
+function renderEntityTags ($entity_realm = '', $bypass_name, $entity_id = 0)
+{
+       global $tagtree;
+       if ($entity_realm == '' or $entity_id <= 0)
+       {
+               showError ('Invalid or missing arguments', __FUNCTION__);
+               return;
+       }
+       global $root, $pageno, $tabno;
+       showMessageOrError();
+       startPortlet ('Tag list');
+       echo "<form method=post action='${root}process.php'>\n";
+       echo "<input type=hidden name=page value=${pageno}>\n";
+       echo "<input type=hidden name=tab value=${tabno}>\n";
+       echo "<input type=hidden name=${bypass_name} value=${entity_id}>\n";
+       echo "<input type=hidden name=op value=save>\n";
+       echo '<select name=taglist[] multiple size=' . getConfigVar ('MAXSELSIZE') . '>';
+       foreach ($tagtree as $taginfo)
+               renderTagOption ($taginfo);
+       echo '</select><br>';
+       echo "<input type=submit value='Save'></form>\n";
+       finishPortlet();
+}
+
+function printTagTRs()
+{
+       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";
+       }
+       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";
+       }
+       if (getConfigVar ('SHOW_AUTOMATIC_TAGS') == 'yes' and count ($auto_tags))
+       {
+               echo "<tr><th width='50%' class=tag_list_th>Automatic tags:</th><td class=tdleft>";
+               echo serializeTags ($auto_tags) . "</td></tr>\n";
+       }
+}
+
+function renderTagFilterPortlet ($tagfilter, $realm)
+{
+       global $pageno, $tabno, $taglist, $tagtree;
+       startPortlet ('Tag filter');
+       if (!count ($taglist))
+       {
+               echo "No tags defined<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";
+       echo '<select name=tagfilter[] multiple>';
+       foreach (getObjectiveTagTree ($tagtree, $realm) 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>';
+}
+
+function renderTagSelect ()
+{
+       global $taglist, $tagtree;
+       if (!count ($taglist))
+       {
+               echo "No tags defined";
+               return;
+       }
+       echo '<select name=taglist[] multiple>';
+       foreach ($tagtree as $taginfo)
+               renderTagOption ($taginfo);
+       echo '</select><br>';
+}
+
+function renderTagRollerForRow ()
+{
+       renderTagSelect();
+}
+
+function dump ($var)
+{
+       echo '<pre>';
+       print_r ($var);
+       echo '</pre>';
+}
+
 ?>