r2411 added deleteRow functionality and fixed a nasty bug in makeHrefProcess()
[racktables] / inc / interface.php
index 90b3b8ef6f95492ce8ba8953b97e34db05c0f74b..0c027cfbe3e5e09773e891472e4476d793e3009d 100644 (file)
@@ -30,6 +30,7 @@ $aac2 = array
 (
        'regular' => '',
        'virtual' => '<strong>L:</strong>',
+       'shared' => '<strong>S:</strong>',
        'router' => '<strong>R:</strong>',
 );
 
@@ -47,6 +48,9 @@ $image['rackspace']['height'] = 200;
 $image['objects']['path'] = 'pix/server.png';
 $image['objects']['width'] = 218;
 $image['objects']['height'] = 200;
+$image['files']['path'] = 'pix/files.png';
+$image['files']['width'] = 218;
+$image['files']['height'] = 200;
 $image['ipv4space']['path'] = 'pix/addressspace.png';
 $image['ipv4space']['width'] = 218;
 $image['ipv4space']['height'] = 200;
@@ -59,44 +63,45 @@ $image['config']['height'] = 200;
 $image['reports']['path'] = 'pix/report.png';
 $image['reports']['width'] = 218;
 $image['reports']['height'] = 200;
-$image['useup']['path'] = 'pix/tango-edit-clear.png';
-$image['useup']['width'] = 16;
-$image['useup']['height'] = 16;
+$image['download']['path'] = 'pix/download.png';
+$image['download']['width'] = 16;
+$image['download']['height'] = 16;
+$image['DOWNLOAD']['path'] = 'pix/download-big.png';
+$image['DOWNLOAD']['width'] = 32;
+$image['DOWNLOAD']['height'] = 32;
 $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['ADD']['path'] = 'pix/tango-list-add-big.png';
+$image['ADD']['width'] = 32;
+$image['ADD']['height'] = 32;
 $image['delete']['path'] = 'pix/tango-list-remove.png';
 $image['delete']['width'] = 16;
 $image['delete']['height'] = 16;
+$image['destroy']['path'] = 'pix/tango-edit-delete.png';
+$image['destroy']['width'] = 16;
+$image['destroy']['height'] = 16;
+$image['nodestroy']['path'] = 'pix/tango-edit-delete-gray.png';
+$image['nodestroy']['width'] = 16;
+$image['nodestroy']['height'] = 16;
+$image['DESTROY']['path'] = 'pix/tango-edit-delete-big.png';
+$image['DESTROY']['width'] = 32;
+$image['DESTROY']['height'] = 32;
 $image['nodelete']['path'] = 'pix/tango-list-remove-shadow.png';
 $image['nodelete']['width'] = 16;
 $image['nodelete']['height'] = 16;
-$image['grant'] = $image['add'];
-$image['revoke'] = $image['delete'];
 $image['inservice']['path'] = 'pix/tango-emblem-system.png';
 $image['inservice']['width'] = 16;
 $image['inservice']['height'] = 16;
 $image['notinservice']['path'] = 'pix/tango-dialog-error.png';
 $image['notinservice']['width'] = 16;
 $image['notinservice']['height'] = 16;
-$image['blockuser'] = $image['inservice'];
-$image['unblockuser'] = $image['notinservice'];
 $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['verge']['path'] = 'pix/pixel.png';
-//$image['verge']['path'] = 'pix/verge.png';
-$image['verge']['width'] = 16;
-$image['verge']['height'] = 16;
 $image['next']['path'] = 'pix/tango-go-next.png';
 $image['next']['width'] = 32;
 $image['next']['height'] = 32;
@@ -124,6 +129,42 @@ $image['CREATE']['height'] = 32;
 $image['DENIED']['path'] = 'pix/tango-dialog-error-big.png';
 $image['DENIED']['width'] = 32;
 $image['DENIED']['height'] = 32;
+$image['apply']['path'] = 'pix/tango-emblem-system.png';
+$image['apply']['width'] = 16;
+$image['apply']['height'] = 16;
+$image['node-collapsed']['path'] = 'pix/node-collapsed.png';
+$image['node-collapsed']['width'] = 16;
+$image['node-collapsed']['height'] = 16;
+$image['node-expanded']['path'] = 'pix/node-expanded.png';
+$image['node-expanded']['width'] = 16;
+$image['node-expanded']['height'] = 16;
+$image['node-expanded-static']['path'] = 'pix/node-expanded-static.png';
+$image['node-expanded-static']['width'] = 16;
+$image['node-expanded-static']['height'] = 16;
+$image['dragons']['path'] = 'pix/mitsudragon.png';
+$image['dragons']['width'] = 125;
+$image['dragons']['height'] = 21;
+$image['LB']['path'] = 'pix/loadbalancer.png';
+$image['LB']['width'] = 32;
+$image['LB']['height'] = 32;
+$image['RS pool']['path'] = 'pix/serverpool.png';
+$image['RS pool']['width'] = 48;
+$image['RS pool']['height'] = 16;
+$image['VS']['path'] = 'pix/servicesign.png';
+$image['VS']['width'] = 39;
+$image['VS']['height'] = 62;
+$image['router']['path'] = 'pix/router.png';
+$image['router']['width'] = 32;
+$image['router']['height'] = 32;
+$image['LINK']['path'] = 'pix/tango-emblem-symbolic-link-big.png';
+$image['LINK']['width'] = 32;
+$image['LINK']['height'] = 32;
+$image['favorite']['path'] = 'pix/tango-emblem-favorite.png';
+$image['favorite']['width'] = 16;
+$image['favorite']['height'] = 16;
+$image['computer']['path'] = 'pix/tango-computer.png';
+$image['computer']['width'] = 16;
+$image['computer']['height'] = 16;
 
 // This may be populated later onsite, report rendering function will use it.
 // See the $systemreport for structure.
@@ -141,33 +182,36 @@ function renderIndex ()
                        <table width='100%' cellspacing=0 cellpadding=30 class=mainmenu border=0>
                                <tr>
                                        <td>
-                                               <h1><a href='<?php echo $root; ?>?page=rackspace'>Rackspace<br>
+                                               <h1><a href='<?php echo makeHref(array('page'=>'rackspace')) ?>'>Rackspace<br>
                                                <?php printImageHREF ('rackspace'); ?></a></h1>
                                        </td>
                                        <td>
-                                               <h1><a href='<?php echo $root; ?>?page=objects'>Objects<br>
+                                               <h1><a href='<?php echo makeHref(array('page'=>'objects')) ?>'>Objects<br>
                                                <?php printImageHREF ('objects'); ?></a></h1>
                                        </td>
                                        <td>
-                                                       <h1><a href='<?php echo $root; ?>?page=ipv4space'>IPv4 space<br>
-                                                       <?php printImageHREF ('ipv4space'); ?></a></h1>
+                                               <h1><a href='<?php echo makeHref(array('page'=>'ipv4space')) ?>'>IPv4 space<br>
+                                               <?php printImageHREF ('ipv4space'); ?></a></h1>
+                                       </td>
+                                       <td>
+                                               <h1><a href='<?php echo makeHref(array('page'=>'files')) ?>'>Files<br>
+                                               <?php printImageHREF ('files'); ?></a></h1>
                                        </td>
                                </tr>
-                       </table>
-                       <table width='100%' cellspacing=0 cellpadding=30 class=mainmenu border=0>
                                <tr>
                                        <td>
-                                                       <h1><a href='<?php echo $root; ?>?page=config'>Configuration<br>
-                                                       <?php printImageHREF ('config'); ?></a></h1>
+                                               <h1><a href='<?php echo makeHref(array('page'=>'config')) ?>'>Configuration<br>
+                                               <?php printImageHREF ('config'); ?></a></h1>
                                        </td>
                                        <td>
-                                               <h1><a href='<?php echo $root; ?>?page=reports'>Reports<br>
+                                               <h1><a href='<?php echo makeHref(array('page'=>'reports')) ?>'>Reports<br>
                                                <?php printImageHREF ('reports'); ?></a></h1>
                                        </td>
                                        <td>
-                                               <h1><a href='<?php echo $root; ?>?page=ipv4slb'>IPv4 SLB<br>
+                                               <h1><a href='<?php echo makeHref(array('page'=>'ipv4slb')) ?>'>IPv4 SLB<br>
                                                <?php printImageHREF ('ipv4slb'); ?></a></h1>
                                        </td>
+                                       <td>&nbsp;</td>
                                </tr>
                        </table>
                        </div>
@@ -187,19 +231,19 @@ function renderRackspace ()
        echo '<table border=0 cellpadding=10 cellpadding=1>';
        // generate thumb gallery
        $rackrowList = getRackspace ($tagfilter);
-       global $root, $nextorder;
+       global $nextorder;
        $rackwidth = getRackImageWidth();
        $order = 'odd';
        foreach ($rackrowList as $rackrow)
        {
                echo "<tr class=row_${order}><th class=tdleft>";
-               echo "<a href='${root}?page=row&row_id=${rackrow['row_id']}${tagfilter_str}'>";
+               echo "<a href='".makeHref(array('page'=>'row', 'row_id'=>$rackrow['row_id']))."${tagfilter_str}'>";
                echo "${rackrow['row_name']}</a></th>";
                $rackList = getRacksForRow ($rackrow['row_id'], $tagfilter);
                echo "<td><table border=0 cellspacing=5><tr>";
                foreach ($rackList as $rack)
                {
-                       echo "<td align=center><a href='${root}?page=rack&rack_id=${rack['id']}'>";
+                       echo "<td align=center><a href='".makeHref(array('page'=>'rack', 'rack_id'=>$rack['id']))."'>";
                        echo "<img border=0 width=${rackwidth} height=";
                        echo getRackImageHeight ($rack['height']);
                        echo " title='${rack['height']} units'";
@@ -213,6 +257,38 @@ function renderRackspace ()
        echo "</td></tr></table>\n";
 }
 
+function renderRackspaceRowEditor ()
+{
+       function printNewItemTR ()
+       {
+               printOpFormIntro ('addRow');
+               echo "<tr><td><input type=text name=name tabindex=100></td><td>";
+               printImageHREF ('create', 'Add new row', TRUE, 101);
+               echo "</td><td></td></tr></form>";
+       }
+       global $pageno, $tabno;
+       startPortlet ('Rows');
+       showMessageOrError();
+       echo "<table border=0 cellspacing=0 cellpadding=5 align=center class=widetable>\n";
+       echo "<tr><th>Name</th></tr>\n";
+       if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
+               printNewItemTR();
+       $rackrowList = getRackspace ();
+       foreach ($rackrowList as $rackrow)
+       {
+               printOpFormIntro ('updateRow', array ('row_id' => $rackrow['row_id']));
+               echo "<tr><td><input type=text name=name value='${rackrow['row_name']}'></td><td>";
+               printImageHREF ('save', 'Save changes', TRUE);
+               echo "</td></form><td><a href=\"".makeHrefProcess(array('op'=>'delete', 'row_id'=>$rackrow['row_id'], 'name'=>$rackrow['row_name']))."\">";
+               printImageHREF ('delete', 'Delete row');
+               echo "</a></td></tr>\n";
+       }
+       if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
+               printNewItemTR();
+       echo "</table><br>\n";
+       finishPortlet();
+}
+
 function renderRow ($row_id = 0)
 {
        if ($row_id == 0)
@@ -234,8 +310,8 @@ function renderRow ($row_id = 0)
        echo "<tr><td class=pcleft>";
        startPortlet ($rowInfo['name']);
        echo "<table border=0 cellspacing=0 cellpadding=3 width='100%'>\n";
-       echo "<tr><th width='50%' class=tdright>Racks:</th><td class=tdleft>${rowInfo['count']}</td></tr>\n";
-       echo "<tr><th width='50%' class=tdright>Units:</th><td class=tdleft>${rowInfo['sum']}</td></tr>\n";
+       echo "<tr><th width='50%' class=tdright>Racks:</th><td class=tdleft>${rowInfo['rackCount']}</td></tr>\n";
+       echo "<tr><th width='50%' class=tdright>Units:</th><td class=tdleft>${rowInfo['sumHeight']}</td></tr>\n";
        echo "<tr><th width='50%' class=tdright>%% used:</th><td class=tdleft>";
        renderProgressBar (getRSUforRackRow ($rackList));
        echo "</td></tr>\n";
@@ -244,14 +320,14 @@ function renderRow ($row_id = 0)
 
        echo "</td><td class=pcright rowspan=2>";
 
-       global $root, $nextorder;
+       global $nextorder;
        $rackwidth = getRackImageWidth() * getConfigVar ('ROW_SCALE');
        $order = 'odd';
        startPortlet ('Racks');
        echo "<table border=0 cellspacing=5 align='center'><tr>";
        foreach ($rackList as $rack)
        {
-               echo "<td align=center class=row_${order}><a href='${root}?page=rack&rack_id=${rack['id']}'>";
+               echo "<td align=center class=row_${order}><a href='".makeHref(array('page'=>'rack', 'rack_id'=>$rack['id']))."'>";
                echo "<img border=0 width=${rackwidth} height=" . (getRackImageHeight ($rack['height']) * getConfigVar ('ROW_SCALE'));
                echo " title='${rack['height']} units'";
                echo "src='render_image.php?img=minirack&rack_id=${rack['id']}'>";
@@ -269,7 +345,6 @@ function renderRow ($row_id = 0)
 
 function showError ($info = '', $location = 'N/A')
 {
-       global $root;
        if (preg_match ('/\.php$/', $location))
                $location = basename ($location);
        elseif ($location != 'N/A')
@@ -279,7 +354,7 @@ function showError ($info = '', $location = 'N/A')
                echo 'No additional information is available.';
        else
                echo "Additional information:<br><p>\n<pre>\n${info}\n</pre></p>";
-       echo "Go back or try starting from <a href='${root}'>index page</a>.<br></div>\n";
+       echo "Go back or try starting from <a href='".makeHref()."'>index page</a>.<br></div>\n";
 }
 
 // This function renders rack as HTML table.
@@ -295,7 +370,7 @@ function renderRack ($rack_id = 0, $hl_obj_id = 0)
                showError ('getRackData() failed', __FUNCTION__);
                return;
        }
-       global $root, $pageno, $tabno;
+       global $pageno, $tabno;
        markAllSpans ($rackData);
        if ($hl_obj_id > 0)
                highlightObject ($rackData, $hl_obj_id);
@@ -303,32 +378,28 @@ function renderRack ($rack_id = 0, $hl_obj_id = 0)
        $prev_id = getPrevIDforRack ($rackData['row_id'], $rack_id);
        $next_id = getNextIDforRack ($rackData['row_id'], $rack_id);
        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>";
+       echo "<td><h2><a href='".makeHref(array('page'=>'row', 'row_id'=>$rackData['row_id']))."'>${rackData['row_name']}</a> :</h2></td>";
        // FIXME: use 'bypass'?
        if ($prev_id != NULL)
        {
-               echo "<td><a href='${root}?page=rack&rack_id=${prev_id}'>";
+               echo "<td><a href='".makeHref(array('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>";
+       echo "<td><h2><a href='".makeHref(array('page'=>'rack', 'rack_id'=>$rackData['id']))."'>${rackData['name']}</a></h2></td>";
        if ($next_id != NULL)
        {
-               echo "<td><a href='${root}?page=rack&rack_id=${next_id}'>";
+               echo "<td><a href='".makeHref(array('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
-               $markup = array ('left' => 'Back', 'right' => 'Front');
        echo "<table class=rack border=0 cellspacing=0 cellpadding=1>\n";
-       echo "<tr><th width='10%'>&nbsp;</th><th width='20%'>${markup['left']}</th>";
-       echo "<th width='50%'>Interior</th><th width='20%'>${markup['right']}</th></tr>\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";
        for ($i = $rackData['height']; $i > 0; $i--)
        {
-               echo '<tr><th>' . ($rackData['bottom_is_unit1'] == 'yes' ? $i : $rackData['height'] - $i + 1) . '</th>';
+               echo "<tr><th>${i}</th>";
                for ($locidx = 0; $locidx < 3; $locidx++)
                {
                        if (isset ($rackData[$i][$locidx]['skipped']))
@@ -356,7 +427,7 @@ function renderRack ($rack_id = 0, $hl_obj_id = 0)
                                        else
                                                $suffix = "'>";
                                        echo $prefix . $suffix;
-                                       echo "<a href='${root}?page=object&object_id=${objectData['id']}'>${objectData['dname']}</a></div>";
+                                       echo "<a href='".makeHref(array('page'=>'object', 'object_id'=>$objectData['id']))."'>${objectData['dname']}</a></div>";
                                        break;
                                case 'A':
                                        echo '<div title="This rackspace does not exist">&nbsp;</div>';
@@ -420,19 +491,20 @@ function renderEditObjectForm ($object_id)
 {
        showMessageOrError();
 
-       global $pageno, $tabno, $root;
+       global $pageno, $tabno;
        $object = getObjectInfo ($object_id);
        if ($object == NULL)
        {
                showError ('getObjectInfo() failed', __FUNCTION__);
                return;
        }
-       echo '<table border=0 width=100%><tr>';
-
-       echo '<td class=pcleft>';
-       startPortlet ('Static attributes');
+       startPortlet ();
        printOpFormIntro ('update');
+       echo '<table border=0 width=100%><tr><td class=pcleft>';
+
+       // static attributes
        echo '<table border=0 align=center>';
+       echo "<tr><th colspan=2><h2>Static attributes</h2></th></tr>";
        echo "<tr><th class=tdright>Type:</th><td class=tdleft>";
        printSelect (getObjectTypeList(), 'object_type_id', $object['objtype_id']);
        echo "</td></tr>\n";
@@ -440,76 +512,71 @@ function renderEditObjectForm ($object_id)
        echo "<tr><th class=tdright>Common name:</th><td class=tdleft><input type=text name=object_name value='${object['name']}'></td></tr>\n";
        echo "<tr><th class=tdright>Visible label:</th><td class=tdleft><input type=text name=object_label value='${object['label']}'></td></tr>\n";
        echo "<tr><th class=tdright>Asset tag:</th><td class=tdleft><input type=text name=object_asset_no value='${object['asset_no']}'></td></tr>\n";
-       echo "<tr><th class=tdright>Barcode:</th><td class=tdleft><input type=text name=object_barcode value='${object['barcode']}'></td></tr>\n";
-       echo "<tr><th class=tdright>Has problems:</th><td class=tdleft><input type=checkbox name=object_has_problems";
+       /*echo "<tr><th class=tdright>Has problems:</th><td class=tdleft><input type=checkbox name=object_has_problems";
        if ($object['has_problems'] == 'yes')
                echo ' checked';
-       echo "></td></tr>\n";
+       echo "></td></tr>\n";*/
+       echo "<tr><th class=tdright>Actions:</th><td class=tdleft><a href='".
+               makeHrefProcess(array('op'=>'deleteObject', 'page'=>'objects', 'tab'=>'default', 'object_id'=>$object_id, 'name'=>$object['name'])).
+               "' onclick=\"javascript:return confirm('Are you sure you want to delete the object?')\">Delete object</a></td></tr>\n";
        echo "<tr><td colspan=2><b>Comment:</b><br><textarea name=object_comment rows=10 cols=80>${object['comment']}</textarea></td></tr>";
-       echo "<tr><th class=submit colspan=2>";
-       printImageHREF ('SAVE', 'Save changes', TRUE);
-       echo "</td></tr>\n";
-       echo '</form></table><br>';
-       finishPortlet();
-       echo '</td>';
+       echo '</table>';
+
+       echo '</td><td class=pcright>';
        
-       // stickers
-       echo '<td class=pcright>';
-       startPortlet ('Optional attributes');
+       // optional attributes
        $values = getAttrValues ($object_id);
-       echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
-       echo "<tr><th>&nbsp;</th><th>Attribute</th><th>Value</th><th>&nbsp;</th></tr>\n";
-       printOpFormIntro ('updateStickers');
-       echo '<input type=hidden name=num_attrs value=' . count($values) . ">\n";
-
-       $i = 0;
-       foreach ($values as $record)
-       {
-               echo "<input type=hidden name=${i}_attr_id value=${record['id']}>";
-               echo '<tr><td>';
-               if (!empty ($record['value']))
-               {
-                       echo "<a href='${root}process.php?page=${pageno}&tab=${tabno}&op=clearSticker&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'])
+       if (count($values) > 0)
+       {
+               echo "<table border=0 cellspacing=0 cellpadding=5 align=center class=widetable>\n";
+               echo "<tr><th colspan=3><h2>Optional attributes</h2></th></tr>";
+               echo "<tr><th>&nbsp;</th><th>Attribute</th><th>Value</th></tr>\n";
+               echo '<input type=hidden name=num_attrs value=' . count($values) . ">\n";
+               $i = 0;
+               foreach ($values as $record)
                {
-                       case 'uint':
-                       case 'float':
-                       case 'string':
-                               echo "<input type=text name=${i}_value value='${record['value']}'>";
-                               break;
-                       case 'dict':
-                               $chapter = readChapter ($record['chapter_name']);
-                               $chapter[0] = '-- NOT SET --';
-                               printSelect ($chapter, "${i}_value", $record['key']);
-                               break;
+                       echo "<input type=hidden name=${i}_attr_id value=${record['id']}>";
+                       echo '<tr><td>';
+                       if (!empty ($record['value']))
+                       {
+                               echo "<a href='".makeHrefProcess(array('op'=>'clearSticker', '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'])
+                       {
+                               case 'uint':
+                               case 'float':
+                               case 'string':
+                                       echo "<input type=text name=${i}_value value='${record['value']}'>";
+                                       break;
+                               case 'dict':
+                                       $chapter = readChapter ($record['chapter_name']);
+                                       $chapter[0] = '-- NOT SET --';
+                                       printSelect ($chapter, "${i}_value", $record['key']);
+                                       break;
+                       }
+                       echo "</td></tr>\n";
+                       $i++;
                }
-               echo "</td></tr>\n";
-               $i++;
+               echo "</table>";
        }
-       echo "<tr><td colspan=3>";
-       printImageHREF ('SAVE', 'Save changes', TRUE);
        echo "</td></tr>\n";
-       echo "</form>";
-       echo "</table>\n";
-       finishPortlet();
-       echo '</td>';
 
-       echo '</tr><tr>';
+       echo "<tr><th class=submit colspan=2>";
+       printImageHREF ('SAVE', 'Save changes', TRUE);
+       echo "</form></th></tr></table>\n";
+       finishPortlet();
 
-       echo '<td colspan=2>';
+       echo '<table border=0 width=100%><tr><td>';
        startPortlet ('history');
        renderHistory ($pageno, $object_id);
        finishPortlet();
-       echo '</td>';
-
-       echo '</tr></table>';
+       echo '</td></tr></table>';
 }
 
 // This is a clone of renderEditObjectForm().
@@ -528,7 +595,7 @@ function renderEditRackForm ($rack_id)
        printOpFormIntro ('updateRack');
        echo '<table border=0 align=center>';
        echo "<tr><th class=tdright>Rack row:</th><td class=tdleft>";
-       printSelect (readChapter ('RackRow'), 'rack_row_id', $rack['row_id']);
+       printSelect (getRackRows(), 'rack_row_id', $rack['row_id']);
        echo "</td></tr>\n";
        echo "<tr><th class=tdright>Name (required):</th><td class=tdleft><input type=text name=rack_name value='${rack['name']}'></td></tr>\n";
        echo "<tr><th class=tdright>Height (required):</th><td class=tdleft><input type=text name=rack_height value='${rack['height']}'></td></tr>\n";
@@ -545,7 +612,7 @@ function renderEditRackForm ($rack_id)
 }
 
 // This is a helper for creators and editors.
-function printSelect ($rowList, $select_name, $selected_id = 1)
+function printSelect ($rowList, $select_name, $selected_id = NULL, $tabindex = NULL)
 {
        // First collect all data for OPTGROUPs, then ouput it and dump
        // the rest of records as is.
@@ -566,7 +633,7 @@ function printSelect ($rowList, $select_name, $selected_id = 1)
                else
                        $other[$dict_key] = $dict_value;
        }
-       echo "<select name=${select_name}>";
+       echo "<select name=${select_name}" . ($tabindex ? " tabindex=${tabindex}" : '') . '>';
        if (!count ($optgroup))
        {
                foreach ($other as $dict_key => $dict_value)
@@ -610,7 +677,6 @@ function printSelect ($rowList, $select_name, $selected_id = 1)
 // used by renderGridForm() and renderRackPage()
 function renderRackInfoPortlet ($rackData)
 {
-       global $root;
        startPortlet ('summary');
        echo "<table border=0 cellspacing=0 cellpadding=3 width='100%'>\n";
        echo "<tr><th width='50%' class=tdright>Rack row:</th><td class=tdleft>${rackData['row_name']}</td></tr>\n";
@@ -622,7 +688,7 @@ function renderRackInfoPortlet ($rackData)
        echo "<tr><th width='50%' class=tdright>Objects:</th><td class=tdleft>";
        echo count (stuffInRackspace ($rackData));
        echo "</td></tr>\n";
-       printTagTRs ("${root}?page=rackspace&");
+       printTagTRs (makeHref(array('page'=>'rackspace', 'tab'=>'default'))."&");
        if (!empty ($rackData['comment']))
                echo "<tr><th width='50%' class=tdright>Comment:</th><td class=tdleft>${rackData['comment']}</td></tr>\n";
        echo '</table>';
@@ -644,20 +710,10 @@ function renderGridForm ($rack_id = 0, $filter, $header, $submit, $state1, $stat
                return;
        }
 
-       global $root, $pageno, $tabno;
+       global $pageno, $tabno;
        $filter ($rackData);
        markupObjectProblems ($rackData);
 
-       // Process form submit.
-       if (isset ($_REQUEST['do_update']))
-       {
-               $log[] = processGridForm ($rackData, $state1, $state2);
-               printLog ($log);
-               $rackData = getRackData ($rack_id);
-               $filter ($rackData);
-               markupObjectProblems ($rackData);
-       }
-
        // Render the result whatever it is.
        // Main layout.
        echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
@@ -675,10 +731,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 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";
+       printOpFormIntro ('updateRack');
        markupAtomGrid ($rackData, $state2);
        renderAtomGrid ($rackData);
        echo "</table></center>\n";
@@ -709,14 +762,13 @@ function finishPortlet ()
 
 function printRefsOfType ($refs, $type, $eq)
 {
-       global $root;
        $gotone=0;
        foreach ($refs as $ref)
        {
                if ($eq($ref['type'], $type))
                {
                        if ($gotone) echo ', ';
-                       echo "<a href='${root}?page=object&object_id=${ref['object_id']}'>";
+                       echo "<a href='".makeHref(array('page'=>'object', 'object_id'=>$ref['object_id']))."'>";
                        if (!empty ($ref['name']))
                                echo $ref['name'] . '@';
                        echo "${ref['object_name']}</a>";
@@ -727,7 +779,7 @@ function printRefsOfType ($refs, $type, $eq)
 
 function renderRackObject ($object_id = 0)
 {
-       global $root, $nextorder, $aac;
+       global $nextorder, $aac;
        if ($object_id <= 0)
        {
                showError ('Invalid object_id', __FUNCTION__);
@@ -751,7 +803,7 @@ function renderRackObject ($object_id = 0)
        elseif (in_array ($info['objtype_id'], explode (',', getConfigVar ('NAMEFUL_OBJTYPES'))))
                echo "<tr><td colspan=2 class=msg_error>Common name is missing.</td></tr>\n";
        echo "<tr><th width='50%' class=tdright>Object type:</th>";
-       echo "<td class=tdleft><a href='${root}?page=objgroup&group_id=${info['objtype_id']}&hl_object_id=${object_id}'>${info['objtype_name']}</a></td></tr>\n";
+       echo "<td class=tdleft><a href='".makeHref(array('page'=>'objgroup', 'group_id'=>$info['objtype_id'], 'hl_object_id'=>$object_id))."'>${info['objtype_name']}</a></td></tr>\n";
        if (!empty ($info['asset_no']))
                echo "<tr><th width='50%' class=tdright>Asset tag:</th><td class=tdleft>${info['asset_no']}</td></tr>\n";
        elseif (in_array ($info['objtype_id'], explode (',', getConfigVar ('REQUIRE_ASSET_TAG_FOR'))))
@@ -764,8 +816,8 @@ function renderRackObject ($object_id = 0)
                echo "<tr><td colspan=2 class=msg_error>Has problems</td></tr>\n";
        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 ("${root}?page=objgroup&group_id=${info['objtype_id']}&");
+                       echo "<tr><th width='50%' class=tdright><span class=sticker>${record['name']}</span>:</th><td class=tdleft>${record['a_value']}</td></tr>\n";
+       printTagTRs (makeHref(array('page'=>'objgroup', 'tab'=>'default', 'group_id'=>$info['objtype_id']))."&");
        echo "</table><br>\n";
        finishPortlet();
 
@@ -776,6 +828,8 @@ function renderRackObject ($object_id = 0)
                finishPortlet ();
        }
 
+       renderFilesPortlet ('object', $object_id);
+
        $ports = getObjectPortsAndLinks ($object_id);
        if (count ($ports))
        {
@@ -801,7 +855,7 @@ function renderRackObject ($object_id = 0)
                                echo "<td>${port['l2address']}</td>";
                                if ($port['remote_object_id'])
                                {
-                                       echo "<td><a href='${root}?page=object&object_id=${port['remote_object_id']}&hl_port_id=${port['remote_id']}'>${port['remote_object_name']}</a></td>";
+                                       echo "<td><a href='".makeHref(array('page'=>'object', 'object_id'=>$port['remote_object_id'], 'hl_port_id'=>$port['remote_id']))."'>${port['remote_object_name']}</a></td>";
                                        echo "<td>${port['remote_name']}</td>";
                                }
                                elseif (!empty ($port['reservation_comment']))
@@ -839,42 +893,36 @@ function renderRackObject ($object_id = 0)
                        $class = $alloc['addrinfo']['class'];
                        $secondclass = ($hl_ipv4_addr == $dottedquad) ? 'tdleft port_highlight' : 'tdleft';
                        $netid = getIPv4AddressNetworkId ($dottedquad);
-                       $netinfo = getIPv4NetworkInfo ($netid);
-                       echo "<tr class='${class}' valign=top><td class=tdleft>${alloc['osif']}</td><td class='${secondclass}'>";
-                       echo "<a href='${root}?page=ipaddress&ip=" . $dottedquad . "&hl_object_id=${object_id}'>${dottedquad}</a>";
-                       if (getConfigVar ('EXT_IPV4_VIEW') != 'yes')
+                       if (NULL !== $netid)
                        {
-                               if (NULL === $netid)
-                                       $suffix = '/??';
-                               else
-                                       echo '<small>/' . $netinfo['mask'] . '</small>';
+                               $netinfo = getIPv4NetworkInfo ($netid);
+                               loadIPv4AddrList ($netinfo);
                        }
+                       echo "<tr class='${class}' valign=top><td class=tdleft>${alloc['osif']}</td><td class='${secondclass}'>";
+                       if (NULL !== $netid)
+                               echo "<a href='".makeHref(array('page'=>'ipaddress', 'ip'=>$dottedquad, 'hl_object_id'=>$object_id))."'>${dottedquad}</a>";
+                       else
+                               echo $dottedquad;
+                       if (getConfigVar ('EXT_IPV4_VIEW') != 'yes')
+                               echo '<small>/' . (NULL === $netid ? '??' : $netinfo['mask']) . '</small>';
                        echo '&nbsp;' . $aac[$alloc['type']];
                        if (!empty ($alloc['addrinfo']['name']))
-                               echo '(' . niftyString ($alloc['addrinfo']['name']) . ')';
+                               echo ' (' . niftyString ($alloc['addrinfo']['name']) . ')';
                        echo '</td>';
                        if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
                        {
                                if (NULL === $netid)
-                                       echo '<td colspan=2>?</td>';
+                                       echo '<td colspan=2>N/A</td><td>&nbsp;</td>';
                                else
-                                       printIPv4NetInfoTDs ($netinfo, $secondclass);
-                               echo "<td class='${secondclass}'>";
-                               // FIXME: These cals are really heavy, replace them with a more appropriate dedicated function.
-                               $netdata = getIPv4Network ($netid);
-                               $newline = '';
-                               foreach (findRouters ($netdata['addrlist']) as $router)
                                {
-                                       if ($router['id'] == $object_id)
-                                               continue;
-                                       echo $newline . $router['addr'] . ", <a href='${root}?page=object&object_id=${router['id']}&hl_ipv4_addr=${router['addr']}'>";
-                                       echo (empty ($router['iface']) ? '' : $router['iface'] . '@') . $router['dname'] . '</a>';
-                                       $routertags = loadRackObjectTags ($router['id']);
-                                       if (count ($routertags))
-                                               echo '<br><small>' . serializeTags ($routertags, "${root}?page=objects&tab=default&") . '</small>';
-                                       $newline = '<br>';
+                                       printIPv4NetInfoTDs ($netinfo, $secondclass);
+                                       // filter out self-allocation
+                                       $other_routers = array();
+                                       foreach (findRouters ($netinfo['addrlist']) as $router)
+                                               if ($router['id'] != $object_id)
+                                                       $other_routers[] = $router;
+                                       printRoutersTD ($other_routers);
                                }
-                               echo '</td>';
                        }
                        // peers
                        echo "<td class='${secondclass}'>\n";
@@ -888,7 +936,7 @@ function renderRackObject ($object_id = 0)
                        {
                                if ($allocpeer['object_id'] == $object_id)
                                        continue;
-                               echo $prefix . "<a href='${root}?page=object&object_id=${allocpeer['object_id']}'>";
+                               echo $prefix . "<a href='".makeHref(array('page'=>'object', 'object_id'=>$allocpeer['object_id']))."'>";
                                if (!empty ($allocpeer['osif']))
                                        echo $allocpeer['osif'] . '@';
                                echo $allocpeer['object_name'] . '</a>';
@@ -923,13 +971,13 @@ function renderRackObject ($object_id = 0)
                                        $osif = $alloclist [$pf['localip']]['osif'] . ': ';
                                }
                                echo "<tr class='$class'>";
-                               echo "<td>${pf['proto']}</td><td class=tdleft>${osif}<a href='${root}?page=ipaddress&tab=default&ip=${pf['localip']}'>${pf['localip']}</a>:${pf['localport']}</td>";
-                               echo "<td class=tdleft><a href='${root}?page=ipaddress&tab=default&ip=${pf['remoteip']}'>${pf['remoteip']}</a>:${pf['remoteport']}</td>";
+                               echo "<td>${pf['proto']}</td><td class=tdleft>${osif}<a href='".makeHref(array('page'=>'ipaddress', 'tab'=>'default', 'ip'=>$pf['localip']))."'>${pf['localip']}</a>:${pf['localport']}</td>";
+                               echo "<td class=tdleft><a href='".makeHref(array('page'=>'ipaddress', 'tab'=>'default', 'ip'=>$pf['remoteip']))."'>${pf['remoteip']}</a>:${pf['remoteport']}</td>";
                                $address = getIPv4Address ($pf['remoteip']);
                                echo "<td class='description'>";
                                if (count ($address['allocs']))
                                        foreach($address['allocs'] as $bond)
-                                               echo "<a href='${root}?page=object&tab=default&object_id=${bond['object_id']}'>${bond['object_name']}(${bond['name']})</a> ";
+                                               echo "<a href='".makeHref(array('page'=>'object', 'tab'=>'default', 'object_id'=>$bond['object_id']))."'>${bond['object_name']}(${bond['name']})</a> ";
                                elseif (!empty ($pf['remote_addr_name']))
                                        echo '(' . $pf['remote_addr_name'] . ')';
                                echo "</td><td class='description'>${pf['description']}</td></tr>";
@@ -944,9 +992,9 @@ function renderRackObject ($object_id = 0)
                        foreach ($forwards['in'] as $pf)
                        {
                                echo "<tr>";
-                               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>";
+                               echo "<td>${pf['proto']}/<a href='".makeHref(array('page'=>'ipaddress', 'tab'=>'default', 'ip'=>$pf['localip']))."'>${pf['localip']}</a>:${pf['localport']}</td>";
+                               echo "<td class='description'><a href='".makeHref(array('page'=>'object', 'tab'=>'default', 'object_id'=>$pf['object_id']))."'>${pf['object_name']}</a>";
+                               echo "</td><td><a href='".makeHref(array('page'=>'ipaddress', 'tab'=>'default', 'ip'=>$pf['remoteip']))."'>${pf['remoteip']}</a>:${pf['remoteport']}</td>";
                                echo "<td class='description'>${pf['description']}</td></tr>";
                        }
                        echo "</table><br><br>";
@@ -963,16 +1011,13 @@ function renderRackObject ($object_id = 0)
                echo "<tr><th>VS</th><th>RS pool</th><th>RS</th><th>VS config</th><th>RS config</th></tr>\n";
                foreach ($pools as $vs_id => $info)
                {
-                       echo "<tr valign=top class=row_${order}><td class=tdleft><a href='${root}?page=ipv4vs&vs_id=${vs_id}'>";
-                       echo buildVServiceName ($info);
-                       echo '</a>';
-                       if (!empty ($info['name']))
-                               echo "<br>${info['name']}";
-                       echo "</td><td class=tdleft><a href='${root}?page=ipv4rsp&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>";
-                       echo "<td class=tdleft><pre>${info['rsconfig']}</pre></td>";
+                       echo "<tr valign=top class=row_${order}><td class=tdleft>";
+                       renderVSCell ($vs_id);
+                       echo "</td><td class=tdleft>";
+                       renderRSPoolCell ($info['pool_id'], $info['pool_name']);
+                       echo '</td><td class=tdleft>' . $info['rscount'] . '</td>';
+                       echo "<td class=slbconf>${info['vsconfig']}</td>";
+                       echo "<td class=slbconf>${info['rsconfig']}</td>";
                        echo "</tr>\n";
                        $order = $nextorder[$order];
                }
@@ -1004,7 +1049,7 @@ function renderRackMultiSelect ($sname, $racks, $selected)
                        $rdata[$rack['row_name']] = array ($rack['id'] => $rack['name']);
                else
                        $rdata[$rack['row_name']][$rack['id']] = $rack['name'];
-       echo "<select name=${sname} multiple size=" . getConfigVar ('MAXSELSIZE') . " onchange='getElementById(\"racks\").submit()'>\n";
+       echo "<select name=${sname} multiple size=" . getConfigVar ('MAXSELSIZE') . " onchange='getElementsByName(\"updateObjectAllocation\")[0].submit()'>\n";
        foreach ($rdata as $optgroup => $racklist)
        {
                echo "<optgroup label='${optgroup}'>";
@@ -1032,7 +1077,20 @@ function showMessageOrError ()
 // This function renders a form for port edition.
 function renderPortsForObject ($object_id = 0)
 {
-       global $root, $pageno, $tabno;
+       function printNewItemTR ()
+       {
+               printOpFormIntro ('addPort');
+               echo "<tr><td>";
+               printImageHREF ('add', 'add a port', TRUE);
+               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><td>";
+               printSelect (getPortTypes(), 'port_type_id', getConfigVar ('default_port_type'), 102);
+               echo "<td><input type=text name=port_l2address tabindex=103></td>\n";
+               echo "<td colspan=3>&nbsp;</td><td>";
+               printImageHREF ('add', 'add a port', TRUE, 104);
+               echo "</td></tr></form>";
+       }
+       global $pageno, $tabno;
        if ($object_id <= 0)
        {
                showError ('Invalid object_id', __FUNCTION__);
@@ -1045,41 +1103,62 @@ function renderPortsForObject ($object_id = 0)
        echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
        echo "<tr><th>&nbsp;</th><th>Local name</th><th>Visible label</th><th>Port type</th><th>L2 address</th>";
        echo "<th>Rem. object</th><th>Rem. port</th><th>(Un)link or (un)reserve</th><th>&nbsp;</th></tr>\n";
+       if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
+               printNewItemTR();
        foreach ($ports as $port)
        {
                printOpFormIntro ('editPort', array ('port_id' => $port['id']));
-               echo "<tr><td><a href='${root}process.php?op=delPort&page=${pageno}&tab=${tabno}&port_id=${port['id']}&object_id=$object_id&port_name=${port['name']}'>";
+               echo "<tr><td><a href='".makeHrefProcess(array('op'=>'delport', 'port_id'=>$port['id'], 'object_id'=>$object_id, 'port_name'=>$port['name']))."'>";
                printImageHREF ('delete', 'Unlink and Delete this port');
                echo "</a></td>\n";
                echo "<td><input type=text name=name value='${port['name']}' size=8></td>";
                echo "<td><input type=text name=label value='${port['label']}' size=24></td>";
-               echo "<td>${port['type']}</td>\n";
+               if (!$port['remote_object_id'])
+               {
+                       echo "<td>";
+                       printSelect (getPortTypes(), 'port_type_id', $port['type_id']);
+                       echo "</td>";
+               }
+               else
+               {
+                       echo "<input type=hidden name=port_type_id value='${port['type_id']}'>";
+                       echo "<td>${port['type']}</td>\n";
+               }
                echo "<td><input type=text name=l2address value='${port['l2address']}'></td>\n";
                if ($port['remote_object_id'])
                {
-                       echo "<td><a href='${root}?page=object&object_id=${port['remote_object_id']}'>${port['remote_object_name']}</a></td>";
+                       echo "<td><a href='".makeHref(array('page'=>'object', 'object_id'=>$port['remote_object_id']))."'>${port['remote_object_name']}</a></td>";
                        echo "<td>${port['remote_name']}</td>";
-                       echo "<td><a href='${root}process.php?op=unlinkPort&page=${pageno}&tab=${tabno}&port_id=${port['id']}&object_id=$object_id&port_name=";
-                       echo urlencode ($port['name']);
-                       echo "&remote_port_name=${port['remote_name']}&remote_object_name=${port['remote_object_name']}'>";
-                       printImageHREF ('unlink', 'Unlink this port');
+                       echo "<td><a href='".
+                               makeHrefProcess(array(
+                                       'op'=>'unlinkPort', 
+                                       'port_id'=>$port['id'], 
+                                       'object_id'=>$object_id, 
+                                       'port_name'=>$port['name'], 
+                                       'remote_port_name'=>$port['remote_name'], 
+                                       'remote_object_name'=>$port['remote_object_name'])).
+                       "'>";
+                       printImageHREF ('clear', 'Unlink this port');
                        echo "</a></td>";
                }
                elseif (!empty ($port['reservation_comment']))
                {
                        echo "<td><b>Reserved;</b></td>";
                        echo "<td><input type=text name=reservation_comment value='${port['reservation_comment']}'></td>";
-                       echo "<td><a href='${root}process.php?op=useup&page=${pageno}&tab=${tabno}&port_id=${port['id']}&object_id=${object_id}'>";
-                       printImageHREF ('useup', 'Use up this port');
+                       echo "<td><a href='".
+                               makeHrefProcess(array(
+                                       'op'=>'useup',
+                                       'port_id'=>$port['id'], 
+                                       'object_id'=>$object_id)).
+                       "'>";
+                       printImageHREF ('clear', 'Use up this port');
                        echo "</a></td>";
                }
                else
                {
                        echo "<td>&nbsp;</td><td>&nbsp;</td>";
                        echo "<td>";
-                       echo "<a href='javascript:;' onclick='window.open(\"${root}link_helper.php?port=${port['id']}&type=${port['type_id']}&object_id=$object_id&port_name=";
-                       echo urlencode ($port['name']);
-                       echo "\",\"findlink\",\"height=700, width=400, location=no, menubar=no, resizable=yes, scrollbars=no, status=no, titlebar=no, toolbar=no\");'>";
+                       echo "<a href='javascript:;' onclick='window.open(\"".makeHrefPortLinkHelper(array('port'=>$port['id'], 'type'=>$port['type_id'], 'object_id'=>$object_id, 'port_name'=>$port['name']))."\",\"findlink\",\"height=700, width=400, location=no, menubar=no, resizable=yes, scrollbars=no, status=no, titlebar=no, toolbar=no\");'>";
                        printImageHREF ('link', 'Link this port');
                        echo "</a> <input type=text name=reservation_comment>";
                        echo "</td>\n";
@@ -1088,24 +1167,8 @@ function renderPortsForObject ($object_id = 0)
                printImageHREF ('save', 'Save changes', TRUE);
                echo "</td></form></tr>\n";
        }
-       printOpFormIntro ('addPort');
-       echo "<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 "<td><select name='port_type_id' tabindex=102>\n";
-       $types = getPortTypes();
-       $default_port_type = getConfigVar ('default_port_type');
-       foreach ($types as $typeid => $typename)
-       {
-               echo "<option value='${typeid}'";
-               if ($typeid == $default_port_type)
-                       echo " selected";
-               echo ">${typename}</option>\n";
-       }
-       echo "</select></td>";
-       echo "<td><input type=text name=port_l2address tabindex=103></td>\n";
-       echo "<td colspan=4>&nbsp;</td></tr></form>";
+       if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
+               printNewItemTR();
        echo "</table><br>\n";
        finishPortlet();
 
@@ -1118,15 +1181,7 @@ function renderPortsForObject ($object_id = 0)
        echo '<option value=ssv1>SSV:&lt;interface name&gt; &lt;MAC address&gt;</option>';
        echo "</select>";
        echo 'Default port type: ';
-       echo "<select name=port_type>\n";
-       foreach ($types as $typeid => $typename)
-       {
-               echo "<option value='${typeid}'";
-               if ($typeid == $default_port_type)
-                       echo " selected";
-               echo ">${typename}</option>\n";
-       }
-       echo "</select>";
+       printSelect (getPortTypes(), 'port_type', getConfigVar ('default_port_type'), 102);
        echo "<input type=submit value='Parse output'><br>\n";
        echo "<textarea name=input cols=100 rows=50></textarea><br>\n";
        echo '</form>';
@@ -1135,7 +1190,22 @@ function renderPortsForObject ($object_id = 0)
 
 function renderIPv4ForObject ($object_id = 0)
 {
-       global $root, $pageno, $tabno, $aat;
+       function printNewItemTR ()
+       {
+               global $aat;
+               printOpFormIntro ('addIPv4Allocation');
+               echo "<tr><td>";
+               printImageHREF ('add', 'allocate', TRUE);
+               echo "</td>";
+               echo "<td class=tdleft><input type='text' size='10' name='bond_name' tabindex=100></td>\n";
+               echo "<td class=tdleft><input type=text name='ip' tabindex=101></td>\n";
+               echo "<td colspan=3>&nbsp;</td><td>";
+               printSelect ($aat, 'bond_type', NULL, 102);
+               echo "</td><td>&nbsp;</td><td>";
+               printImageHREF ('add', 'allocate', TRUE, 103);
+               echo "</td></tr></form>";
+       }
+       global $pageno, $tabno, $aat;
        if ($object_id <= 0)
        {
                showError ('Invalid object_id', __FUNCTION__);
@@ -1149,51 +1219,56 @@ function renderIPv4ForObject ($object_id = 0)
        if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
                echo '<th colspan=2>network</th><th>routed by</th>';
        echo '<th>type</th><th>misc</th><th>&nbsp</th></tr>';
+
+       if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
+               printNewItemTR();
        foreach ($alloclist as $dottedquad => $alloc)
        {
                $class = $alloc['addrinfo']['class'];
                $netid = getIPv4AddressNetworkId ($dottedquad);
-               $netinfo = getIPv4NetworkInfo ($netid);
+               if (NULL !== $netid)
+               {
+                       $netinfo = getIPv4NetworkInfo ($netid);
+                       loadIPv4AddrList ($netinfo);
+               }
                printOpFormIntro ('updIPv4Allocation', array ('ip' => $dottedquad));
-               echo "<tr class='$class' valign=top><td><a href='${root}process.php?op=delIPv4Allocation&page=${pageno}&tab=${tabno}&ip=${dottedquad}&object_id=$object_id'>";
+               echo "<tr class='$class' valign=top><td><a href='".makeHrefProcess(array('op'=>'delIPv4Allocation', 'ip'=>$dottedquad, 'object_id'=>$object_id))."'>";
                printImageHREF ('delete', 'Delete this IPv4 address');
                echo "</a></td>";
-               echo "<td class=tdleft><input type='text' name='bond_name' value='${alloc['osif']}' size=10></td>";
-               echo "<td class=tdleft><a href='${root}?page=ipaddress&ip=${dottedquad}'>${dottedquad}</a>";
+               echo "<td class=tdleft><input type='text' name='bond_name' value='${alloc['osif']}' size=10></td><td class=tdleft>";
+               if (NULL !== $netid)
+                       echo "<a href='".makeHref(array('page'=>'ipaddress', 'ip'=>$dottedquad))."'>${dottedquad}</a>";
+               else
+                       echo $dottedquad;
                if (getConfigVar ('EXT_IPV4_VIEW') != 'yes')
-               {
-                       if (NULL === $netid)
-                               $suffix = '/??';
-                       else
-                               echo '<small>/' . $netinfo['mask'] . '</small>';
-               }
-               echo '&nbsp;' . $aac[$alloc['type']];
+                       echo '<small>/' . (NULL === $netid ? '??' : $netinfo['mask']) . '</small>';
                if (!empty ($alloc['addrinfo']['name']))
-                       echo '(' . niftyString ($alloc['addrinfo']['name']) . ')';
+                       echo ' (' . niftyString ($alloc['addrinfo']['name']) . ')';
                echo '</td>';
                // FIXME: this a copy-and-paste from renderRackObject()
                if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
                {
                        if (NULL === $netid)
-                               echo '<td colspan=2>?</td>';
+                               echo '<td colspan=2>N/A</td><td>&nbsp;</td>';
                        else
-                               printIPv4NetInfoTDs ($netinfo);
-                       echo "<td class='${secondclass}'>";
-                       // FIXME: These cals are really heavy, replace them with a more appropriate dedicated function.
-                       $netdata = getIPv4Network ($netid);
-                       $newline = '';
-                       foreach (findRouters ($netdata['addrlist']) as $router)
                        {
-                               if ($router['id'] == $object_id)
-                                       continue;
-                               echo $newline . $router['addr'] . ", <a href='${root}?page=object&object_id=${router['id']}&hl_ipv4_addr=${router['addr']}'>";
-                               echo (empty ($router['iface']) ? '' : $router['iface'] . '@') . $router['dname'] . '</a>';
-                               $routertags = loadRackObjectTags ($router['id']);
-                               if (count ($routertags))
-                                       echo '<br><small>' . serializeTags ($routertags, "${root}?page=objects&tab=default&") . '</small>';
-                               $newline = '<br>';
+                               printIPv4NetInfoTDs ($netinfo);
+                               echo "<td class=tdleft>";
+                               // FIXME: These calls are really heavy, replace them with a more appropriate dedicated function.
+                               $newline = '';
+                               foreach (findRouters ($netinfo['addrlist']) as $router)
+                               {
+                                       if ($router['id'] == $object_id)
+                                               continue;
+                                       echo $newline . $router['addr'] . ", <a href='".makeHref(array('page'=>'object', 'object_id'=>$router['id'], 'hl_ipv4_addr'=>$router['addr']))."'>";
+                                       echo (empty ($router['iface']) ? '' : $router['iface'] . '@') . $router['dname'] . '</a>';
+                                       $routertags = loadRackObjectTags ($router['id']);
+                                       if (count ($routertags))
+                                               echo '<br><small>' . serializeTags ($routertags, makeHref(array('page'=>'objects', 'tab'=>'default'))."&") . '</small>';
+                                       $newline = '<br>';
+                               }
+                               echo '</td>';
                        }
-                       echo '</td>';
                }
                echo '<td>';
                printSelect ($aat, 'bond_type', $alloc['type']);
@@ -1208,7 +1283,7 @@ function renderIPv4ForObject ($object_id = 0)
                {
                        if ($allocpeer['object_id'] == $object_id)
                                continue;
-                       echo $prefix . "<a href='${root}?page=object&object_id=${allocpeer['object_id']}'>";
+                       echo $prefix . "<a href='".makeHref(array('page'=>'object', 'object_id'=>$allocpeer['object_id']))."'>";
                        if (!empty ($allocpeer['osif']))
                                echo $allocpeer['osif'] . '@';
                        echo $allocpeer['object_name'] . '</a>';
@@ -1218,16 +1293,10 @@ function renderIPv4ForObject ($object_id = 0)
                printImageHREF ('save', 'Save changes', TRUE);
                echo "</td></form></tr>\n";
        }
+       if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
+               printNewItemTR();
 
-       printOpFormIntro ('addIPv4Allocation');
-       echo "<tr><td>";
-       printImageHREF ('add', 'Allocate new address', TRUE, 99);
-       echo "</td>";
-       echo "<td class=tdleft><input type='text' size='10' name='bond_name' tabindex=100></td>\n";
-       echo "<td class=tdleft><input type=text name='ip' tabindex=101></td>\n";
-       echo "<td colspan=3>&nbsp;</td><td>";
-       printSelect ($aat, 'bond_type');
-       echo "</td><td colspan=2>&nbsp;</td></tr></form></table><br>\n";
+       echo "</table><br>\n";
        finishPortlet();
 
 }
@@ -1290,8 +1359,8 @@ function printLog ($log)
                                38 => array ('code' => 'success', 'format' => '%u real server(s) were successfully (de)activated'),
                                39 => array ('code' => 'success', 'format' => 'User account %s updated.'),
                                40 => array ('code' => 'success', 'format' => 'User account %s created.'),
-                               41 => array ('code' => 'success', 'format' => 'User account disabled.'),
-                               42 => array ('code' => 'success', 'format' => 'User account enabled.'),
+// ...
+// ...
                                43 => array ('code' => 'success', 'format' => 'Saved successfully.'),
                                44 => array ('code' => 'success', 'format' => '%s failures and %s successfull changes.'),
                                45 => array ('code' => 'success', 'format' => "Attribute '%s' created."),
@@ -1318,15 +1387,23 @@ function printLog ($log)
                                66 => array ('code' => 'success', 'format' => "File sent Ok via handler '%s'"),
                                67 => array ('code' => 'success', 'format' => "Tag rolling done, %u objects involved"),
                                68 => array ('code' => 'success', 'format' => "Updated rack '%s'"),
+                               69 => array ('code' => 'success', 'format' => 'File %s was added successfully'),
+                               70 => array ('code' => 'success', 'format' => 'File %s was updated successfully'),
+                               71 => array ('code' => 'success', 'format' => 'File %s was linked successfully'),
+                               72 => array ('code' => 'success', 'format' => 'File %s was unlinked successfully'),
+                               73 => array ('code' => 'success', 'format' => 'File %s was deleted successfully'),
+                               74 => array ('code' => 'success', 'format' => 'Row %s was added successfully'),
+                               75 => array ('code' => 'success', 'format' => 'Row %s was updated successfully'),
+                               76 => array ('code' => 'success', 'format' => 'Object %s was deleted successfully'),
 
                                100 => array ('code' => 'error', 'format' => 'Generic error: %s'),
                                101 => array ('code' => 'error', 'format' => 'Port name cannot be empty'),
                                102 => array ('code' => 'error', 'format' => "Error creating user account '%s'"),
                                103 => array ('code' => 'error', 'format' => 'getHashByID() failed'),
                                104 => array ('code' => 'error', 'format' => "Error updating user account '%s'"),
-                               105 => array ('code' => 'error', 'format' => 'Error enabling user account.'),
-                               106 => array ('code' => 'error', 'format' => 'Error disabling user account.'),
-                               107 => array ('code' => 'error', 'format' => 'Admin account cannot be disabled'),
+// ...
+// ...
+// ...
                                108 => array ('code' => 'error', 'format' => '%u failures and %u successfull changes.'),
                                109 => array ('code' => 'error', 'format' => 'Update failed!'),
                                110 => array ('code' => 'error', 'format' => 'Supplement failed!'),
@@ -1397,6 +1474,7 @@ function printLog ($log)
                                175 => array ('code' => 'error', 'format' => 'Invalid netmask'),
                                176 => array ('code' => 'error', 'format' => 'This network already exists'),
                                177 => array ('code' => 'error', 'format' => 'commitUpdateRack() failed'),
+                               178 => array ('code' => 'error', 'format' => 'file not found'),
 
                                200 => array ('code' => 'warning', 'format' => 'generic warning: %s'),
                                201 => array ('code' => 'warning', 'format' => 'nothing happened...'),
@@ -1404,6 +1482,7 @@ function printLog ($log)
                                203 => array ('code' => 'warning', 'format' => 'Port %s seems to be the first in VLAN %u at this switch.'),
                                204 => array ('code' => 'warning', 'format' => 'Check uplink/downlink configuration for proper operation.'),
                                205 => array ('code' => 'warning', 'format' => '%u change request(s) have been ignored'),
+                               206 => array ('code' => 'success', 'format' => 'Row %s was deleted successfully'),
                        );
                        // Handle the arguments. Is there any better way to do it?
                        foreach ($log['m'] as $record)
@@ -1520,56 +1599,11 @@ function renderRackSpaceForObject ($object_id = 0)
        // Now we workaround an old caveat: http://bugs.php.net/bug.php?id=37410
        unset ($rackData);
 
-       // Here we process form submit by trying to save all submitted info to database.
-       if ($is_submit)
-       {
-               $oldMolecule = getMoleculeForObject ($object_id);
-               $worldchanged = FALSE;
-               $log = array();
-               foreach ($workingRacksData as $rack_id => $rackData)
-               {
-                       $logrecord = processGridForm ($rackData, 'F', 'T', $object_id);
-                       $log[] = $logrecord;
-                       if ($logrecord['code'] != 300)
-                       {
-                               $worldchanged = TRUE;
-                               // Reload our working copy after form processing.
-                               $rackData = getRackData ($rack_id);
-                               if ($rackData == NULL)
-                                       $log[] = array ('code' => 500, 'message' => 'Working copy update failed in ', __FUNCTION__);
-                               applyObjectMountMask ($rackData, $object_id);
-                               $workingRacksData[$rack_id] = $rackData;
-                       }
-               }
-               if ($worldchanged)
-               {
-                       // Log a record.
-                       $newMolecule = getMoleculeForObject ($object_id);
-                       $oc = count ($oldMolecule);
-                       $nc = count ($newMolecule);
-                       $omid = $oc ? createMolecule ($oldMolecule) : 'NULL';
-                       $nmid = $nc ? createMolecule ($newMolecule) : 'NULL';
-                       global $remote_username;
-                       $comment = empty ($_REQUEST['comment']) ? 'NULL' : "'${_REQUEST['comment']}'";
-                       $query =
-                               "insert into MountOperation(object_id, old_molecule_id, new_molecule_id, user_name, comment) " .
-                               "values (${object_id}, ${omid}, ${nmid}, '${remote_username}', ${comment})";
-                       global $dbxlink;
-                       $result = $dbxlink->query ($query);
-                       if ($result == NULL)
-                               $log[] = array ('code' => 'error', 'message' => 'SQL query failed during history logging.');
-                       else
-                               $log[] = array ('code' => 'success', 'message' => 'History logged.');
-               }
-               printLog ($log);
-       }
-
        // This is the time for rendering.
-       global $root, $pageno, $tabno;
-       echo "<form id='racks' action='${root}'>";
-       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";
+       global $pageno, $tabno;
+
+       printOpFormIntro ('updateObjectAllocation');
+
        // Main layout starts.
        echo "<table border=0 class=objectview cellspacing=0 cellpadding=0><tr>";
 
@@ -1693,16 +1727,16 @@ function renderUnmountedObjectsPortlet ()
                showError ('getUnmountedObjects() failed', __FUNCTION__);
                return;
        }
-       global $root, $nextorder;
+       global $nextorder;
        $order = 'odd';
        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 number</th><th>Barcode</th></tr>';
+       echo '<tr><th>Common name</th><th>Visible label</th><th>Asset number</th></tr>';
        foreach ($objs as $obj)
        {
-               echo "<tr class=row_${order}><td><a href='${root}?page=object&object_id=${obj['id']}'>${obj['dname']}</a></td>";
+               echo "<tr class=row_${order}><td><a href='".makeHref(array('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>";
+               echo "</tr>";
                $order = $nextorder[$order];
        }
        echo "</table><br>\n";
@@ -1718,14 +1752,14 @@ function renderProblematicObjectsPortlet ()
                showError ('getProblematicObjects() failed', __FUNCTION__);
                return;
        }
-       global $root, $nextorder;
+       global $nextorder;
        $order = 'odd';
        echo '<br><br><table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
        echo '<tr><th>Type</th><th>Common name</th></tr>';
        foreach ($objs as $obj)
        {
                echo "<tr class=row_${order}><td>${obj['objtype_name']}</td>";
-               echo "<td><a href='${root}?page=object&object_id=${obj['id']}'>${obj['dname']}</a></tr>";
+               echo "<td><a href='".makeHref(array('page'=>'object', 'object_id'=>$obj['id']))."'>${obj['dname']}</a></tr>";
                $order = $nextorder[$order];
        }
        echo "</table><br>\n";
@@ -1734,7 +1768,8 @@ function renderProblematicObjectsPortlet ()
 
 function renderObjectSpace ()
 {
-       global $root, $taglist, $tagtree;
+       global $taglist, $tagtree;
+       showMessageOrError();
        echo "<table border=0 class=objectview>\n";
        echo "<tr><td class=pcleft width='50%'>";
        startPortlet ('View all by type');
@@ -1750,7 +1785,7 @@ function renderObjectSpace ()
        {
                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 "<li><a href='".makeHref(array('page'=>'objgroup', 'group_id'=>$gi['id']))."'>${gi['name']}</a> (${gi['count']})</li>";
                echo '</ul></div>';
        }
        finishPortlet();
@@ -1768,7 +1803,8 @@ function renderObjectSpace ()
 
 function renderObjectGroup ()
 {
-       global $root, $pageno, $tabno, $nextorder, $taglist, $tagtree;
+       global $pageno, $tabno, $nextorder, $taglist, $tagtree;
+       showMessageOrError();
        assertUIntArg ('group_id', __FUNCTION__, TRUE);
        $group_id = $_REQUEST['group_id'];
        $tagfilter = getTagFilter();
@@ -1789,7 +1825,7 @@ function renderObjectGroup ()
                echo '<div align=left><ul>';
                foreach ($groupInfo as $gi)
                {
-                       echo "<li><a href='${root}?page=${pageno}&group_id=${gi['id']}${tagfilter_str}'>";
+                       echo "<li><a href='".makeHref(array('page'=>$pageno, 'group_id'=>$gi['id']))."${tagfilter_str}'>";
                        if ($gi['id'] == $group_id)
                                echo '<strong>';
                        echo "${gi['name']}</a>";
@@ -1806,15 +1842,15 @@ function renderObjectGroup ()
 
        echo '</td><td class=pcleft>';
 
-       startPortlet ('Objects');
        $objects = getObjectList ($group_id, $tagfilter, getTFMode());
+       startPortlet ('Objects (' . count ($objects) . ')');
        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>';
+       echo '<tr><th>Common name</th><th>Visible label</th><th>Asset tag</th><th>Row/Rack</th><th></th></tr>';
        $order = 'odd';
        foreach ($objects as $obj)
        {
@@ -1822,12 +1858,14 @@ function renderObjectGroup ()
                        $secondclass = 'tdleft port_highlight';
                else
                        $secondclass = 'tdleft';
-               echo "<tr class=row_${order}><td class='${secondclass}'><a href='${root}?page=object&object_id=${obj['id']}'>${obj['dname']}</a></td>";
-               echo "<td class='${secondclass}'>${obj['label']}</td>";
+               $tags = loadRackObjectTags ($obj['id']);
+               echo "<tr class=row_${order} valign=top><td class='${secondclass}'><a href='".makeHref(array('page'=>'object', 'object_id'=>$obj['id']))."'><strong>${obj['dname']}</strong></a>";
+               if (count ($tags))
+                       echo '<br><small>' . serializeTags ($tags, makeHref(array('page'=>$pageno, 'tab'=>'default', 'group_id'=>$group_id))."&") . '</small>';
+               echo "</td><td class='${secondclass}'>${obj['label']}</td>";
                echo "<td class='${secondclass}'>${obj['asset_no']}</td>";
-               echo "<td class='${secondclass}'>${obj['barcode']}</td>";
                if ($obj['rack_id'])
-                       echo "<td class='${secondclass}'><a href='${root}?page=rack&rack_id=${obj['rack_id']}'>${obj['Rack_name']}</a></td>";
+                       echo "<td class='${secondclass}'><a href='".makeHref(array('page'=>'row', 'row_id'=>$obj['row_id']))."'>${obj['Row_name']}</a>/<a href='".makeHref(array('page'=>'rack', 'rack_id'=>$obj['rack_id']))."'>${obj['Rack_name']}</a></td>";
                else
                        echo "<td class='${secondclass}'>Unmounted</td>";
                echo '</tr>';
@@ -1878,17 +1916,17 @@ function renderHistory ($object_type, $object_id)
                        $query =
                                "select ctime, user_name, rh.name, rh.deleted, d.dict_value as name, rh.height, rh.comment " .
                                "from RackHistory as rh left join Dictionary as d on rh.row_id = d.dict_key " .
-                               "natural join Chapter " .
-                               "where chapter_name = 'RackRow' and rh.id = ${object_id} order by ctime";
+                               "join Chapter on Dictionary.chapter_id = Chapter.id " .
+                               "where Chapter.name = 'RackRow' and rh.id = ${object_id} order by ctime";
                        $header = '<tr><th>change time</th><th>author</th><th>rack name</th><th>is deleted?</th><th>rack row name</th><th>rack height</th><th>rack comment</th></tr>';
                        $extra = 6;
                        break;
                case 'object':
                        $query =
-                               "select ctime, user_name, name, label, barcode, asset_no, deleted, has_problems, dict_value, comment " .
-                               "from RackObjectHistory inner join Dictionary on objtype_id = dict_key natural join Chapter " .
-                               "where chapter_name = 'RackObjectType' and id=${object_id} order by ctime";
-                       $header = '<tr><th>change time</th><th>author</th><th>common name</th><th>visible label</th><th>barcode</th><th>asset no</th><th>is deleted?</th><th>has problems?</th><th>object type</th><th>comment</th></tr>';
+                               "select ctime, user_name, RackObjectHistory.name as name, label, asset_no, deleted, has_problems, dict_value, comment " .
+                               "from RackObjectHistory inner join Dictionary on objtype_id = dict_key join Chapter on Dictionary.chapter_id = Chapter.id " .
+                               "where Chapter.name = 'RackObjectType' and RackObjectHistory.id=${object_id} order by ctime";
+                       $header = '<tr><th>change time</th><th>author</th><th>common name</th><th>visible label</th><th>asset no</th><th>is deleted?</th><th>has problems?</th><th>object type</th><th>comment</th></tr>';
                        $extra = 9;
                        break;
                default:
@@ -1919,7 +1957,7 @@ function renderHistory ($object_type, $object_id)
 
 function renderRackspaceHistory ()
 {
-       global $root, $nextorder, $pageno, $tabno;
+       global $nextorder, $pageno, $tabno;
        $order = 'odd';
        $history = getRackspaceHistory();
        // Show the last operation by default.
@@ -1962,9 +2000,9 @@ function renderRackspaceHistory ()
        else
                echo "nothing";
        finishPortlet();
-       
+
        echo '</td></tr><tr><td colspan=2>';
-       
+
        // Bottom portlet with list
 
        startPortlet ('Rackspace allocation history');
@@ -1976,7 +2014,7 @@ function renderRackspaceHistory ()
                        $class = 'hl';
                else
                        $class = "row_${order}";
-               echo "<tr class=${class}><td><a href='${root}?page=${pageno}&tab=${tabno}&op_id=${row['mo_id']}'>${row['ctime']}</a></td>";
+               echo "<tr class=${class}><td><a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'op_id'=>$row['mo_id']))."'>${row['ctime']}</a></td>";
                echo "<td>${row['user_name']}</td>";
                echo "<td>${row['ro_id']}</td><td>${row['objtype_name']}</td><td>${row['name']}</td><td>${row['comment']}</td>\n";
                echo "</tr>\n";
@@ -1984,49 +2022,103 @@ function renderRackspaceHistory ()
        }
        echo "</table>\n";
        finishPortlet();
-       
+
        echo '</td></tr></table>';
-       
 }
 
-function renderIPv4SpaceRecords ($tree, $level = 0, &$tagcache = array())
+function renderIPv4SpaceRecords ($tree, &$tagcache, $baseurl, $target = 0, $level = 1)
 {
        $self = __FUNCTION__;
        foreach ($tree as $item)
        {
-               $netdata = getIPv4Network ($item['id']);
-               $total = ($netdata['ip_bin'] | $netdata['mask_bin_inv']) - ($netdata['ip_bin'] & $netdata['mask_bin']) + 1;
-               $used = count ($netdata['addrlist']);
-               echo "<tr valign=top>";
-               printIPv4NetInfoTDs ($netdata, 'tdleft', $level);
-               echo "<td class=tdcenter>";
-               renderProgressBar ($used/$total);
-               echo "<br><small>${used}/${total}</small></td>";
-               if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
-                       printRoutersTD (findRouters ($netdata['addrlist']), $tagcache);
-               echo "</tr>";
-               $self ($item['kids'], $level + 1, $tagcache);
+               $total = $item['addrt'];
+               if (getConfigVar ('IPV4_TREE_SHOW_USAGE') == 'yes')
+               {
+                       loadIPv4AddrList ($item); // necessary to compute router list and address counter
+                       $used = $item['addrc'];
+               }
+               else
+               {
+                       $item['addrlist'] = array();
+                       $item['addrc'] = 0;
+               }
+               if (isset ($item['id']))
+               {
+                       if ($item['symbol'] == 'node-collapsed')
+                               $expandurl = "${baseurl}&eid=" . $item['id'] . "#netid" . $item['id'];
+                       elseif ($item['symbol'] == 'node-expanded')
+                               $expandurl = $baseurl . ($item['parent_id'] ? "&eid=${item['parent_id']}#netid${item['parent_id']}" : '');
+                       else
+                               $expandurl = '';
+                       echo "<tr valign=top>";
+                       printIPv4NetInfoTDs ($item, 'tdleft', $level, $item['symbol'], $expandurl);
+                       echo "<td class=tdcenter>";
+                       if ($target == $item['id'])
+                               echo "<a name=netid${target}></a>";
+                       if (getConfigVar ('IPV4_TREE_SHOW_USAGE') == 'yes')
+                       {
+                               renderProgressBar ($total ? $used/$total : 0);
+                               echo "<br><small>${used}/${total}</small>";
+                       }
+                       else
+                               echo "<small>${total}</small>";
+                       echo "</td>";
+                       if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
+                               printRoutersTD (findRouters ($item['addrlist']), $tagcache);
+                       echo "</tr>";
+                       if ($item['symbol'] == 'node-expanded' or $item['symbol'] == 'node-expanded-static')
+                               $self ($item['kids'], $tagcache, $baseurl, $target, $level + 1);
+               }
+               else
+               {
+                       echo "<tr valign=top>";
+                       printIPv4NetInfoTDs ($item, 'tdleft sparenetwork', $level, $item['symbol']);
+                       echo "<td class=tdcenter>";
+                       if (getConfigVar ('IPV4_TREE_SHOW_USAGE') == 'yes')
+                       {
+                               renderProgressBar ($used/$total, 'sparenetwork');
+                               echo "<br><small>${used}/${total}</small>";
+                       }
+                       else
+                               echo "<small>${total}</small>";
+                       echo "</td><td>&nbsp;</td></tr>";
+               }
        }
 }
 
 function renderIPv4Space ()
 {
-       global $root, $pageno;
+       global $pageno, $tabno;
+       $tagfilter = getTagFilter();
+       $netlist = getIPv4NetworkList ($tagfilter, getTFMode());
+
+
+       $netcount = count ($netlist);
+       // expand request can take either natural values or "ALL". Zero means no expanding.
+       $eid = isset ($_REQUEST['eid']) ? $_REQUEST['eid'] : 0;
+       $tree = prepareIPv4Tree ($netlist, $eid);
 
        echo "<table border=0 class=objectview>\n";
        echo "<tr><td class=pcleft>";
-
-       $tagfilter = getTagFilter();
-       $tree = treeFromList (getIPv4NetworkList ($tagfilter, getTFMode()));
-       sortTree ($tree, 'IPv4NetworkCmp');
-       // FIXME: the counter is wrong
-       startPortlet ('networks (' . count ($tree) . ')');
-       echo "<table class='widetable' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
-       echo "<tr><th>prefix</th><th>name/tags</th><th>%% used</th>";
+       startPortlet ("networks (${netcount})");
+       echo '<h4>';
+       if ($eid === 0)
+               echo 'auto-collapsing at threshold ' . getConfigVar ('TREE_THRESHOLD') . " (<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'eid'=>'ALL'))."'>expand all</a>)";
+       elseif ($eid === 'ALL')
+               echo "expanding all (<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno))."'>auto-collapse</a>)";
+       else
+       {
+               $netinfo = getIPv4NetworkInfo ($eid);
+               echo "expanding ${netinfo['ip']}/${netinfo['mask']} (<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno))."'>auto-collapse</a> / <a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'eid'=>'ALL'))."'>expand&nbsp;all</a>)"; 
+       }
+       echo "</h4><table class='widetable' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
+       echo "<tr><th>prefix</th><th>name/tags</th><th>capacity</th>";
        if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
                echo "<th>routed by</th>";
        echo "</tr>\n";
-       renderIPv4SpaceRecords ($tree);
+       $tagcache = array();
+       $baseurl = makeHref(array('page'=>$pageno, 'tab'=>$tabno)) . getTagFilterStr ($tagfilter);
+       renderIPv4SpaceRecords ($tree, $tagcache, $baseurl, $eid);
        echo "</table>\n";
        finishPortlet();
        echo '</td><td class=pcright>';
@@ -2036,12 +2128,12 @@ function renderIPv4Space ()
 
 function renderIPv4SLB ()
 {
-       global $root, $page, $nextorder;
+       global $page, $nextorder;
 
        startPortlet ('SLB configuration');
        echo "<table border=0 width='100%'><tr>";
        foreach (array ('ipv4vslist', 'ipv4rsplist', 'rservers', 'lbs') as $pno)
-               echo "<td><h3><a href='${root}?page=${pno}'>" . $page[$pno]['title'] . "</a></h3></td>";
+               echo "<td><h3><a href='".makeHref(array('page'=>$pno))."'>" . $page[$pno]['title'] . "</a></h3></td>";
        echo '</tr></table>';
        finishPortlet();
 
@@ -2065,17 +2157,19 @@ function renderIPv4SLB ()
        {
                $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>";
+               echo "<tr valign=top><td>&nbsp;</td>";
                foreach ($lblist as $lb_object_id)
-                       echo "<th><a href='${root}?page=object&tab=default&object_id=${lb_object_id}'>" . $lbdname[$lb_object_id]  . "</a></th>";
+               {
+                       #echo "<th><a href='".makeHref(array('page'=>'object', 'tab'=>'default', 'object_id'=>$lb_object_id))."'>" . $lbdname[$lb_object_id]  . "</a></th>";
+                       echo '<td>';
+                       renderLBCell ($lb_object_id);
+                       echo '</td>';
+               }
                echo "</tr>\n";
                foreach ($summary as $vsid => $vsdata)
                {
-                       echo "<tr class=row_${order}><td class=tdleft><a href='$root?page=ipv4vs&tab=default&vs_id=${vsid}'>";
-                       echo buildVServiceName ($vsdata);
-                       echo '</a>';
-                       if (!empty ($vsdata['name']))
-                               echo "<br>${vsdata['name']}";
+                       echo "<tr class=row_${order}><td class=tdleft>";
+                       renderVSCell ($vsid);
                        echo "</td>";
                        foreach ($lblist as $lb_object_id)
                        {
@@ -2085,8 +2179,7 @@ function renderIPv4SLB ()
                                else
                                {
                                        echo $vsdata['lblist'][$lb_object_id]['size'];
-//                                     echo " (<a href='${root}?page=ipv4rsp&pool_id=";
-//                                     echo $vsdata['lblist'][$lb_object_id]['id'] . "'>";
+//                                     echo " (<a href='".makeHref(array('page'=>'ipv4rspool', 'pool_id'=>$vsdata['lblist'][$lb_object_id]['id'])). "'>";
 //                                     echo $vsdata['lblist'][$lb_object_id]['name'] . '</a>)';
                                }
                                echo '</td>';
@@ -2101,7 +2194,7 @@ function renderIPv4SLB ()
 
 function renderIPv4SpaceEditor ()
 {
-       global $root, $pageno, $tabno;
+       global $pageno, $tabno;
        showMessageOrError();
 
        // IPv4 validator
@@ -2120,10 +2213,8 @@ function renderIPv4SpaceEditor ()
        echo '<table border=0 cellpadding=10 align=center>';
        // This form requires a name, so JavaScript validator can find it.
        // No printOpFormIntro() hence
-       echo "<form method=post name='add_new_range' action='${root}process.php'>\n";
+       echo "<form method=post name='add_new_range' action='".makeHrefProcess()."'>\n";
        echo "<input type=hidden name=op value=addIPv4Prefix>\n";
-       echo "<input type=hidden name=page value='${pageno}'>\n";
-       echo "<input type=hidden name=tab value='${tabno}'>\n";
        // tags column
        echo '<tr><td rowspan=4><h3>assign tags</h3>';
        renderTagSelect();
@@ -2131,36 +2222,27 @@ function renderIPv4SpaceEditor ()
        // inputs column
        echo "<th class=tdright>prefix</th><td class=tdleft><input type=text name='range' size=18 class='live-validate' tabindex=1></td>";
        echo "<tr><th class=tdright>name</th><td class=tdleft><input type=text name='name' size='20' tabindex=2></td></tr>";
-       echo "<tr><th class=tdright>connected network</th><td class=tdleft><input type=checkbox name='is_bcast' tabindex=3 checked></td></tr>";
+       echo "<tr><th class=tdright>connected network</th><td class=tdleft><input type=checkbox name='is_bcast' tabindex=3></td></tr>";
        echo "<tr><td colspan=2>";
        printImageHREF ('CREATE', 'Add a new network', TRUE, 4);
        echo '</td></tr>';
        echo "</form></table><br><br>\n";
        finishPortlet();
 
-       $addrspaceList = getAddressspaceList();
-       if (count ($addrspaceList))
+       $addrspaceList = getIPv4NetworkList();
+       $netcount = count ($addrspaceList);
+       if ($netcount)
        {
-               startPortlet ('Manage existing (' . count ($addrspaceList) . ')');
+               startPortlet ("Manage existing (${netcount})");
                echo "<table class='widetable' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
                echo "<tr><th>&nbsp;</th><th>prefix</th><th>name</th><th>&nbsp;</th></tr>";
-               foreach ($addrspaceList as $iprange)
+               foreach ($addrspaceList as $netinfo)
                {
-                       $netdata = getIPv4Network ($iprange['id']);
-                       $usedips = count ($netdata['addrlist']);
-                       $totalips = ($netdata['ip_bin'] | $netdata['mask_bin_inv']) - ($netdata['ip_bin'] & $netdata['mask_bin']) + 1;
-                       echo "<form method=post action='${root}process.php?page=${pageno}&tab=${tabno}&op=updIPv4Prefix&id=${iprange['id']}'>";
-                       echo '<tr valign=top><td>';
-                       if ($usedips == 0)
-                       {
-                               echo "<a href='${root}process.php?op=delIPv4Prefix&page=${pageno}&tab=${tabno}&id=${iprange['id']}'>";
-                               printImageHREF ('delete', 'Delete this IP range');
-                               echo "</a>";
-                       }
-                       else
-                               printImageHREF ('nodelete', 'There are IP addresses allocated or reserved');
-                       echo "</td>\n<td class=tdleft>${netdata['ip']}/${netdata['mask']}</td>";
-                       echo "<td><input type=text name=name size=40 value='${netdata['name']}'>";
+                       echo "<form method=post action='".makeHrefProcess(array($op=>'updIPv4Prefix', 'id'=>$netinfo['id']))."'>";
+                       echo "<tr valign=top><td><a href='".makeHrefProcess(array('op'=>'delIPv4Prefix', 'id'=>$netinfo['id']))."'>";
+                       printImageHREF ('delete', 'Delete this IP range');
+                       echo "</a></td>\n<td class=tdleft>${netinfo['ip']}/${netinfo['mask']}</td>";
+                       echo "<td><input type=text name=name size=40 value='${netinfo['name']}'>";
                        echo "</td><td>";
                        printImageHREF ('save', 'Save changes', TRUE);
                        echo "</td></tr></form>\n";
@@ -2172,7 +2254,7 @@ function renderIPv4SpaceEditor ()
 
 function renderIPv4Network ($id)
 {
-       global $root, $pageno, $tabno, $aac2;
+       global $pageno, $tabno, $aac2;
        $netmaskbylen = array
        (
                32 => '255.255.255.255',
@@ -2249,7 +2331,8 @@ function renderIPv4Network ($id)
        else
                $page=0;
 
-       $range = getIPv4Network ($id);
+       $range = getIPv4NetworkInfo ($id);
+       loadIPv4AddrList ($range);
        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";
 
@@ -2263,10 +2346,41 @@ function renderIPv4Network ($id)
        renderProgressBar ($used/$total);
        echo "&nbsp;${used}/${total}</td></tr>\n";
 
+       if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
+       {
+               // Build a backtrace from all parent networks.
+               $clen = $range['mask'];
+               $backtrace = array();
+               while (NULL !== ($upperid = getIPv4AddressNetworkId ($range['ip'], $clen)))
+               {
+                       $upperinfo = getIPv4NetworkInfo ($upperid);
+                       $clen = $upperinfo['mask'];
+                       $backtrace[] = $upperid;
+               }
+               $arrows = count ($backtrace);
+               foreach (array_reverse ($backtrace) as $ancestorid)
+               {
+                       $ainfo = getIPv4NetworkInfo ($ancestorid);
+                       echo "<tr><th width='50%' class=tdright>";
+                       for ($i = 0; $i < $arrows; $i++)
+                               echo '&uarr;';
+                       $arrows--;
+                       echo "</th><td class=tdleft><a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'id'=>$ainfo['id']))."'>${ainfo['ip']}/${ainfo['mask']}</a></td></tr>";
+               }
+               echo "<tr><th width='50%' class=tdright>&rarr;</th>";
+               echo "<td class=tdleft>${range['ip']}/${range['mask']}</td></tr>";
+               // FIXME: get and display nested networks
+               // $theitem = pickLeaf ($ipv4tree, $id);
+       }
+
        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>Netmask:</th><td class=tdleft>";
+       printf ('0x%08X', binMaskFromDec ($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";
@@ -2279,9 +2393,11 @@ function renderIPv4Network ($id)
                echo "</tr>\n";
        }
 
-       printTagTRs ("${root}?page=ipv4space&");
+       printTagTRs (makeHref(array('page'=>'ipv4space', 'tab'=>'default'))."&");
        echo "</table><br>\n";
        finishPortlet();
+
+       renderFilesPortlet ('ipv4net', $id);
        echo "</td>\n";
 
        echo "<td class=pcright>";
@@ -2305,7 +2421,7 @@ function renderIPv4Network ($id)
                if ($i == $page)
                        echo "<b>$i</b> ";
                else
-                       echo "<a href='${root}?page=${pageno}&tab=${tabno}&id=$id&pg=$i'>$i</a> ";
+                       echo "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'id'=>$id, 'pg'=>$i))."'>$i</a> ";
        }
        echo "</center>";
 
@@ -2320,14 +2436,14 @@ function renderIPv4Network ($id)
                        $secondstyle = 'tdleft';
                if (!isset ($range['addrlist'][$ip]))
                {
-                       echo "<tr><td class=tdleft><a href='${root}?page=ipaddress&ip=" . long2ip ($ip) . "'>" . long2ip ($ip);
+                       echo "<tr><td class=tdleft><a href='".makeHref(array('page'=>'ipaddress', 'ip'=>ip_long2quad($ip)))."'>" . ip_long2quad($ip);
                        echo "</a></td><td class='${secondstyle}'>&nbsp;</td><td class='${secondstyle}'>&nbsp;</td></tr>\n";
                        continue;
                }
                $addr = $range['addrlist'][$ip];
                echo "<tr class='${addr['class']}'>";
 
-               echo "<td class=tdleft><a href='${root}?page=ipaddress&ip=${addr['ip']}'>${addr['ip']}</a></td>";
+               echo "<td class=tdleft><a href='".makeHref(array('page'=>'ipaddress', 'ip'=>$addr['ip']))."'>${addr['ip']}</a></td>";
                echo "<td class='${secondstyle}'>${addr['name']}</td><td class='${secondstyle}'>";
                $delim = '';
                $prologue = '';
@@ -2339,8 +2455,7 @@ function renderIPv4Network ($id)
                foreach ($range['addrlist'][$ip]['allocs'] as $ref)
                {
                        echo $delim . $aac2[$ref['type']];
-                       echo "<a href='${root}?page=object&object_id=${ref['object_id']}";
-                       echo "&hl_ipv4_addr=${addr['ip']}'>";
+                       echo "<a href='".makeHref(array('page'=>'object', 'object_id'=>$ref['object_id'], 'hl_ipv4_addr'=>$addr['ip']))."'>";
                        echo $ref['name'] . (empty ($ref['name']) ? '' : '@');
                        echo "${ref['object_name']}</a>";
                        $delim = '; ';
@@ -2354,8 +2469,8 @@ function renderIPv4Network ($id)
                {
                        echo $prologue;
                        $prologue = '';
-                       echo "${delim}<a href='${root}?page=object&object_id=${ref['object_id']}'>";
-                       echo "${ref['object_name']}</a>:<a href='${root}?page=ipv4vs&vs_id=${ref['vs_id']}'>";
+                       echo "${delim}<a href='".makeHref(array('page'=>'object', 'object_id'=>$ref['object_id']))."'>";
+                       echo "${ref['object_name']}</a>:<a href='".makeHref(array('page'=>'ipv4vs', 'vs_id'=>$ref['vs_id']))."'>";
                        echo "${ref['vport']}/${ref['proto']}</a>&rarr;";
                        $delim = '; ';
                }
@@ -2368,7 +2483,7 @@ function renderIPv4Network ($id)
                {
                        echo $prologue;
                        $prologue = '';
-                       echo "${delim}&rarr;${ref['rsport']}@<a href='${root}?page=ipv4rsp&pool_id=${ref['rspool_id']}'>";
+                       echo "${delim}&rarr;${ref['rsport']}@<a href='".makeHref(array('page'=>'ipv4rspool', 'pool_id'=>$ref['rspool_id']))."'>";
                        echo "${ref['rspool_name']}</a>";
                        $delim = '; ';
                }
@@ -2392,12 +2507,11 @@ function renderIPv4NetworkProperties ($id)
        echo "<tr><td colspan=2 class=tdcenter>";
        printImageHREF ('SAVE', 'Save changes', TRUE);
        echo "</td></form></tr></table>\n";
-
 }
 
 function renderIPv4Address ($dottedquad)
 {
-       global $root, $aat;
+       global $aat;
        $address = getIPv4Address ($dottedquad);
        echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
        echo "<tr><td colspan=2 align=center><h1>${dottedquad}</h1></td></tr>\n";
@@ -2419,7 +2533,7 @@ function renderIPv4Address ($dottedquad)
 
        echo "<td class=pcright>";
 
-       if (!empty ($address['class']))
+       if (isset ($address['class']))
        {
                startPortlet ('allocations');
                echo "<table class='widetable' cellpadding=5 cellspacing=0 border=0 align='center' width='100%'>\n";
@@ -2434,8 +2548,7 @@ function renderIPv4Address ($dottedquad)
                                $secondclass = 'tdleft port_highlight';
                        else
                                $secondclass = 'tdleft';
-                       echo "<tr class='$class'><td class=tdleft><a href='${root}?page=object&object_id=${bond['object_id']}";
-                       echo "&hl_ipv4_addr=${dottedquad}'>${bond['object_name']}</td><td class='${secondclass}'>${bond['name']}</td><td class='${secondclass}'><strong>";
+                       echo "<tr class='$class'><td class=tdleft><a href='".makeHref(array('page'=>'object', 'object_id'=>$bond['object_id'], 'hl_ipv4_addr'=>$dottedquad))."'>${bond['object_name']}</td><td class='${secondclass}'>${bond['name']}</td><td class='${secondclass}'><strong>";
                        echo $aat[$bond['type']];
                        echo "</strong></td></tr>\n";
                }
@@ -2452,7 +2565,7 @@ function renderIPv4Address ($dottedquad)
                echo "<tr><th>VS</th><th>name</th></tr>\n";
                foreach ($address['lblist'] as $vsinfo)
                {
-                       echo "<tr><td class=tdleft><a href='${root}?page=ipv4vs&vs_id=${vsinfo['vs_id']}'>";
+                       echo "<tr><td class=tdleft><a href='".makeHref(array('page'=>'ipv4vs', 'vs_id'=>$vsinfo['vs_id']))."'>";
                        echo buildVServiceName ($vsinfo) . "</a></td><td class=tdleft>";
                        echo $vsinfo['name'] . "</td></tr>\n";
                }
@@ -2472,7 +2585,7 @@ function renderIPv4Address ($dottedquad)
                                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=ipv4rsp&pool_id=${rsinfo['rspool_id']}'>";
+                       echo "</td><td class=tdleft>${rsinfo['rsport']}</td><td class=tdleft><a href='".makeHref(array('page'=>'ipv4rspool', 'pool_id'=>$rsinfo['rspool_id']))."'>";
                        echo $rsinfo['rspool_name'] . "</a></td></tr>\n";
                }
                echo "</table><br><br>";
@@ -2533,59 +2646,102 @@ function renderIPv4AddressProperties ($dottedquad)
 function renderIPv4AddressAllocations ($dottedquad)
 {
        showMessageOrError();
-       global $pageno, $tabno, $root, $aat;
+       function printNewItemTR ()
+       {
+               global $aat;
+               printOpFormIntro ('addIPv4Allocation');
+               echo "<tr><td>";
+               printImageHREF ('add', 'allocate', TRUE);
+               echo "</td><td><select name='object_id' tabindex=100>";
+
+               foreach (explode (',', getConfigVar ('IPV4_PERFORMERS')) as $type) 
+                       foreach (getNarrowObjectList ($type) as $object)
+                               echo "<option value='${object['id']}'>${object['dname']}</option>";
+
+               echo "</select></td><td><input type=text tabindex=101 name=bond_name size=10></td><td>";
+               printSelect ($aat, 'bond_type', NULL, 102);
+               echo "</td><td>";
+               printImageHREF ('add', 'allocate', TRUE, 103);
+               echo "</td></form></tr>";
+       }
+       global $pageno, $tabno, $aat;
 
        $address = getIPv4Address ($dottedquad);
-       $class = $address['class'];
+
        echo "<center><h1>${dottedquad}</h1></center>\n";
        echo "<table class='widetable' cellpadding=5 cellspacing=0 border=0 align='center'>\n";
        echo "<tr><th>&nbsp;</th><th>object</th><th>OS interface</th><th>allocation type</th><th>&nbsp;</th></tr>\n";
 
-       if ($address['reserved'] == 'yes')
-               echo "<tr class='${class}'><td colspan=3>&nbsp;</td><td class=tdleft><strong>RESERVED</strong></td><td>&nbsp;</td></tr>";
-       foreach ($address['allocs'] as $bond)
+       if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
+               printNewItemTR();
+       if (isset ($address['class']))
        {
-               echo "<tr class='$class'>";
-               printOpFormIntro ('updIPv4Allocation', array ('object_id' => $bond['object_id']));
-               echo "<td><a href='${root}process.php?op=delIPv4Allocation&page=${pageno}&tab=${tabno}&ip=${dottedquad}&object_id=${bond['object_id']}'>";
-               printImageHREF ('delete', 'Unallocate address');
-               echo "</a></td>";
-               echo "<td><a href='${root}?page=object&object_id=${bond['object_id']}&hl_ipv4_addr=${dottedquad}'>${bond['object_name']}</td>";
-               echo "<td><input type='text' name='bond_name' value='${bond['name']}' size=10></td><td>";
-               printSelect ($aat, 'bond_type', $bond['type']);
-               echo "</td><td>";
-               printImageHREF ('save', 'Save changes', TRUE);
-               echo "</td></form></tr>\n";
+               $class = $address['class'];
+               if ($address['reserved'] == 'yes')
+                       echo "<tr class='${class}'><td colspan=3>&nbsp;</td><td class=tdleft><strong>RESERVED</strong></td><td>&nbsp;</td></tr>";
+               foreach ($address['allocs'] as $bond)
+               {
+                       echo "<tr class='$class'>";
+                       printOpFormIntro ('updIPv4Allocation', array ('object_id' => $bond['object_id']));
+                       echo "<td><a href='".makeHrefProcess(array('op'=>'delIPv4Allocation', 'ip'=>$dottedquad, 'object_id'=>$bond['object_id']))."'>";
+                       printImageHREF ('delete', 'Unallocate address');
+                       echo "</a></td>";
+                       echo "<td><a href='".makeHref(array('page'=>'object', 'object_id'=>$bond['object_id'], 'hl_ipv4_addr'=>$dottedquad))."'>${bond['object_name']}</td>";
+                       echo "<td><input type='text' name='bond_name' value='${bond['name']}' size=10></td><td>";
+                       printSelect ($aat, 'bond_type', $bond['type']);
+                       echo "</td><td>";
+                       printImageHREF ('save', 'Save changes', TRUE);
+                       echo "</td></form></tr>\n";
+               }
        }
-       printOpFormIntro ('addIPv4Allocation');
-       echo "<td>";
-       printImageHREF ('add', 'new allocation', TRUE);
-       echo "</td><td><select name='object_id'>";
-
-       foreach (explode (',', getConfigVar ('IPV4_PERFORMERS')) as $type) 
-               foreach (getNarrowObjectList ($type) as $object)
-                       echo "<option value='${object['id']}'>${object['dname']}</option>";
-
-       echo "</select></td><td><input type='text' name='bond_name' value='' size=10></td><td>";
-       printSelect ($aat, 'bond_type');
-       echo "</td><td>&nbsp;</td></form></tr>";
+       if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
+               printNewItemTR();
        echo "</table><br><br>";
-
 }
 
 function renderNATv4ForObject ($object_id = 0)
 {
-       global $pageno, $tabno, $root;
-       
-       $info = getObjectInfo ($object_id);
-       $forwards = getNATv4ForObject ($object_id);
-       $alloclist = getObjectIPv4Allocations ($object_id);
-       showMessageOrError();
-       echo "<center><h2>locally performed NAT</h2></center>";
-
-       echo "<table class='widetable' cellpadding=5 cellspacing=0 border=0 align='center'>\n";
+       global $root;
+       function printNewItemTR ($alloclist)
+       {
+               global $root;
+               printOpFormIntro ('addNATv4Rule');
+               echo "<tr align='center'><td>";
+               printImageHREF ('add', 'Add new NAT rule', TRUE);
+               echo '</td><td>';
+               printSelect (array ('TCP' => 'TCP', 'UDP' => 'UDP'), 'proto');
+               echo "<select name='localip' tabindex=1>";
+
+               foreach ($alloclist as $dottedquad => $alloc)
+               {
+                       $name = empty ($alloc['addrinfo']['name']) ? '' : (' (' . niftyString ($alloc['addrinfo']['name']) . ')');
+                       $osif = empty ($alloc['osif']) ? '' : ($alloc['osif'] . ': ');
+                       echo "<option value='${dottedquad}'>${osif}${dottedquad}${name}</option>";
+               }
+
+               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\");'>";
+               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></td><td>";
+               printImageHREF ('add', 'Add new NAT rule', TRUE, 6);
+               echo "</td></tr></form>";
+       }
+       global $pageno, $tabno;
+       
+       $info = getObjectInfo ($object_id);
+       $forwards = getNATv4ForObject ($object_id);
+       $alloclist = getObjectIPv4Allocations ($object_id);
+       showMessageOrError();
+       echo "<center><h2>locally performed NAT</h2></center>";
+
+       echo "<table class='widetable' cellpadding=5 cellspacing=0 border=0 align='center'>\n";
        echo "<tr><th></th><th>Match endpoint</th><th>Translate to</th><th>Target object</th><th>Comment</th><th>&nbsp;</th></tr>\n";
 
+       if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
+               printNewItemTR ($alloclist);
        foreach ($forwards['out'] as $pf)
        {
                $class = 'trerror';
@@ -2597,21 +2753,30 @@ function renderNATv4ForObject ($object_id = 0)
                }
 
                echo "<tr class='$class'>";
-               echo "<td><a href='${root}process.php?op=delNATv4Rule&localip=${pf['localip']}&localport=${pf['localport']}&remoteip=${pf['remoteip']}&remoteport=${pf['remoteport']}&proto=${pf['proto']}&object_id=$object_id&page=${pageno}&tab=${tabno}'>";
+               echo "<td><a href='".
+                       makeHrefProcess(array(
+                               'op'=>'delNATv4Rule', 
+                               'localip'=>$pf['localip'],
+                               'localport'=>$pf['localport'],
+                               'remoteip'=>$pf['remoteip'],
+                               'remoteport'=>$pf['remoteport'],
+                               'proto'=>$pf['proto'], 
+                               'object_id'=>$object_id)).
+               "'>";
                printImageHREF ('delete', 'Delete NAT rule');
                echo "</a></td>";
-               echo "<td>${pf['proto']}/${osif}<a href='${root}?page=ipaddress&tab=default&ip=${pf['localip']}'>${pf['localip']}</a>:${pf['localport']}";
+               echo "<td>${pf['proto']}/${osif}<a href='".makeHref(array('page'=>'ipaddress', 'tab'=>'default', 'ip'=>$pf['localip']))."'>${pf['localip']}</a>:${pf['localport']}";
                if (!empty ($pf['local_addr_name']))
                        echo ' (' . $pf['local_addr_name'] . ')';
                echo "</td>";
-               echo "<td><a href='${root}?page=ipaddress&tab=default&ip=${pf['remoteip']}'>${pf['remoteip']}</a>:${pf['remoteport']}</td>";
+               echo "<td><a href='".makeHref(array('page'=>'ipaddress', 'tab'=>'default', 'ip'=>$pf['remoteip']))."'>${pf['remoteip']}</a>:${pf['remoteport']}</td>";
 
                $address = getIPv4Address ($pf['remoteip']);
 
                echo "<td class='description'>";
                if (count ($address['allocs']))
                        foreach ($address['allocs'] as $bond)
-                               echo "<a href='${root}?page=object&tab=default&object_id=${bond['object_id']}'>${bond['object_name']}(${bond['name']})</a> ";
+                               echo "<a href='".makeHref(array('page'=>'object', 'tab'=>'default', 'object_id'=>$bond['object_id']))."'>${bond['object_name']}(${bond['name']})</a> ";
                elseif (!empty ($pf['remote_addr_name']))
                        echo '(' . $pf['remote_addr_name'] . ')';
                printOpFormIntro
@@ -2631,28 +2796,8 @@ function renderNATv4ForObject ($object_id = 0)
                printImageHREF ('save', 'Save changes', TRUE);
                echo "</td></form></tr>";
        }
-       printOpFormIntro ('addNATv4Rule');
-       echo "<tr align='center'><td>";
-       printImageHREF ('add', 'Add new NAT rule', TRUE);
-       echo '</td><td>';
-       printSelect (array ('TCP' => 'TCP', 'UDP' => 'UDP'), 'proto');
-       echo "<select name='localip' tabindex=1>";
-
-       foreach ($alloclist as $dottedquad => $alloc)
-       {
-               $name = empty ($alloc['addrinfo']['name']) ? '' : (' (' . niftyString ($alloc['addrinfo']['name']) . ')');
-               $osif = empty ($alloc['osif']) ? '' : ($alloc['osif'] . ': ');
-               echo "<option value='${dottedquad}'>${osif}${dottedquad}${name}</option>";
-       }
-
-       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\");'>";
-       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></td><td>&nbsp;</td></tr>";
-       echo "</form>";
+       if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
+               printNewItemTR ($alloclist);
 
        echo "</table><br><br>";
 
@@ -2662,12 +2807,22 @@ function renderNATv4ForObject ($object_id = 0)
 
        foreach ($forwards['in'] as $pf)
        {
-               echo "<tr><td><a href='${root}process.php?op=delNATv4Rule&localip=${pf['localip']}&localport=${pf['localport']}&remoteip=${pf['remoteip']}&remoteport=${pf['remoteport']}&proto=${pf['proto']}&object_id=${pf['object_id']}&page=${pageno}&tab=${tabno}'>";
+               echo "<tr><td><a href='".
+                       makeHrefProcess(array(
+                               'op'=>'delNATv4Rule',
+                               'localip'=>$pf['localip'],
+                               'localport'=>$pf['localport'],
+                               'remoteip'=>$pf['remoteip'],
+                               'remoteport'=>$pf['remoteport'],
+                               'proto'=>$pf['proto'],
+                               'object_id'=>$pf['object_id']
+                               )).
+               "'>";
                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>";
+               echo "<td>${pf['proto']}/<a href='".makeHref(array('page'=>'ipaddress', 'tab'=>'default', 'ip'=>$pf['localip']))."'>${pf['localip']}</a>:${pf['localport']}</td>";
+               echo "<td class='description'><a href='".makeHref(array('page'=>'object', 'tab'=>'default', 'object_id'=>$pf['object_id']))."'>${pf['object_name']}</a>";
+               echo "</td><td><a href='".makeHref(array('page'=>'ipaddress', 'tab'=>'default', 'ip'=>$pf['remoteip']))."'>${pf['remoteip']}</a>:${pf['remoteport']}</td>";
                echo "<td class='description'>${pf['description']}</td></tr>";
        }
 
@@ -2822,10 +2977,10 @@ function renderAddMultipleObjectsForm ()
 
 function printGreeting ()
 {
-       global $remote_username, $accounts, $root;
-       $account = $accounts[$remote_username];
-       $person = empty ($account['user_realname']) ? $account['user_name'] : $account['user_realname'];
-       echo "Hello, ${person}. This is RackTables " . CODE_VERSION . ". Click <a href='${root}?logout'>here</a> to logout.";
+       global $root, $remote_username, $remote_displayname;
+       echo "Hello, <a href='${root}?page=myaccount&tab=default'>${remote_displayname}</a>. This is RackTables " .
+               CODE_VERSION .
+               ". Click <a href='${root}?logout'>here</a> to logout.";
 }
 
 function renderSearchResults ()
@@ -2846,16 +3001,18 @@ function renderSearchResults ()
        // 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) 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)
+               preg_match (RE_L2_IFCFG, $terms) or
+               preg_match (RE_L2_SOLID, $terms) or
+               preg_match (RE_L2_CISCO, $terms) or
+               preg_match (RE_L2_IPCFG, $terms) or
+               // Foundry STP bridge ID: bridge priotity + port MAC address. Cut off first 4 chars and look for MAC address.
+               preg_match (RE_L2_FDRYSTP, $terms)
        )
        // Search for L2 address.
        {
                $terms = str_replace ('.', '', $terms);
                $terms = str_replace (':', '', $terms);
+               $terms = str_replace ('-', '', $terms);
                $terms = substr ($terms, -12);
                $result = searchByl2address ($terms);
                if ($result !== NULL)
@@ -2865,8 +3022,8 @@ function renderSearchResults ()
                        $summary['port'][] = $result;
                }
        }
-       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.
+       elseif (preg_match (RE_IP4_ADDR, $terms))
+       // Search for IPv4 address.
        {
                if (NULL !== getIPv4AddressNetworkId ($terms))
                {
@@ -2875,6 +3032,17 @@ function renderSearchResults ()
                        $summary['ipv4addressbydq'][] = $terms;
                }
        }
+       elseif (preg_match (RE_IP4_NET, $terms))
+       // Search for IPv4 network
+       {
+               list ($base, $len) = explode ('/', $terms);
+               if (NULL !== ($tmp = getIPv4AddressNetworkId ($base, $len + 1)))
+               {
+                       $nhits++;
+                       $lasthit = 'ipv4network';
+                       $summary['ipv4network'][] = getIPv4NetworkInfo ($tmp);
+               }
+       }
        else
        // Search for objects, addresses, networks, virtual services and RS pools by their description.
        {
@@ -2920,6 +3088,13 @@ function renderSearchResults ()
                        $lasthit = 'user';
                        $summary['user'] = $tmp;
                }
+               $tmp = getFileSearchResult ($terms);
+               if (count ($tmp))
+               {
+                       $nhits += count ($tmp);
+                       $lasthit = 'file';
+                       $summary['file'] = $tmp;
+               }
        }
        if ($nhits == 0)
                echo "<center><h2>Nothing found for '${terms}'</h2></center>";
@@ -2936,19 +3111,19 @@ function renderSearchResults ()
                        case 'ipv4addressbydq':
                                $parentnet = getIPv4AddressNetworkId ($record);
                                if ($parentnet !== NULL)
-                                       echo "<script language='Javascript'>document.location='${root}?page=iprange&tab=default&id=${parentnet}&hl_ipv4_addr=${record}';//</script>";
+                                       echo "<script language='Javascript'>document.location='${root}?page=ipv4net&tab=default&id=${parentnet}&hl_ipv4_addr=${record}';//</script>";
                                else
                                        echo "<script language='Javascript'>document.location='${root}?page=ipaddress&ip=${record}';//</script>";
                                break;
                        case 'ipv4addressbydescr':
                                $parentnet = getIPv4AddressNetworkId ($record['ip']);
                                if ($parentnet !== NULL)
-                                       echo "<script language='Javascript'>document.location='${root}?page=iprange&tab=default&id=${parentnet}&hl_ipv4_addr=${record['ip']}';//</script>";
+                                       echo "<script language='Javascript'>document.location='${root}?page=ipv4net&tab=default&id=${parentnet}&hl_ipv4_addr=${record['ip']}';//</script>";
                                else
                                        echo "<script language='Javascript'>document.location='${root}?page=ipaddress&ip=${record['ip']}';//</script>";
                                break;
                        case 'ipv4network':
-                               echo "<script language='Javascript'>document.location='${root}?page=iprange";
+                               echo "<script language='Javascript'>document.location='${root}?page=ipv4net";
                                echo "&id=${record['id']}";
                                echo "';//</script>";
                                break;
@@ -2956,7 +3131,7 @@ function renderSearchResults ()
                                echo "<script language='Javascript'>document.location='${root}?page=object&object_id=${record['id']}';//</script>";
                                break;
                        case 'ipv4rspool':
-                               echo "<script language='Javascript'>document.location='${root}?page=ipv4rsp&pool_id=${record['pool_id']}';//</script>";
+                               echo "<script language='Javascript'>document.location='${root}?page=ipv4rspool&pool_id=${record['pool_id']}';//</script>";
                                break;
                        case 'ipv4vs':
                                echo "<script language='Javascript'>document.location='${root}?page=ipv4vs&vs_id=${record['id']}';//</script>";
@@ -2964,6 +3139,9 @@ function renderSearchResults ()
                        case 'user':
                                echo "<script language='Javascript'>document.location='${root}?page=user&user_id=${record['user_id']}';//</script>";
                                break;
+                       case 'file':
+                               echo "<script language='Javascript'>document.location='${root}?page=file&file_id=${record['id']}';//</script>";
+                               break;
                }
                return;
        }
@@ -2978,11 +3156,14 @@ function renderSearchResults ()
                                case 'object':
                                        startPortlet ("<a href='${root}?page=objects'>Objects</a>");
                                        echo '<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
-                                       echo '<tr><th>Common name</th><th>Visible label</th><th>Asset tag</th><th>barcode</th></tr>';
+                                       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>";
+                                               $tags = loadRackObjectTags ($obj['id']);
+                                               echo "<tr class=row_${order} valign=top><td class=tdleft><a href=\"${root}?page=object&object_id=${obj['id']}\">${obj['dname']}</a>";
+                                               if (count ($tags))
+                                                       echo '<br><small>' . serializeTags ($tags) . '</small>';
+                                               echo "</td><td>${obj['label']}</td>";
                                                echo "<td>${obj['asset_no']}</td>";
                                                echo "<td>${obj['barcode']}</td></tr>";
                                                $order = $nextorder[$order];
@@ -2993,7 +3174,7 @@ function renderSearchResults ()
                                case 'ipv4network':
                                        startPortlet ("<a href='${root}?page=ipv4space'>IPv4 networks</a>");
                                        echo '<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
-                                       echo '<tr><th>network</th><th>name/tags</th></tr>';
+                                       echo '<tr><th>Network</th><th>Name/tags</th></tr>';
                                        foreach ($what as $netinfo)
                                        {
                                                echo "<tr class=row_${order} valign=top>";
@@ -3008,13 +3189,13 @@ function renderSearchResults ()
                                        startPortlet ('IPv4 addresses');
                                        echo '<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
                                        // FIXME: address, parent network, routers (if extended view is enabled)
-                                       echo '<tr><th>Address</th><th>Descritpion</th></tr>';
+                                       echo '<tr><th>Address</th><th>Description</th></tr>';
                                        foreach ($what as $addr)
                                        {
                                                echo "<tr class=row_${order}><td class=tdleft>";
                                                $parentnet = getIPv4AddressNetworkId ($addr['ip']);
                                                if ($parentnet !== NULL)
-                                                       echo "<a href='${root}?page=iprange&id=${parentnet}&hl_ipv4_addr=${addr['ip']}'>${addr['ip']}</a></td>";
+                                                       echo "<a href='${root}?page=ipv4net&tab=default&id=${parentnet}&hl_ipv4_addr=${addr['ip']}'>${addr['ip']}</a></td>";
                                                else
                                                        echo "<a href='${root}?page=ipaddress&ip=${addr['ip']}'>${addr['ip']}</a></td>";
                                                echo "<td class=tdleft>${addr['name']}</td></tr>";
@@ -3028,7 +3209,7 @@ function renderSearchResults ()
                                        echo '<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
                                        foreach ($what as $rspool)
                                        {
-                                               echo "<tr class=row_${order}><td class=tdleft><a href='${root}?page=ipv4rsp&pool_id=${rspool['pool_id']}'>";
+                                               echo "<tr class=row_${order}><td class=tdleft><a href='${root}?page=ipv4rspool&pool_id=${rspool['pool_id']}'>";
                                                echo buildRSPoolName ($rspool);
                                                echo "</a></td></tr>";
                                                $order = $nextorder[$order];
@@ -3039,7 +3220,7 @@ function renderSearchResults ()
                                case 'ipv4vs':
                                        startPortlet ("<a href='${root}?page=ipv4vslist'>Virtual services</a>");
                                        echo '<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
-                                       echo '<tr><th>VS</th><th>Descritpion</th></tr>';
+                                       echo '<tr><th>VS</th><th>Description</th></tr>';
                                        foreach ($what as $vs)
                                        {
                                                echo "<tr class=row_${order}><td class=tdleft><a href='${root}?page=ipv4vs&vs_id=${vs['id']}'>";
@@ -3053,7 +3234,7 @@ function renderSearchResults ()
                                case 'user':
                                        startPortlet ("<a href='${root}?page=userlist'>Users</a>");
                                        echo '<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
-                                       echo '<tr><th>username</th><th>realname</th></tr>';
+                                       echo '<tr><th>Username</th><th>Real Name</th></tr>';
                                        foreach ($what as $item)
                                        {
                                                echo "<tr class=row_${order}><td class=tdleft><a href='${root}?page=user&user_id=${item['user_id']}'>";
@@ -3064,6 +3245,20 @@ function renderSearchResults ()
                                        echo '</table>';
                                        finishPortlet();
                                        break;
+                               case 'file':
+                                       startPortlet ("<a href='${root}?page=files'>Files</a>");
+                                       echo '<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
+                                       echo '<tr><th>Name</th><th>Comment</th></tr>';
+                                       foreach ($what as $item)
+                                       {
+                                               echo "<tr class=row_${order}><td class=tdleft><a href='${root}?page=file&file_id=${item['id']}'>";
+                                               echo $item['name'];
+                                               echo "</a></td><td class=tdleft>${item['comment']}</td></tr>";
+                                               $order = $nextorder[$order];
+                                       }
+                                       echo '</table>';
+                                       finishPortlet();
+                                       break;
                        }
        }
 }
@@ -3130,41 +3325,33 @@ function renderUserList ()
 
 function renderUserListEditor ()
 {
+       function printNewItemTR ()
+       {
+               printOpFormIntro ('createUser');
+               echo "<tr><td><input type=text size=16 name=username tabindex=100></td>\n";
+               echo "<td><input type=text size=24 name=realname tabindex=101></td>";
+               echo "<td><input type=password size=64 name=password tabindex=102></td><td>";
+               printImageHREF ('create', 'Add new account', TRUE, 103);
+               echo "</td></tr></form>";
+       }
        global $root, $pageno, $tabno, $accounts;
        startPortlet ('User accounts');
        showMessageOrError();
        echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
-       echo "<tr><th>status (click to change)</th><th>Username</th><th>Real name</th><th>Password</th><th>&nbsp;</th></tr>\n";
+       echo "<tr><th>Username</th><th>Real name</th><th>Password</th><th>&nbsp;</th></tr>\n";
+       if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
+               printNewItemTR();
        foreach ($accounts as $account)
        {
                printOpFormIntro ('updateUser', array ('user_id' => $account['user_id']));
-               echo "<tr><td>";
-               if ($account['user_enabled'] == 'yes' && $account['user_id'] != 1)
-               {
-                       echo "<a href='${root}process.php?op=disableUser&page=${pageno}&tab=${tabno}&user_id=${account['user_id']}'>";
-                       printImageHREF ('blockuser', 'disable account');
-                       echo "</a>\n";
-               }
-               if ($account['user_enabled'] == 'no' && $account['user_id'] != 1)
-               {
-                       echo "<a href='${root}process.php?op=enableUser&page=${pageno}&tab=${tabno}&user_id=${account['user_id']}'>";
-                       printImageHREF ('unblockuser', 'enable account');
-                       echo "</a>\n";
-               }
-               // Otherwise skip icon.
-               echo "</td>";
-               echo "<td><input type=text name=username value='${account['user_name']}' size=16></td>";
+               echo "<tr><td><input type=text name=username value='${account['user_name']}' size=16></td>";
                echo "<td><input type=text name=realname value='${account['user_realname']}' size=24></td>";
                echo "<td><input type=password name=password value='${account['user_password_hash']}' size=64></td><td>";
                printImageHREF ('save', 'Save changes', TRUE);
                echo "</td></form></tr>\n";
        }
-       printOpFormIntro ('createUser');
-       echo "<td>&nbsp;</td><td><input type=text size=16 name=username tabindex=100></td>\n";
-       echo "<td><input type=text size=24 name=realname tabindex=101></td>";
-       echo "<td><input type=password size=64 name=password tabindex=102></td><td>";
-       printImageHREF ('create', 'Add new account', TRUE, 103);
-       echo "</td></tr></form>";
+       if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
+               printNewItemTR();
        echo "</table><br>\n";
        finishPortlet();
 }
@@ -3181,7 +3368,7 @@ function renderPortMapEditor ()
 
 function renderPortMap ($editable = FALSE)
 {
-       global $nextorder, $root, $pageno, $tabno;
+       global $nextorder, $pageno, $tabno;
        showMessageOrError();
        startPortlet ("Port compatibility map");
        $ptlist = getPortTypes();
@@ -3248,6 +3435,7 @@ function renderRackPage ($rack_id)
        // Left column with information.
        echo "<td class=pcleft>";
        renderRackInfoPortlet ($rackData);
+       renderFilesPortlet ('rack', $rack_id);
        echo '</td>';
 
        // Right column with rendered rack.
@@ -3264,88 +3452,129 @@ function renderDictionary ()
 {
        global $nextorder;
        $dict = getDict (TRUE);
-       echo "<br><table class=cooltable border=0 cellpadding=5 cellspacing=0 align=center>\n";
+       echo '<ul>';
        foreach ($dict as $chapter_no => $chapter)
        {
-               $order = 'odd';
-               echo "<tr><th>Chapter</th><th>refs</th><th>Word</th></tr>\n";
                $wc = count ($chapter['word']);
-               echo "<tr class=row_${order}><td class=tdleft" . ($wc ? " rowspan = ${wc}" : '');
-               echo "><div title='number=${chapter_no}'>${chapter['name']} (${wc} records)</div></td>";
+               echo "<li><a href='".makeHref(array('page'=>'chapter', 'chapter_no'=>$chapter_no))."'>${chapter['name']}</a>";
+               echo " (${wc} records)</li>";
+       }
+       echo '</ul>';
+}
+
+function renderChapter ($tgt_chapter_no)
+{
+       global $nextorder;
+       foreach (getDict (TRUE) as $chapter_no => $chapter)
+       {
+               if ($chapter_no != $tgt_chapter_no)
+                       continue;
+               $wc = count ($chapter['word']);
                if (!$wc)
-                       echo "<td colspan=2>none</td>";
-               else
                {
-                       $chap_start = TRUE;
-                       foreach ($chapter['word'] as $key => $value)
-                       {
-                               if (!$chap_start)
-                                       echo "<tr class=row_${order}>";
-                               else
-                                       $chap_start = FALSE;
-                               echo '<td>' . ($chapter['refcnt'][$key] ? $chapter['refcnt'][$key] : '&nbsp;') . '</td>';
-                               echo "<td><div title='key=${key}'>${value}</div></td></tr>\n";
-                               $order = $nextorder[$order];
-                       }
+                       echo "<center><h2>(no records)</h2></center>";
+                       break;
                }
+               echo "<br><table class=cooltable border=0 cellpadding=5 cellspacing=0 align=center>\n";
+               echo "<tr><th colspan=3>${wc} record(s)</th></tr>\n";
+               echo "<tr><th>Origin</th><th>Refcnt</th><th>Word</th></tr>\n";
+               $order = 'odd';
+               foreach ($chapter['word'] as $key => $value)
+               {
+                       echo "<tr class=row_${order}><td>";
+                       printImageHREF (($key <= MAX_DICT_KEY) ? 'computer' : 'favorite');
+                       echo '</td><td>';
+                       if ($chapter['refcnt'][$key])
+                               echo $chapter['refcnt'][$key];
+                       echo "</td><td><div title='key=${key}'>${value}</div></td></tr>\n";
+                       $order = $nextorder[$order];
+               }
+               echo "</table>\n<br>";
+               break;
        }
-       echo "</table>\n<br>";
 }
 
-function renderDictionaryEditor ()
+function renderChapterEditor ($tgt_chapter_no)
 {
-       global $root, $pageno, $tabno, $nextorder;
+       global $pageno, $tabno, $nextorder;
+       function printNewItemTR ()
+       {
+               printOpFormIntro ('add');
+               echo '<tr><td>&nbsp;</td><td>';
+               printImageHREF ('add', 'Add new', TRUE);
+               echo "</td>";
+               echo "<td class=tdleft><input type=text name=dict_value size=32></td><td>";
+               printImageHREF ('add', 'Add new', TRUE);
+               echo '</td></tr></form>';
+       }
        $dict = getDict();
        showMessageOrError();
        echo "<br><table class=cooltable border=0 cellpadding=5 cellspacing=0 align=center>\n";
        foreach ($dict as $chapter_no => $chapter)
        {
+               if ($chapter_no != $tgt_chapter_no)
+                       continue;
                $order = 'odd';
-               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) : '');
-               echo "><div title='number=${chapter_no}'>${chapter['name']} (${wc} records)</div></td>";
-               printOpFormIntro ('add', array ('chapter_no' => $chapter['no']));
-               echo "<td>";
-               printImageHREF ('add', 'Add new', TRUE);
-               echo "</td>";
-               echo "<td class=tdright><input type=text name=dict_value size=32></td>";
-               echo "<td>&nbsp;</td>";
-               echo '</tr></form>';
-               $order = $nextorder[$order];
+               echo "<tr><th>Origin</th><th>&nbsp;</th><th>Word</th><th>&nbsp;</th></tr>\n";
+               if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
+                       printNewItemTR();
                foreach ($chapter['word'] as $key => $value)
                {
-                       printOpFormIntro ('upd', array ('chapter_no' => $chapter['no'], 'dict_key' => $key));
                        echo "<tr class=row_${order}><td>";
-                       // Prevent deleting words currently used somewhere.
-                       if ($chapter['refcnt'][$key])
-                               printImageHREF ('nodelete', 'referenced ' . $chapter['refcnt'][$key] . ' time(s)');
+                       // Show plain row for stock records, render a form for user's ones.
+                       if ($key <= MAX_DICT_KEY)
+                       {
+                               printImageHREF ('computer');
+                               echo "</td><td>&nbsp;</td><td>${value}</td><td>&nbsp;</td></tr>";
+                       }
                        else
                        {
-                               echo "<a href='${root}process.php?page=${pageno}&tab=${tabno}&op=del&chapter_no=${chapter['no']}&dict_key=${key}'>";
-                               printImageHREF ('delete', 'Delete word');
-                               echo "</a>";
+                               printOpFormIntro ('upd', array ('dict_key' => $key));
+                               printImageHREF ('favorite');
+                               echo "</td><td>";
+                               // Prevent deleting words currently used somewhere.
+                               if ($chapter['refcnt'][$key])
+                                       printImageHREF ('nodelete', 'referenced ' . $chapter['refcnt'][$key] . ' time(s)');
+                               else
+                               {
+                                       echo "<a href='".makeHrefProcess(array('op'=>'del', 'chapter_no'=>$chapter_no, 'dict_key'=>$key))."'>";
+                                       printImageHREF ('delete', 'Delete word');
+                                       echo "</a>";
+                               }
+                               echo '</td>';
+                               echo "<td class=tdright><input type=text name=dict_value size=64 value='${value}'></td><td>";
+                               printImageHREF ('save', 'Save changes', TRUE);
+                               echo "</td></tr></form>\n";
                        }
-                       echo '</td>';
-                       echo "<td class=tdright><input type=text name=dict_value size=32 value='${value}'></td><td>";
-                       printImageHREF ('save', 'Save changes', TRUE);
-                       echo "</td></tr></form>\n";
                        $order = $nextorder[$order];
                } // foreach ($chapter['word']
+               if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
+                       printNewItemTR();
+               echo "</table>\n";
+               break;
        } // foreach ($dict
-       echo "</table>\n";
 }
 
 // We don't allow to rename/delete a sticky chapter and we don't allow
 // to delete a non-empty chapter.
 function renderChaptersEditor ()
 {
-       global $root, $pageno, $tabno;
+       function printNewItemTR ()
+       {
+               printOpFormIntro ('add');
+               echo '<tr><td>';
+               printImageHREF ('add', 'Add new', TRUE);
+               echo "</td><td><input type=text name=chapter_name tabindex=100></td><td>&nbsp;</td><td>";
+               printImageHREF ('add', 'Add new', TRUE, 101);
+               echo '</td></tr></form>';
+       }
+       global $pageno, $tabno;
        showMessageOrError();
        $dict = getDict();
        echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
        echo '<tr><th>&nbsp;</th><th>Chapter name</th><th>Words</th><th>&nbsp;</th></tr>';
+       if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
+               printNewItemTR();
        foreach ($dict as $chapter)
        {
                $wordcount = count ($chapter['word']);
@@ -3359,7 +3588,7 @@ function renderChaptersEditor ()
                        printImageHREF ('nodelete', 'contains ' . $wordcount . ' word(s)');
                else
                {
-                       echo "<a href='${root}process.php?page=${pageno}&tab=${tabno}&op=del&chapter_no=${chapter['no']}'>";
+                       echo "<a href='".makeHrefProcess(array('op'=>'del', 'chapter_no'=>$chapter['no']))."'>";
                        printImageHREF ('delete', 'Remove chapter');
                        echo "</a>";
                }
@@ -3373,12 +3602,8 @@ function renderChaptersEditor ()
                echo '</td></tr>';
                echo '</form>';
        }
-       printOpFormIntro ('add');
-       echo '<tr><td>';
-       printImageHREF ('add', 'Add new', TRUE);
-       echo "</td><td colspan=3><input type=text name=chapter_name></td>";
-       echo '</tr>';
-       echo '</form>';
+       if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
+               printNewItemTR();
        echo "</table>\n";
 }
 
@@ -3414,17 +3639,34 @@ function renderAttributes ()
 
 function renderEditAttributesForm ()
 {
-       global $root, $pageno, $tabno;
+       function printNewItemTR ()
+       {
+               printOpFormIntro ('add');
+               echo '<tr><td>';
+               printImageHREF ('add', 'Create attribute', TRUE);
+               echo "</td><td><input type=text tabindex=100 name=attr_name></td>";
+               echo '<td><select name=attr_type tabindex=101>';
+               echo '<option value=uint>uint</option>';
+               echo '<option value=float>float</option>';
+               echo '<option value=string>string</option>';
+               echo '<option value=dict>dict</option>';
+               echo '</select></td><td>';
+               printImageHREF ('add', 'Create attribute', TRUE, 102);
+               echo '</td></tr></form>';
+       }
+       global $pageno, $tabno;
        $attrMap = getAttrMap();
        showMessageOrError();
        startPortlet ('Optional attributes');
        echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
        echo '<tr><th>&nbsp;</th><th>Name</th><th>Type</th><th>&nbsp;</th></tr>';
+       if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
+               printNewItemTR();
        foreach ($attrMap as $attr)
        {
                printOpFormIntro ('upd', array ('attr_id' => $attr['id']));
                echo '<tr>';
-               echo "<td><a href='${root}process.php?page=${pageno}&tab=${tabno}&op=del&attr_id=${attr['id']}'>";
+               echo "<td><a href='".makeHrefProcess(array('op'=>'del', 'attr_id'=>$attr['id']))."'>";
                printImageHREF ('delete', 'Remove attribute');
                echo '</a></td>';
                echo "<td><input type=text name=attr_name value='${attr['name']}'></td>";
@@ -3433,30 +3675,48 @@ function renderEditAttributesForm ()
                echo '</td></tr>';
                echo '</form>';
        }
-       printOpFormIntro ('add');
-       echo '<tr><td>';
-       printImageHREF ('add', '', TRUE);
-       echo "</td><td><input type=text name=attr_name></td>";
-       echo '<td><select name=attr_type>';
-       echo '<option value=uint>uint</option>';
-       echo '<option value=float>float</option>';
-       echo '<option value=string>string</option>';
-       echo '<option value=dict>dict</option>';
-       echo '</select></td>';
-       echo '</tr>';
-       echo '</form>';
+       if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
+               printNewItemTR();
        echo "</table>\n";
        finishPortlet();
 }
 
 function renderEditAttrMapForm ()
 {
-       global $root, $pageno, $tabno;
+       function printNewItemTR ($attrMap)
+       {
+               printOpFormIntro ('add');
+               echo '<tr><td>';
+               printImageHREF ('add', '', TRUE);
+               echo "</td><td><select name=attr_id tabindex=100>";
+               $shortType['uint'] = 'U';
+               $shortType['float'] = 'F';
+               $shortType['string'] = 'S';
+               $shortType['dict'] = 'D';
+               foreach ($attrMap as $attr)
+                       echo "<option value=${attr['id']}>[" . $shortType[$attr['type']] . "] ${attr['name']}</option>";
+               echo "</select></td>";
+               echo '<td>';
+               printSelect (getObjectTypeList(), 'objtype_id', NULL, 101);
+               echo '</td>';
+               $dict = getDict();
+               echo '<td><select name=chapter_no tabindex=102>';
+               foreach ($dict as $chapter)
+                       if (!$chapter['sticky'])
+                               echo "<option value='${chapter['no']}'>${chapter['name']}</option>";
+               echo '</select></td><td>';
+               printImageHREF ('add', '', TRUE, 103);
+               echo '</td></tr>';
+               echo '</form>';
+       }
+       global $pageno, $tabno;
        $attrMap = getAttrMap();
        showMessageOrError();
        startPortlet ('Attribute map');
        echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
-       echo '<tr><th>&nbsp;</th><th>Attribute name</th><th>Object type</th><th>Dictionary chapter</th></tr>';
+       echo '<tr><th>&nbsp;</th><th>Attribute name</th><th>Object type</th><th>Dictionary chapter</th><th>&nbsp;</th></tr>';
+       if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
+               printNewItemTR ($attrMap);
        foreach ($attrMap as $attr)
        {
                if (count ($attr['application']) == 0)
@@ -3465,8 +3725,7 @@ function renderEditAttrMapForm ()
                {
                        echo '<tr>';
                        echo '<td>';
-                       echo "<a href='${root}process.php?page=${pageno}&tab=${tabno}&op=del&";
-                       echo "attr_id=${attr['id']}&objtype_id=${app['objtype_id']}'>";
+                       echo "<a href='".makeHrefProcess(array('op'=>'del', 'attr_id'=>$attr['id'], 'objtype_id'=>$app['objtype_id']))."'>";
                        printImageHREF ('delete', 'Remove mapping');
                        echo "</a>";
                        echo '</td>';
@@ -3480,28 +3739,8 @@ function renderEditAttrMapForm ()
                        echo "</td></tr>\n";
                }
        }
-       printOpFormIntro ('add');
-       echo '<tr><td>';
-       printImageHREF ('add', '', TRUE);
-       echo "</td><td><select name=attr_id>";
-       $shortType['uint'] = 'U';
-       $shortType['float'] = 'F';
-       $shortType['string'] = 'S';
-       $shortType['dict'] = 'D';
-       foreach ($attrMap as $attr)
-               echo "<option value=${attr['id']}>[" . $shortType[$attr['type']] . "] ${attr['name']}</option>";
-       echo "</select></td>";
-       echo '<td>';
-       printSelect (getObjectTypeList(), 'objtype_id');
-       echo '</td>';
-       $dict = getDict();
-       echo '<td><select name=chapter_no>';
-       foreach ($dict as $chapter)
-               if (!$chapter['sticky'])
-                       echo "<option value='${chapter['no']}'>${chapter['name']}</option>";
-       echo '</select></td>';
-       echo '</tr>';
-       echo '</form>';
+       if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
+               printNewItemTR ($attrMap);
        echo "</table>\n";
        finishPortlet();
 }
@@ -3517,7 +3756,7 @@ function printImageHREF ($tag, $title = '', $do_input = FALSE, $tabindex = 0)
                        "<input type=image name=submit class=icon " .
                        "src='${root}${img['path']}' " .
                        "border=0 " .
-                       ($tabindex ? '' : "tabindex=${tabindex}") .
+                       ($tabindex ? "tabindex=${tabindex}" : '') .
                        (empty ($title) ? '' : " title='${title}'") . // JT: Add title to input hrefs too
                        ">";
        else
@@ -3538,10 +3777,9 @@ function getFaviconURL ()
        return $root . 'pix/racktables.ico';
 }
 
-// FIXME: stack the report sections somehow, so they can register themselves.
 function renderSystemReports ()
 {
-       $systemreports = array
+       $tmp = array
        (
                array
                (
@@ -3550,12 +3788,6 @@ function renderSystemReports ()
                        'func' => 'getDictStats'
                ),
                array
-               (
-                       'title' => 'IPv4',
-                       'type' => 'counters',
-                       'func' => 'getIPv4Stats'
-               ),
-               array
                (
                        'title' => 'Rackspace',
                        'type' => 'counters',
@@ -3568,7 +3800,7 @@ function renderSystemReports ()
                        'func' => 'renderTagStats'
                ),
        );
-       renderReports ($systemreports);
+       renderReports ($tmp);
 }
 
 function renderLocalReports ()
@@ -3579,7 +3811,7 @@ function renderLocalReports ()
 
 function renderRackCodeReports ()
 {
-       $rcr = array
+       $tmp = array
        (
                array
                (
@@ -3594,7 +3826,27 @@ function renderRackCodeReports ()
                        'func' => 'getRackCodeWarnings'
                ),
        );
-       renderReports ($rcr);
+       renderReports ($tmp);
+}
+
+function renderIPv4Reports ()
+{
+       $tmp = array
+       (
+               array
+               (
+                       'title' => 'Stats',
+                       'type' => 'counters',
+                       'func' => 'getIPv4Stats'
+               ),
+               array
+               (
+                       'title' => 'Lost addresses',
+                       'type' => 'custom',
+                       'func' => 'getLostIPv4Addresses'
+               ),
+       );
+       renderReports ($tmp);
 }
 
 function renderReports ($what)
@@ -3848,18 +4100,21 @@ function renderSNMPPortFinder ($object_id = 0)
                showError ('Invalid argument', __FUNCTION__);
                return;
        }
-// FIXME: check if SNMP PHP extension is available!
        printOpFormIntro ('querySNMPData');
-?>
-<p align=center>
-This asset has no ports listed, that's why you see this form. If you supply SNMP community,
-I can try automatic data harvesting on the asset. As soon as at least one port is added,
-this tab will not be seen any more. Good luck.<br>
-<input type=text name=community value='public'>
-<input type=submit name='do_scan' value='Go!'> 
-</form>
-</p>
-<?php
+       if (!extension_loaded ('snmp'))
+       {
+               echo "<div class=msg_error>The PHP SNMP extension is not loaded.  Cannot continue.</div>";
+       }
+       else
+       {
+               echo "<p align=center>
+This object has no ports listed, that's why you see this form. If you supply a SNMP community,
+I can try to automatically harvest the data. As soon as at least one port is added,
+this tab will not be seen any more. Good luck.<br>\n";
+               echo "<input type=text name=community value='public'>\n";
+               echo "<input type=submit name='do_scan' value='Go!'> \n";
+               echo "</form></p>\n";
+       }
 }
 
 function renderUIResetForm()
@@ -3870,27 +4125,6 @@ function renderUIResetForm()
        echo "</form>";
 }
 
-function renderFirstRowForm ()
-{
-       global $root;
-       echo "<form action='${root}process.php'>\n";
-       echo "<input type=hidden name=page value=dict>\n";
-       echo "<input type=hidden name=tab value=edit>\n";
-       echo "<input type=hidden name=op value=add>\n";
-       echo "<input type=hidden name=chapter_no value=3>\n";
-?>
-<p align=center>
-Your rackspace seems to be empty, and this form will create your first rack row,
-just fill in the name. All the subsequent rack rows will have to be added from the
-Dictionary edit page in Configuration section (you will be redirected right there).
-<br>
-<input type=text name=dict_value value='my server room'><br>
-<?php printImageHREF ('CREATE', 'Add record', TRUE); ?>
-</form>
-</p>
-<?php
-}
-
 function renderLVSConfig ($object_id = 0)
 {
        showMessageOrError();
@@ -3910,7 +4144,7 @@ function renderLVSConfig ($object_id = 0)
 
 function renderVirtualService ($vsid)
 {
-       global $root, $nextorder;
+       global $nextorder;
        if ($vsid <= 0)
        {
                showError ('Invalid argument', __FUNCTION__);
@@ -3928,18 +4162,18 @@ function renderVirtualService ($vsid)
        if (!empty ($vsinfo['name']))
                echo "<tr><th width='50%' class=tdright>Name:</th><td class=tdleft>${vsinfo['name']}</td></tr>\n";
        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 IP address:</th><td class=tdleft><a href='".makeHref(array('page'=>'ipaddress', 'tab'=>'default', 'ip'=>$vsinfo['vid']))."'>${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 ("${root}?page=ipv4vslist&");
+       printTagTRs (makeHref(array('page'=>'ipv4vslist', 'tab'=>'default'))."&");
        if (!empty ($vsinfo['vsconfig']))
        {
-               echo "<tr><th width='50%' class=tdright>VS configuration:</th><td class=tdleft>&nbsp;</td></tr>\n";
-               echo "<tr><td class=tdleft colspan=2><pre>${vsinfo['vsconfig']}</pre></td></tr>\n";
+               echo "<tr><th class=slbconf>VS configuration:</th><td>&nbsp;</td></tr>";
+               echo "<tr><td colspan=2 class='dashed slbconf'>${vsinfo['vsconfig']}</td></tr>\n";
        }
        if (!empty ($vsinfo['rsconfig']))
        {
-               echo "<tr><th width='50%' class=tdright>RS configuration:</th><td class=tdleft>&nbsp;</td></tr>\n";
-               echo "<tr><td class=tdleft colspan=2><pre>${vsinfo['rsconfig']}</pre></td></tr>\n";
+               echo "<tr><th class=slbconf>RS configuration:</th><td class=tdleft>&nbsp;</td></tr>\n";
+               echo "<tr><td colspan=2 class='dashed slbconf'>${vsinfo['rsconfig']}</td></tr>\n";
        }
        echo "</table>\n";
        finishPortlet ();
@@ -3952,19 +4186,16 @@ function renderVirtualService ($vsid)
        $order = 'odd';
        foreach ($vsinfo['rspool'] as $pool_id => $poolInfo)
        {
-               echo "<tr class=row_${order}><td class=tdleft>";
+               echo "<tr class=row_${order} valign=top><td class=tdleft>";
                // Pool info
                echo '<table width=100%>';
-               echo "<tr><td colspan=2><a href='${root}?page=ipv4rsp&pool_id=${pool_id}'>";
-               if (!empty ($poolInfo['name']))
-                       echo $poolInfo['name'];
-               else
-                       echo 'ANONYMOUS';
-               echo "</a></td></tr>";
+               echo "<tr><td colspan=2>";
+               renderRSPoolCell ($pool_id, $poolInfo['name']);
+               echo "</td></tr>";
                if (!empty ($poolInfo['vsconfig']))
-                       echo "<tr><th>VS config</th><td class=tdleft><pre>${poolInfo['vsconfig']}</pre></td></tr>";
+                       echo "<tr><th>VS config</th><td class='dashed slbconf'>${poolInfo['vsconfig']}</td></tr>";
                if (!empty ($poolInfo['rsconfig']))
-                       echo "<tr><th>RS config</th><td class=tdleft><pre>${poolInfo['rsconfig']}</pre></td></tr>";
+                       echo "<tr><th>RS config</th><td class='dashed slbconf'>${poolInfo['rsconfig']}</td></tr>";
                echo '</table>';
                echo '</td><td>';
                // LB list
@@ -3975,14 +4206,13 @@ function renderVirtualService ($vsid)
                        echo '<table width=100%>';
                        foreach ($poolInfo['lblist'] as $object_id => $lbInfo)
                        {
-                               // FIXME: dname should be cached
-                               $oi = getObjectInfo ($object_id);
-                               echo "<tr><td colspan=2><a href='${root}?page=object&object_id=${object_id}'>";
-                               echo $oi['dname'] . '</a></td></tr>';
+                               echo "<tr><td colspan=2>";
+                               renderLBCell ($object_id);
+                               echo '</td></tr>';
                                if (!empty ($lbInfo['vsconfig']))
-                                       echo "<tr><th>VS config</th><td class=tdleft><pre>${lbInfo['vsconfig']}</pre></td></tr>";
+                                       echo "<tr><th>VS config</th><td class='dashed slbconf'>${lbInfo['vsconfig']}</td></tr>";
                                if (!empty ($lbInfo['rsconfig']))
-                                       echo "<tr><th>RS config</th><td class=tdleft><pre>${lbInfo['rsconfig']}</pre></td></tr>";
+                                       echo "<tr><th>RS config</th><td class='dashed slbconf'>${lbInfo['rsconfig']}</td></tr>";
                        }
                        echo '</table>';
                }
@@ -3996,16 +4226,17 @@ function renderVirtualService ($vsid)
        echo '</tr><table>';
 }
 
-function renderProgressBar ($percentage = 0)
+function renderProgressBar ($percentage = 0, $theme = '')
 {
        global $root;
        $done = ((int) ($percentage * 100));
-       echo "<img width=100 height=10 border=0 title='${done}%' src='${root}render_image.php?img=progressbar&done=${done}'>";
+       echo "<img width=100 height=10 border=0 title='${done}%' src='${root}render_image.php?img=progressbar&done=${done}";
+       echo (empty ($theme) ? '' : "&theme=${theme}") . "'>";
 }
 
 function renderRSPoolServerForm ($pool_id = 0)
 {
-       global $root, $pageno, $tabno, $nextorder;
+       global $pageno, $tabno, $nextorder;
        if ($pool_id <= 0)
        {
                showError ('Invalid pool_id', __FUNCTION__);
@@ -4023,13 +4254,12 @@ function renderRSPoolServerForm ($pool_id = 0)
                foreach ($poolInfo['rslist'] as $rsid => $rs)
                {
                        printOpFormIntro ('updRS', array ('rs_id' => $rsid));
-                       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}'>";
+                       echo "<tr valign=top class=row_${order}><td><a href='".makeHrefProcess(array('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><td>";
-                       printImageHREF ('save', 'Save changes', TRUE);
+                       printImageHREF ('SAVE', 'Save changes', TRUE);
                        echo "</td></tr></form>\n";
                        $order = $nextorder[$order];
                }
@@ -4084,7 +4314,7 @@ function renderRSPoolServerForm ($pool_id = 0)
 
 function renderRSPoolLBForm ($pool_id = 0)
 {
-       global $root, $pageno, $tabno, $nextorder;
+       global $pageno, $tabno, $nextorder;
        showMessageOrError();
 
        $poolInfo = getRSPoolInfo ($pool_id);
@@ -4101,20 +4331,17 @@ function renderRSPoolLBForm ($pool_id = 0)
                foreach ($poolInfo['lblist'] as $object_id => $vslist)
                        foreach ($vslist as $vs_id => $configs)
                        {
-                               $oi = getObjectInfo ($object_id);
                                printOpFormIntro ('updLB', array ('vs_id' => $vs_id, 'object_id' => $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}'>";
+                               echo "<tr valign=top class=row_${order}><td><a href='".makeHrefProcess(array('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=ipv4vs&vs_id=${vs_id}'>";
-                               $vsinfo = getVServiceInfo ($vs_id);
-                               echo buildVServiceName ($vsinfo) . '</a>';
-                               if (!empty ($vsinfo['name']))
-                                       echo " (${vsinfo['name']})";
+                               echo "<td class=tdleft>";
+                               renderLBCell ($object_id);
+                               echo "</td><td class=tdleft>";
+                               renderVSCell ($vs_id);
                                echo "</td><td><textarea name=vsconfig>${configs['vsconfig']}</textarea></td>";
                                echo "<td><textarea name=rsconfig>${configs['rsconfig']}</textarea></td><td>";
-                               printImageHREF ('save', 'Save changes', TRUE);
+                               printImageHREF ('SAVE', 'Save changes', TRUE);
                                echo "</td></tr></form>\n";
                                $order = $nextorder[$order];
                        }
@@ -4130,19 +4357,19 @@ function renderRSPoolLBForm ($pool_id = 0)
                foreach (getNarrowObjectList ($type) as $object)
                        echo "<option value='${object['id']}'>${object['dname']}</option>";
        echo "</select> ";
-       printSelect ($vs_list, 'vs_id');
+       printSelect ($vs_list, 'vs_id', NULL, 2);
        echo "</td><td>";
-       printImageHREF ('add', 'Configure LB', TRUE, 2);
+       printImageHREF ('add', 'Configure LB', TRUE, 5);
        echo "</td></tr>\n";
-       echo "<tr><th>VS config</th><td colspan=2><textarea name=vsconfig rows=10 cols=80></textarea></td></tr>";
-       echo "<tr><th>RS config</th><td colspan=2><textarea name=rsconfig rows=10 cols=80></textarea></td></tr>";
+       echo "<tr><th>VS config</th><td colspan=2><textarea tabindex=3 name=vsconfig rows=10 cols=80></textarea></td></tr>";
+       echo "<tr><th>RS config</th><td colspan=2><textarea tabindex=4 name=rsconfig rows=10 cols=80></textarea></td></tr>";
        echo "</form></table>\n";
        finishPortlet();
 }
 
 function renderVServiceLBForm ($vs_id = 0)
 {
-       global $root, $pageno, $tabno, $nextorder;
+       global $pageno, $tabno, $nextorder;
        showMessageOrError();
        $vsinfo = getVServiceInfo ($vs_id);
 
@@ -4155,16 +4382,17 @@ function renderVServiceLBForm ($vs_id = 0)
                foreach ($vsinfo['rspool'] as $pool_id => $rspinfo)
                        foreach ($rspinfo['lblist'] as $object_id => $configs)
                        {
-                               $oi = getObjectInfo ($object_id);
                                printOpFormIntro ('updLB', array ('pool_id' => $pool_id, 'object_id' => $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}'>";
+                               echo "<tr valign=top class=row_${order}><td><a href='".makeHrefProcess(array('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=ipv4rsp&pool_id=${pool_id}'>${rspinfo['name']}</a></td>";
-                               echo "<td><textarea name=vsconfig>${configs['vsconfig']}</textarea></td>";
+                               echo "<td class=tdleft>";
+                               renderLBCell ($object_id);
+                               echo "</td><td class=tdleft>";
+                               renderRSPoolCell ($pool_id, $rspinfo['name']);
+                               echo "</td><td><textarea name=vsconfig>${configs['vsconfig']}</textarea></td>";
                                echo "<td><textarea name=rsconfig>${configs['rsconfig']}</textarea></td><td>";
-                               printImageHREF ('save', 'Save changes', TRUE);
+                               printImageHREF ('SAVE', 'Save changes', TRUE);
                                echo "</td></tr></form>\n";
                                $order = $nextorder[$order];
                        }
@@ -4183,19 +4411,19 @@ function renderVServiceLBForm ($vs_id = 0)
                foreach (getNarrowObjectList ($type) as $object)
                        echo "<option value='${object['id']}'>${object['dname']}</option>";
        echo "</select> ";
-       printSelect ($rsplist, 'pool_id');
+       printSelect ($rsplist, 'pool_id', NULL, 2);
        echo "</td><td>";
-       printImageHREF ('add', 'Configure LB', TRUE, 2);
+       printImageHREF ('add', 'Configure LB', TRUE, 5);
        echo "</td></tr>\n";
-       echo "<tr><th>VS config</th><td colspan=2><textarea name=vsconfig rows=10 cols=80></textarea></td></tr>";
-       echo "<tr><th>RS config</th><td colspan=2><textarea name=rsconfig rows=10 cols=80></textarea></td></tr>";
+       echo "<tr><th>VS config</th><td colspan=2><textarea tabindex=3 name=vsconfig rows=10 cols=80></textarea></td></tr>";
+       echo "<tr><th>RS config</th><td colspan=2><textarea tabindex=4 name=rsconfig rows=10 cols=80></textarea></td></tr>";
        echo "</form></table>\n";
        finishPortlet();
 }
 
 function renderRSPool ($pool_id = 0)
 {
-       global $root;
+       global $nextorder;
        if ($pool_id <= 0)
        {
                showError ('Invalid pool_id', __FUNCTION__);
@@ -4213,29 +4441,40 @@ function renderRSPool ($pool_id = 0)
                echo "<tr><td colspan=2 align=center><h1>{$poolInfo['name']}</h1></td></tr>";
        echo "<tr><td class=pcleft>\n";
 
-       startPortlet ('Configuration');
+       startPortlet ('Summary');
        echo "<table border=0 cellspacing=0 cellpadding=3 width='100%'>\n";
        if (!empty ($poolInfo['name']))
                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 ("${root}?page=ipv4rsplist&");
+       printTagTRs (makeHref(array('page'=>'ipv4rsplist', 'tab'=>'default'))."&");
+       if (!empty ($poolInfo['vsconfig']))
+       {
+               echo "<tr><th width='50%' class=tdright>VS configuration:</th><td>&nbsp;</td></tr>\n";
+               echo "<tr><td colspan=2 class='dashed slbconf'>${poolInfo['vsconfig']}</td></tr>\n";
+       }
+       if (!empty ($poolInfo['rsconfig']))
+       {
+               echo "<tr><th width='50%' class=tdright>RS configuration:</th><td>&nbsp;</td></tr>\n";
+               echo "<tr><td colspan=2 class='dashed slbconf'>${poolInfo['rsconfig']}</td></tr>\n";
+       }
        echo "</table>";
        finishPortlet();
 
        startPortlet ('Load balancers (' . count ($poolInfo['lblist']) . ')');
        echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
        echo "<tr><th>VS</th><th>LB</th><th>VS config</th><th>RS config</th></tr>";
+       $order = 'odd';
        foreach ($poolInfo['lblist'] as $object_id => $vslist)
                foreach ($vslist as $vs_id => $configs)
        {
-               $oi = getObjectInfo ($object_id);
-               $vi = getVServiceInfo ($vs_id);
-               echo "<tr valign=top><td class=tdleft><a href='${root}?page=ipv4vs&vs_id=${vs_id}'>";
-               echo buildVServiceName ($vi);
-               echo "</a></td><td class=tdleft><a href='${root}?page=object&object_id=${object_id}'>${oi['dname']}</a></td>";
-               echo "<td class=tdleft><pre>${configs['vsconfig']}</pre></td>";
-               echo "<td class=tdleft><pre>${configs['rsconfig']}</pre></td></tr>\n";
+               echo "<tr valign=top class=row_${order}><td class=tdleft><a href='".makeHref(array('page'=>'ipv4vs', 'vs_id'=>$vs_id))."'>";
+               renderVSCell ($vs_id);
+               echo "</td><td>";
+               renderLBCell ($object_id);
+               echo "</td><td class=slbconf>${configs['vsconfig']}</td>";
+               echo "<td class=slbconf>${configs['rsconfig']}</td></tr>\n";
+               $order = $nextorder[$order];
        }
        echo "</table>\n";
        finishPortlet();
@@ -4252,8 +4491,8 @@ function renderRSPool ($pool_id = 0)
                        printImageHREF ('inservice', 'in service');
                else
                        printImageHREF ('notinservice', 'NOT in service');
-               echo "</td><td class=tdleft><a href='${root}?page=ipaddress&ip=${rs['rsip']}'>${rs['rsip']}</a></td>";
-               echo "<td class=tdleft>${rs['rsport']}</td><td class=tdleft><pre>${rs['rsconfig']}</pre></td></tr>\n";
+               echo "</td><td class=tdleft><a href='".makeHref(array('page'=>'ipaddress', 'ip'=>$rs['rsip']))."'>${rs['rsip']}</a></td>";
+               echo "<td class=tdleft>${rs['rsport']}</td><td class=slbconf>${rs['rsconfig']}</td></tr>\n";
        }
        echo "</table>\n";
        finishPortlet();
@@ -4263,7 +4502,7 @@ function renderRSPool ($pool_id = 0)
 
 function renderVSList ()
 {
-       global $root, $pageno, $nextorder;
+       global $pageno, $nextorder;
        $tagfilter = getTagFilter();
        $vslist = getVSList ($tagfilter, getTFMode());
        echo "<table border=0 class=objectview>\n";
@@ -4275,16 +4514,10 @@ function renderVSList ()
        $order = 'odd';
        foreach ($vslist as $vsid => $vsinfo)
        {
-               $vstags = loadIPv4VSTags ($vsid);
-               echo "<tr align=left valign=top class=row_${order}><td class=tdleft><a href='${root}?page=ipv4vs&vs_id=${vsid}'>" . buildVServiceName ($vsinfo);
-               echo "</a><br>${vsinfo['name']}";
-               if (count ($vstags))
-               {
-                       echo '<br>';
-                       echo serializeTags ($vstags, "${root}?page=${pageno}&tab=default&");
-               }
-               echo "</td><td><pre>${vsinfo['vsconfig']}</pre></td>";
-               echo "<td><pre>${vsinfo['rsconfig']}</pre></td>";
+               echo "<tr align=left valign=top class=row_${order}><td class=tdleft>";
+               renderVSCell ($vsid);
+               echo "</td><td class=slbconf>${vsinfo['vsconfig']}</td>";
+               echo "<td class=slbconf>${vsinfo['rsconfig']}</td>";
                echo "</tr>\n";
                $order = $nextorder[$order];
        }
@@ -4297,7 +4530,7 @@ function renderVSList ()
 
 function renderVSListEditForm ()
 {
-       global $root, $pageno, $tabno, $nextorder;
+       global $pageno, $tabno, $nextorder;
        showMessageOrError();
        $protocols = array ('TCP' => 'TCP', 'UDP' => 'UDP');
 
@@ -4340,7 +4573,7 @@ function renderVSListEditForm ()
                        printImageHREF ('nodelete', 'there are ' . $vsinfo['poolcount'] . ' RS pools configured');
                else
                {
-                       echo "<a href='${root}process.php?page=${pageno}&tab=${tabno}&op=del&vs_id=${vsid}'>";
+                       echo "<a href='".makeHrefProcess(array('op'=>'del', 'vs_id'=>$vsid))."'>";
                        printImageHREF ('delete', 'delete virtual service');
                        echo '</a>';
                }
@@ -4352,7 +4585,7 @@ function renderVSListEditForm ()
                echo "<td class=tdleft><input type=text name=name value='${vsinfo['name']}'></td>";
                echo "<td><textarea name=vsconfig>${vsinfo['vsconfig']}</textarea></td>";
                echo "<td><textarea name=rsconfig>${vsinfo['rsconfig']}</textarea></td><td>";
-               printImageHREF ('save', 'save changes', TRUE);
+               printImageHREF ('SAVE', 'save changes', TRUE);
                echo "</td></tr></form>\n";
                $order = $nextorder[$order];
        }
@@ -4362,7 +4595,7 @@ function renderVSListEditForm ()
 
 function renderRSPoolList ()
 {
-       global $root, $pageno, $nextorder;
+       global $pageno, $nextorder;
        $tagfilter = getTagFilter();
        $pool_list = getRSPoolList ($tagfilter, getTFMode());
        if ($pool_list === NULL)
@@ -4380,12 +4613,12 @@ function renderRSPoolList ()
        {
                $pooltags = loadIPv4RSPoolTags ($pool_id);
                echo "<tr valign=top class=row_${order}><td class=tdleft>";
-               echo "<a href='${root}?page=ipv4rsp&pool_id=${pool_id}'>" . (empty ($pool_info['name']) ? 'ANONYMOUS' : $pool_info['name']) . '</a>';
+               echo "<a href='".makeHref(array('page'=>'ipv4rspool', 'pool_id'=>$pool_id))."'>" . (empty ($pool_info['name']) ? 'ANONYMOUS' : $pool_info['name']) . '</a>';
                echo ($pool_info['refcnt'] ? ", ${pool_info['refcnt']}" : '');
                if (count ($pooltags))
                {
                        echo '<br>';
-                       echo serializeTags ($pooltags, "${root}?page=${pageno}&tab=default&");
+                       echo serializeTags ($pooltags, makeHref(array('page'=>$pageno, 'tab'=>'default'))."&");
                }
                echo "</td><td class=tdleft><pre>${pool_info['vsconfig']}</pre></td>";
                echo "<td class=tdleft><pre>${pool_info['rsconfig']}</pre></td>";
@@ -4401,7 +4634,7 @@ function renderRSPoolList ()
 
 function editRSPools ()
 {
-       global $root, $pageno, $tabno, $nextorder;
+       global $pageno, $tabno, $nextorder;
        showMessageOrError();
 
        startPortlet ('Add new');
@@ -4433,7 +4666,7 @@ function editRSPools ()
                        printImageHREF ('nodelete', 'RS pool is used ' . $pool_info['refcnt'] . ' time(s)');
                else
                {
-                       echo "<a href='${root}process.php?page=${pageno}&tab=${tabno}&op=del&pool_id=${pool_id}'>";
+                       echo "<a href='".makeHrefProcess(array('op'=>'del', 'pool_id'=>$pool_id))."'>";
                        printImageHREF ('delete', 'delete real server pool');
                        echo '</a>';
                }
@@ -4451,7 +4684,7 @@ function editRSPools ()
 
 function renderRealServerList ()
 {
-       global $root, $nextorder;
+       global $nextorder;
        $rslist = getRSList ();
        $pool_list = getRSPoolList ();
        echo "<table class=widetable border=0 cellpadding=10 cellspacing=0 align=center>\n";
@@ -4465,14 +4698,14 @@ function renderRealServerList ()
                        $order = $nextorder[$order];
                        $last_pool_id = $rsinfo['rspool_id'];
                }
-               echo "<tr valign=top class=row_${order}><td><a href='${root}?page=ipv4rsp&pool_id=${rsinfo['rspool_id']}'>";
+               echo "<tr valign=top class=row_${order}><td><a href='".makeHref(array('page'=>'ipv4rspool', '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')
                        printImageHREF ('inservice', 'in service');
                else
                        printImageHREF ('notinservice', 'NOT in service');
-               echo "</td><td><a href='${root}?page=ipaddress&ip=${rsinfo['rsip']}'>${rsinfo['rsip']}</a></td>";
+               echo "</td><td><a href='".makeHref(array('page'=>'ipaddress', 'ip'=>$rsinfo['rsip']))."'>${rsinfo['rsip']}</a></td>";
                echo "<td>${rsinfo['rsport']}</td>";
                echo "<td><pre>${rsinfo['rsconfig']}</pre></td>";
                echo "</tr>\n";
@@ -4482,7 +4715,7 @@ function renderRealServerList ()
 
 function renderLBList ()
 {
-       global $root, $nextorder;
+       global $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();
@@ -4491,7 +4724,7 @@ function renderLBList ()
        {
                if (!isset ($oicache[$object_id]))
                        $oicache[$object_id] = getObjectInfo ($object_id);
-               echo "<tr valign=top class=row_${order}><td><a href='${root}?page=object&object_id=${object_id}'>";
+               echo "<tr valign=top class=row_${order}><td><a href='".makeHref(array('page'=>'object', 'object_id'=>$object_id))."'>";
                echo $oicache[$object_id]['dname'] . '</a></td>';
                echo "<td>${poolcount}</td></tr>";
                $order = $nextorder[$order];
@@ -4537,9 +4770,10 @@ function renderLivePTR ($id = 0)
                $page = $_REQUEST['pg'];
        else
                $page=0;
-       global $root, $pageno, $tabno;
+       global $pageno, $tabno;
        $maxperpage = getConfigVar ('IPV4_ADDRS_PER_PAGE');
-       $range = getIPv4Network ($id);
+       $range = getIPv4NetworkInfo ($id);
+       loadIPv4AddrList ($range);
        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>";
@@ -4562,7 +4796,7 @@ function renderLivePTR ($id = 0)
                if ($i == $page)
                        echo "<b>$i</b> ";
                else
-                       echo "<a href='${root}?page=${pageno}&tab=${tabno}&id=$id&pg=$i'>$i</a> ";
+                       echo "<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'id'=>$id, 'pg'=>$i))."'>$i</a> ";
        echo "</center>";
 
        // FIXME: address counter could be calculated incorrectly in some cases
@@ -4609,7 +4843,7 @@ function renderLivePTR ($id = 0)
                echo "><td class='tdleft";
                if (!empty ($range['addrlist'][$ip]['class']))
                        echo ' ' . $range['addrlist'][$ip]['class'];
-               echo "'><a href='${root}?page=ipaddress&ip=${straddr}'>${straddr}</a></td>";
+               echo "'><a href='".makeHref(array('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}>";
@@ -4663,13 +4897,13 @@ function renderAutoPortsForm ($object_id = 0)
 
 function renderTagRowForViewer ($taginfo, $level = 0)
 {
-       echo '<tr><td align=left>';
-       echo '<div title="id = ' . $taginfo['id'] . '">';
-       for ($i = 0; $i < $level - 1; $i++)
-               printImageHREF ('spacer');
-       if ($level)
-               printImageHREF ('verge');
-       echo $taginfo['tag'] . '</div>';
+       if (!count ($taginfo['kids']))
+               $level++; // Shift instead of placing a spacer. This won't impact any nested nodes.
+       echo "<tr><td align=left style='padding-left: " . ($level * 16) . "px;'>";
+       if (count ($taginfo['kids']))
+               printImageHREF ('node-expanded-static');
+       echo '<span title="id = ' . $taginfo['id'] . '">';
+       echo $taginfo['tag'] . '</span>';
        echo "</td></tr>\n";
        foreach ($taginfo['kids'] as $kid)
                renderTagRowForViewer ($kid, $level + 1);
@@ -4678,13 +4912,8 @@ function renderTagRowForViewer ($taginfo, $level = 0)
 // FIXME: generated hyperlink must depend on the realm given
 function renderTagRowForCloud ($taginfo, $realm, $level = 0)
 {
-       global $root;
-       echo '<tr><td align=left>';
-       for ($i = 0; $i < $level - 1; $i++)
-               printImageHREF ('spacer');
-       if ($level)
-               printImageHREF ('verge');
-       echo "<a href='${root}?page=objgroup&group_id=0&tagfilter[]=${taginfo['id']}'>";
+       echo "<tr><td align=left style='padding-left: " . ($level * 16) . "px;'>";
+       echo "<a href='".makeHref(array('page'=>'objgroup', 'group_id'=>0, 'tagfilter[]'=>$taginfo['id']))."'>";
        echo $taginfo['tag'] . '</a>';
        if (isset ($taginfo['refcnt'][$realm]))
                echo ' (' . $taginfo['refcnt'][$realm] . ')';
@@ -4695,19 +4924,21 @@ function renderTagRowForCloud ($taginfo, $realm, $level = 0)
 
 function renderTagRowForEditor ($taginfo, $level = 0)
 {
-       global $root, $pageno, $tabno, $taglist;
-       echo '<tr><td class=tdleft>';
-       for ($i = 0; $i < $level; $i++)
-               printImageHREF ('spacer');
+       global $pageno, $tabno, $taglist;
+       if (!count ($taginfo['kids']))
+               $level++; // Idem
+       echo "<tr><td align=left style='padding-left: " . ($level * 16) . "px;'>";
+       if (count ($taginfo['kids']))
+               printImageHREF ('node-expanded-static');
        $nrefs = 0;
        foreach ($taginfo['refcnt'] as $part)
                $nrefs += $part;
        if ($nrefs > 0 or count ($taginfo['kids']) > 0)
-               printImageHREF ('nodelete', "${nrefs} references, " . count ($taginfo['kids']) . ' sub-tags');
+               printImageHREF ('nodestroy', "${nrefs} references, " . count ($taginfo['kids']) . ' sub-tags');
        else
        {
-               echo "<a href='${root}process.php?page=${pageno}&tab=${tabno}&op=destroyTag&tag_id=${taginfo['id']}'>";
-               printImageHREF ('delete', 'Delete tag');
+               echo "<a href='".makeHrefProcess(array('op'=>'destroyTag', 'tag_id'=>$taginfo['id']))."'>";
+               printImageHREF ('destroy', 'Delete tag');
                echo "</a>";
        }
        echo "</td>\n<td>";
@@ -4755,6 +4986,20 @@ function renderTagCloud ($realm = '')
 
 function renderTagTreeEditor ()
 {
+       function printNewItemTR ()
+       {
+               global $taglist;
+               printOpFormIntro ('createTag');
+               echo "<tr><td class=tdleft>";
+               printImageHREF ('create', 'Create tag', TRUE);
+               echo '</td><td><input type=text name=tag_name tabindex=100></td><td><select name=parent_id tabindex=101>';
+               echo "<option value=0>-- NONE --</option>\n";
+               foreach ($taglist as $taginfo)
+                       echo "<option value=${taginfo['id']}>${taginfo['tag']}</option>";
+               echo "</select></td><td>";
+               printImageHREF ('create', 'Create tag', TRUE, 102);
+               echo "</td></tr></form>\n";
+       }
        global $taglist, $tagtree;
        showMessageOrError();
 
@@ -4785,17 +5030,12 @@ function renderTagTreeEditor ()
        startPortlet ('tag tree');
        echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
        echo "<tr><th>&nbsp;</th><th>tag</th><th>parent</th><th>&nbsp;</th></tr>\n";
+       if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
+               printNewItemTR();
        foreach ($tagtree as $taginfo)
                renderTagRowForEditor ($taginfo);
-       printOpFormIntro ('createTag');
-       echo "<tr><td class=tdleft>";
-       printImageHREF ('grant', 'Create tag', TRUE, 102);
-       echo '</td><td><input type=text name=tag_name tabindex=100></td><td><select name=parent_id tabindex=101>';
-       echo "<option value=0>-- NONE --</option>\n";
-       foreach ($taglist as $taginfo)
-               echo "<option value=${taginfo['id']}>${taginfo['tag']}</option>";
-       echo "</select></td><td>&nbsp;</td></tr>";
-       echo "</form>\n";
+       if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
+               printNewItemTR();
        echo '</table>';
        finishPortlet();
 }
@@ -4805,13 +5045,26 @@ function renderTagTreeEditor ()
 function renderTagOption ($taginfo, $level = 0)
 {
        global $expl_tags;
+       $self = __FUNCTION__;
        $selected = tagOnChain ($taginfo, $expl_tags) ? ' selected' : '';
        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);
+               $self ($kid, $level + 1);
+}
+
+function renderTagCheckbox ($taginfo, $level = 0)
+{
+       global $expl_tags;
+       $self = __FUNCTION__;
+       $selected = tagOnChain ($taginfo, $expl_tags) ? ' checked' : '';
+       echo "<tr><td colspan=2 align=left style='padding-left: " . ($level * 16) . "px;'>";
+       echo '<input type=checkbox name=taglist[] value=' . $taginfo['id'] . "${selected}> ";
+       echo $taginfo['tag'] . "</td></tr>\n";
+       foreach ($taginfo['kids'] as $kid)
+               $self ($kid, $level + 1);
 }
 
 // Idem, but select those, which are shown on the $_REQUEST['tagfiler'] array.
@@ -4827,57 +5080,27 @@ function renderTagOptionForFilter ($taginfo, $tagfilter, $realm, $level = 0)
                renderTagOptionForFilter ($kid, $tagfilter, $realm, $level + 1);
 }
 
-function renderObjectTags ($id)
-{
-       renderEntityTagChainEditor ('object', 'object_id', $id);
-}
-
-function renderIPv4PrefixTags ($id)
-{
-       renderEntityTagChainEditor ('ipv4net', 'id', $id);
-}
-
-function renderRackTags ($id)
-{
-       renderEntityTagChainEditor ('rack', 'rack_id', $id);
-}
-
-function renderIPv4VSTags ($id)
-{
-       renderEntityTagChainEditor ('ip4vs', 'vs_id', $id);
-}
-
-function renderIPv4RSPoolTags ($id)
+function renderEntityTags ($entity_id = 0)
 {
-       renderEntityTagChainEditor ('ip4rspool', 'pool_id', $id);
-}
-
-function renderUserTags ($id)
-{
-       renderEntityTagChainEditor ('user', 'user_id', $id);
-}
-
-function renderEntityTagChainEditor ($entity_realm = '', $bypass_name, $entity_id = 0)
-{
-       global $tagtree, $expl_tags;
-       if ($entity_realm == '' or $entity_id <= 0)
+       global $tagtree, $expl_tags, $pageno, $page, $etype_by_pageno;
+       if ($entity_id <= 0)
        {
                showError ('Invalid or missing arguments', __FUNCTION__);
                return;
        }
        showMessageOrError();
+       $entity_realm = $etype_by_pageno[$pageno];
+       $bypass_name = $page[$pageno]['bypass'];
        startPortlet ('Tag list');
        if (count ($expl_tags))
                echo '<h3>(' . serializeTags ($expl_tags) . ')</h3>';
        echo '<table border=0 align=center>';
-       echo '<tr><td colspan=2><h4>hold Ctrl to select more, than one</h4></td></tr>';
        printOpFormIntro ('saveTags', array ($bypass_name => $entity_id));
-       echo '<tr><td colspan=2><select name=taglist[] multiple size=' . getConfigVar ('MAXSELSIZE') . '>';
        foreach ($tagtree as $taginfo)
-               renderTagOption ($taginfo);
-       echo '</select></td></tr><tr><td>';
+               renderTagCheckbox ($taginfo);
+       echo '<tr><td class=tdleft>';
        printImageHREF ('SAVE', 'Save changes', TRUE);
-       echo "</form></td><td>";
+       echo "</form></td><td class=tdright>";
        printOpFormIntro ('saveTags', array ($bypass_name => $entity_id, 'taglist[]' => ''));
        printImageHREF ('CLEAR', 'Reset all tags', TRUE);
        echo '</form></td></tr></table>';
@@ -4889,17 +5112,17 @@ function printTagTRs ($baseurl = '')
        global $expl_tags, $impl_tags, $auto_tags;
        if (getConfigVar ('SHOW_EXPLICIT_TAGS') == 'yes' and count ($expl_tags))
        {
-               echo "<tr><th width='50%' class=tag_list_th>Explicit tags:</th><td class=tdleft>";
+               echo "<tr><th width='50%' class=tdright><span class=tagheader>Explicit tags</span>:</th><td class=tdleft>";
                echo serializeTags ($expl_tags, $baseurl) . "</td></tr>\n";
        }
        if (getConfigVar ('SHOW_IMPLICIT_TAGS') == 'yes' and count ($impl_tags))
        {
-               echo "<tr><th width='50%' class=tag_list_th>Implicit tags:</th><td class=tdleft>";
+               echo "<tr><th width='50%' class=tdright>Implicit tags:</th><td class=tdleft>";
                echo serializeTags ($impl_tags, $baseurl) . "</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 "<tr><th width='50%' class=tdright>Automatic tags:</th><td class=tdleft>";
                echo serializeTags ($auto_tags) . "</td></tr>\n";
        }
 }
@@ -4916,8 +5139,6 @@ function getTFMode ()
 function renderTagFilterPortlet ($tagfilter, $realm, $bypass_name = '', $bypass_value = '')
 {
        global $pageno, $tabno, $taglist, $tagtree;
-       // Temporary hack, needs to be reviewed later.
-       $tagfilter = isset ($_REQUEST['tagfilter']) ? $_REQUEST['tagfilter'] : array();
        $objectivetags = getObjectiveTagTree ($tagtree, $realm);
        startPortlet ('Tag filter');
        if (!count ($objectivetags))
@@ -4934,14 +5155,14 @@ function renderTagFilterPortlet ($tagfilter, $realm, $bypass_name = '', $bypass_
        echo "<input type=hidden name=tab value=${tabno}>\n";
        if ($bypass_name != '')
                echo "<input type=hidden name=${bypass_name} value='${bypass_value}'>\n";
-       echo '<tr><td colspan=2><select name=tagfilter[] multiple>';
+       echo '<tr><td colspan=2><select name=tagfilter[] multiple size=' . getConfigVar ('MAXSELSIZE') . '>';
        foreach ($objectivetags as $taginfo)
                renderTagOptionForFilter ($taginfo, complementByKids ($tagfilter), $realm);
        echo '</select></td></tr><tr><td>';
 //     $tfmode = getTFMode();
 //     echo '<input type=radio name=tfmode value=all' . ($tfmode == 'all' ? ' checked' : '') . '>all ';
 //     echo '<input type=radio name=tfmode value=any' . ($tfmode == 'any' ? ' checked' : '') . '>any ';
-       printImageHREF ('save', 'apply', TRUE);
+       printImageHREF ('apply', 'Apply filter', TRUE);
        echo "</form></td><td>";
 
        // "reset"
@@ -4964,10 +5185,10 @@ function renderTagSelect ()
                echo "No tags defined";
                return;
        }
-       echo '<select name=taglist[] multiple>';
+       echo '<div class=tagselector><table border=0 align=center>';
        foreach ($tagtree as $taginfo)
-               renderTagOption ($taginfo);
-       echo '</select><br>';
+               renderTagCheckbox ($taginfo);
+       echo '</table></div>';
 }
 
 function renderTagRollerForRow ($row_id)
@@ -4991,7 +5212,7 @@ function renderTagRollerForRow ($row_id)
 
 function renderObjectSLB ($object_id)
 {
-       global $root, $pageno, $tabno, $nextorder;
+       global $pageno, $tabno, $nextorder;
        showMessageOrError();
        $vs_list = $rsplist = array();
        foreach (getVSList() as $vsid => $vsinfo)
@@ -5003,14 +5224,14 @@ function renderObjectSLB ($object_id)
        echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
        printOpFormIntro ('addLB');
        echo "<tr valign=top><th>VS / RS pool</th><td class=tdleft>";
-       printSelect ($vs_list, 'vs_id');
+       printSelect ($vs_list, 'vs_id', NULL, 1);
        echo "</td><td>";
-       printSelect ($rsplist, 'pool_id');
+       printSelect ($rsplist, 'pool_id', NULL, 2);
        echo "</td><td>";
-       printImageHREF ('add', 'Configure LB', TRUE, 2);
+       printImageHREF ('add', 'Configure LB', TRUE, 5);
        echo "</td></tr>\n";
-       echo "<tr><th>VS config</th><td colspan=2><textarea name=vsconfig rows=10 cols=80></textarea></td></tr>";
-       echo "<tr><th>RS config</th><td colspan=2><textarea name=rsconfig rows=10 cols=80></textarea></td></tr>";
+       echo "<tr><th>VS config</th><td colspan=2><textarea tabindex=3 name=vsconfig rows=10 cols=80></textarea></td></tr>";
+       echo "<tr><th>RS config</th><td colspan=2><textarea tabindex=4 name=rsconfig rows=10 cols=80></textarea></td></tr>";
        echo "</form></table>\n";
        finishPortlet();
 
@@ -5024,17 +5245,16 @@ function renderObjectSLB ($object_id)
                foreach ($myvslist as $vs_id => $vsinfo)
                {
                        printOpFormIntro ('updLB', array ('vs_id' => $vs_id, 'pool_id' => $vsinfo['pool_id']));
-                       echo "<tr valign=top class=row_${order}><td><a href='${root}process.php?page=${pageno}&tab=${tabno}&op=delLB&pool_id=${vsinfo['pool_id']}&object_id=${object_id}&vs_id=${vs_id}'>";
+                       echo "<tr valign=top class=row_${order}><td><a href='".makeHrefProcess(array('op'=>'delLB', 'pool_id'=>$vsinfo['pool_id'], 'object_id'=>$object_id, 'vs_id'=>$vs_id))."'>";
                        printImageHREF ('delete', 'Unconfigure');
                        echo "</a></td>";
-                       echo "</td><td class=tdleft><a href='${root}?page=ipv4vs&vs_id=${vs_id}'>";
-                       echo buildVServiceName ($vsinfo) . "</a>";
-                       if (!empty ($vsinfo['name']))
-                               echo '<br>' . $vsinfo['name'];
-                       echo "</td><td class=tdleft>" . $rsplist[$vsinfo['pool_id']] . "</td>";
-                       echo "<td><textarea name=vsconfig>${vsinfo['vsconfig']}</textarea></td>";
+                       echo "</td><td class=tdleft>";
+                       renderVSCell ($vs_id);
+                       echo "</td><td class=tdleft>";
+                       renderRSPoolCell ($vsinfo['pool_id'], $rsplist[$vsinfo['pool_id']]);
+                       echo "</td><td><textarea name=vsconfig>${vsinfo['vsconfig']}</textarea></td>";
                        echo "<td><textarea name=rsconfig>${vsinfo['rsconfig']}</textarea></td><td>";
-                       printImageHREF ('save', 'Save changes', TRUE);
+                       printImageHREF ('SAVE', 'Save changes', TRUE);
                        echo "</td></tr></form>\n";
                        $order = $nextorder[$order];
                }
@@ -5064,25 +5284,25 @@ function renderEditVService ($vsid)
        $vsinfo = getVServiceInfo ($vsid);
        printOpFormIntro ('updIPv4VS');
        echo '<table border=0 align=center>';
-       echo "<tr><th class=tdright>VIP:</th><td class=tdleft><input type=text name=vip value='${vsinfo['vip']}'></td></tr>\n";
-       echo "<tr><th class=tdright>port:</th><td class=tdleft><input type=text name=vport value='${vsinfo['vport']}'></td></tr>\n";
+       echo "<tr><th class=tdright>VIP:</th><td class=tdleft><input tabindex=1 type=text name=vip value='${vsinfo['vip']}'></td></tr>\n";
+       echo "<tr><th class=tdright>port:</th><td class=tdleft><input tabindex=2 type=text name=vport value='${vsinfo['vport']}'></td></tr>\n";
        echo "<tr><th class=tdright>proto:</th><td class=tdleft>";
        printSelect (array ('TCP' => 'TCP', 'UDP' => 'UDP'), 'proto', $vsinfo['proto']);
        echo "</td></tr>\n";
-       echo "<tr><th class=tdright>name:</th><td class=tdleft><input type=text name=name value='${vsinfo['name']}'></td></tr>\n";
-       echo "<tr><th class=tdright>VS config:</th><td class=tdleft><textarea name=vsconfig rows=20 cols=80>${vsinfo['vsconfig']}</textarea></td></tr>\n";
-       echo "<tr><th class=tdright>RS config:</th><td class=tdleft><textarea name=rsconfig rows=20 cols=80>${vsinfo['rsconfig']}</textarea></td></tr>\n";
+       echo "<tr><th class=tdright>name:</th><td class=tdleft><input tabindex=4 type=text name=name value='${vsinfo['name']}'></td></tr>\n";
+       echo "<tr><th class=tdright>VS config:</th><td class=tdleft><textarea tabindex=5 name=vsconfig rows=20 cols=80>${vsinfo['vsconfig']}</textarea></td></tr>\n";
+       echo "<tr><th class=tdright>RS config:</th><td class=tdleft><textarea tabindex=6 name=rsconfig rows=20 cols=80>${vsinfo['rsconfig']}</textarea></td></tr>\n";
        echo "<tr><th class=submit colspan=2>";
-       printImageHREF ('SAVE', 'Save changes', TRUE);
+       printImageHREF ('SAVE', 'Save changes', TRUE, 7);
        echo "</td></tr>\n";
        echo "</table></form>\n";
 }
 
 function dump ($var)
 {
-       echo '<pre>';
+       echo '<div align=left><pre>';
        print_r ($var);
-       echo '</pre>';
+       echo '</pre></div>';
 }
 
 function renderRackCodeViewer ()
@@ -5092,7 +5312,7 @@ function renderRackCodeViewer ()
        $lineno = 1;
        foreach (explode ("\n", $text) as $line)
        {
-               echo "<tr><td class=tdright><a name=line${lineno}>${lineno}</td>";
+               echo "<tr><td class=tdright><a name=line${lineno}>${lineno}</a></td>";
                echo "<td class=tdleft>${line}</td></tr>";
                $lineno++;
        }
@@ -5118,37 +5338,34 @@ function renderUser ($user_id)
 {
        global $accounts, $expl_tags, $impl_tags;
        $username = getUsernameByID ($user_id);
+
+       startPortlet ('summary');
        echo '<table border=0 align=center>';
-       echo "<tr><th class=tdright>Account name:</th><td>${username}</td></tr>";
-       echo '<tr><th class=tdright>Real name:</th><td>' . $accounts[$username]['user_realname'] . '</td></tr>';
-       echo '<tr><th class=tdright>Enabled:</th><td>';
-       // This is weird, some other image titles have to be used.
-       if ($accounts[$username]['user_enabled'] == 'yes')
-               printImageHREF ('blockuser', 'enabled');
-       else
-               printImageHREF ('unblockuser', 'disabled');
-       echo '</td></tr>';
+       echo "<tr><th class=tdright>Account name:</th><td class=tdleft>${username}</td></tr>";
+       echo '<tr><th class=tdright>Real name:</th><td class=tdleft>' . $accounts[$username]['user_realname'] . '</td></tr>';
        // Using printTagTRs() is inappropriate here, because autotags will be filled with current user's
        // data, not the viewed one.
-//     printTagTRs ("${root}?page=userlist&");
-       $baseurl = "${root}?page=userlist&tab=default&";
+       $baseurl = makeHref(array('page'=>'userlist', 'tab'=>'default'))."&";
        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 "<tr><th width='50%' class=tdright><span class=tagheader>Explicit tags</span>:</th><td class=tdleft>";
                echo serializeTags ($expl_tags, $baseurl) . "</td></tr>\n";
        }
        if (getConfigVar ('SHOW_IMPLICIT_TAGS') == 'yes' and count ($impl_tags))
        {
-               echo "<tr><th width='50%' class=tag_list_th>Implicit tags:</th><td class=tdleft>";
+               echo "<tr><th width='50%' class=tdright><span class=tagheader>Implicit tags</span>:</th><td class=tdleft>";
                echo serializeTags ($impl_tags, $baseurl) . "</td></tr>\n";
        }
        $target_auto_tags = getUserAutoTags ($username);
        if (getConfigVar ('SHOW_AUTOMATIC_TAGS') == 'yes' and count ($target_auto_tags))
        {
-               echo "<tr><th width='50%' class=tag_list_th>Automatic tags:</th><td class=tdleft>";
+               echo "<tr><th width='50%' class=tdright><span class=tagheader>Automatic tags</span>:</th><td class=tdleft>";
                echo serializeTags ($target_auto_tags) . "</td></tr>\n";
        }
        echo '</table>';
+       finishPortlet();
+
+       renderFilesPortlet ('user', $user_id);
 }
 
 function renderMyPasswordEditor ()
@@ -5173,23 +5390,22 @@ function renderAccessDenied ()
        echo "<link rel=icon href='" . getFaviconURL() . "' type='image/x-icon' />";
        echo "<link rel=icon href='" . getFaviconURL() . "' type='image/x-icon' />";
        echo "</head><body>";
-       global $user_tags, $auto_tags, $expl_tags, $impl_tags, $pageno, $tabno;
+       global $root, $auto_tags, $expl_tags, $impl_tags, $pageno, $tabno;
        echo "<table border=1 cellspacing=0 cellpadding=3 width='50%' align=center>\n";
        echo '<tr><th colspan=2><h3>';
        printImageHREF ('DENIED');
        echo ' access denied ';
        printImageHREF ('DENIED');
        echo '</h3></th></tr>';
-       echo "<tr><th width='50%' class=tag_list_th>Explicit tags:</th><td class=tdleft>";
+       echo "<tr><th width='50%' class=tdright><span class=tagheader>Explicit tags</span>:</th><td class=tdleft>";
        echo serializeTags ($expl_tags) . "&nbsp;</td></tr>\n";
-       echo "<tr><th width='50%' class=tag_list_th>Implicit tags:</th><td class=tdleft>";
+       echo "<tr><th width='50%' class=tdright><span class=tagheader>Implicit tags</span>:</th><td class=tdleft>";
        echo serializeTags ($impl_tags) . "&nbsp;</td></tr>\n";
-       echo "<tr><th width='50%' class=tag_list_th>Automatic tags:</th><td class=tdleft>";
+       echo "<tr><th width='50%' class=tdright><span class=tagheader>Automatic tags</span>:</th><td class=tdleft>";
        echo serializeTags ($auto_tags) . "&nbsp;</td></tr>\n";
-       echo "<tr><th width='50%' class=tag_list_th>This user tags:</th><td class=tdleft>";
-       echo serializeTags ($user_tags) . "&nbsp;</td></tr>\n";
-       echo "<tr><th width='50%' class=tag_list_th>Requested page:</th><td class=tdleft>${pageno}</td></tr>\n";
-       echo "<tr><th width='50%' class=tag_list_th>Requested tab:</th><td class=tdleft>${tabno}</td></tr>\n";
+       echo "<tr><th width='50%' class=tdright>Requested page:</th><td class=tdleft>${pageno}</td></tr>\n";
+       echo "<tr><th width='50%' class=tdright>Requested tab:</th><td class=tdleft>${tabno}</td></tr>\n";
+       echo "<tr><td colspan=2 align=center>Click <a href='${root}?logout'>here</a> to logout.</td></tr>\n";
        echo "</table>\n";
        echo "</body></html>";
 }
@@ -5203,19 +5419,385 @@ function renderMyAccount ()
        echo "</table>";
 }
 
+// File-related functions
+function renderFileSpace ()
+{
+       global $taglist, $tagtree;
+       echo "<table border=0 class=objectview>\n";
+       echo "<tr><td class=pcleft width='50%'>";
+       startPortlet ('View all by link');
+       $linkInfo = getFileLinkInfo();
+       if ($linkInfo === NULL)
+       {
+               showError ('getFileLinkInfo() failed', __FUNCTION__);
+               return;
+       }
+       if (count ($linkInfo) == 0)
+               echo "No files exist in DB";
+       else
+       {
+               echo '<div align=left><ul>';
+               foreach ($linkInfo as $li)
+                       echo "<li><a href='".makeHref(array('page'=>'filesbylink', 'entity_type'=>$li['entity_type']))."'>${li['name']}</a> (${li['count']})</li>";
+               echo '</ul></div>';
+       }
+       finishPortlet();
+       echo '</td><td class=pcright>';
+       renderTagFilterPortlet (getTagFilter(), 'file');
+       echo "</td></tr></table>\n";
+}
+
+function renderFile ($file_id = 0)
+{
+       global $nextorder, $aac, $root;
+       if ($file_id <= 0)
+       {
+               showError ('Invalid file_id', __FUNCTION__);
+               return;
+       }
+       $file = getFileInfo ($file_id);
+       if ($file == NULL)
+       {
+               showError ('getFileInfo() failed', __FUNCTION__);
+               return;
+       }
+       echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
+       echo "<tr><td colspan=2 align=center><h1>${file['name']}</h1></td></tr>\n";
+       echo "<tr><td class=pcleft>";
+       startPortlet ('summary');
+       echo "<table border=0 cellspacing=0 cellpadding=3 width='100%'>\n";
+       echo "<tr><th width='50%' class=tdright>Type:</th>";
+       printf("<td class=tdleft>%s</td></tr>", $file['type']);
+       echo "<tr><th width='50%' class=tdright>Size:</th>";
+       printf("<td class=tdleft>%s</td></tr>", formatFileSize($file['size']));
+       echo "<tr><th width='50%' class=tdright>Created:</th>";
+       printf("<td class=tdleft>%s</td></tr>", formatTimestamp($file['ctime']));
+       echo "<tr><th width='50%' class=tdright>Modified:</th>";
+       printf("<td class=tdleft>%s</td></tr>", formatTimestamp($file['mtime']));
+       echo "<tr><th width='50%' class=tdright>Accessed:</th>";
+       printf("<td class=tdleft>%s</td></tr>", formatTimestamp($file['atime']));
+
+       echo "<tr><th width='50%' class=tdright>Download file:</th>";
+       echo "<td class=tdleft><a href='${root}download.php?file_id=${file_id}'>";
+       printImageHREF ('download', 'Download file');
+       echo "</a></td></tr>\n";
+
+       echo "<tr><th width='50%' class=tdright>Upload replacement:</th>";
+       printOpFormIntro ('replaceFile', array ('MAX_FILE_SIZE' => convertToBytes(get_cfg_var('upload_max_filesize'))), TRUE);
+       echo "<td class=tdleft><input type='file' size='10' name='file' tabindex=100>&nbsp;\n";
+       printImageHREF ('save', 'Save changes', TRUE, 101);
+       echo "</form></td></tr>\n";
+
+       printTagTRs (makeHref(array('page'=>'files', 'tab'=>'default'))."&");
+       echo "</table><br>\n";
+       finishPortlet();
+
+       if (!empty ($file['comment']))
+       {
+               startPortlet ('comment');
+               echo '<div class=commentblock>' . string_insert_hrefs ($file['comment']) . '</div>';
+               finishPortlet ();
+       }
+
+       $links = getFileLinks ($file_id);
+       if (count ($links))
+       {
+               startPortlet ('Links');
+               ///usort($ports, 'sortByName');
+               echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
+               echo "<tr><th>Linked to</th><th>Name</th></tr>\n";
+               foreach ($links as $link)
+               {
+                       echo "<tr><td>${link['entity_type']}</td>";
+                       echo "<td><a href='".makeHref(array('page'=>$link['page'], $link['id_name']=>$link['entity_id']))."'>${link['name']}</a></td>";
+                       echo "</tr>\n";
+               }
+               echo "</table><br>\n";
+               finishPortlet();
+       }
+       echo "</td></tr>";
+       echo "</table>\n";
+}
+
+function renderFileProperties ($file_id = 0)
+{
+       global $root;
+       $file = getFileInfo ($file_id);
+       if ($file === NULL)
+       {
+               showError ('getFileInfo() failed', __FUNCTION__);
+               return;
+       }
+       showMessageOrError();
+       echo '<table border=0 align=center>';
+       printOpFormIntro ('updateFile');
+       echo "<tr><th class=tdright>MIME-type:</th><td class=tdleft>${file['type']}</td></tr>";
+       echo "<tr><th class=tdright>Filename:</th><td class=tdleft><input tabindex=100 type=text name=name value='${file['name']}'></td></tr>\n";
+       echo "<tr><th class=tdright>Comment:</th><td class=tdleft><input tabindex=101 type=text name=comment value='${file['comment']}'></td></tr>\n";
+       echo "<tr><th class=submit colspan=2>";
+       printImageHREF ('SAVE', 'Save changes', TRUE, 102);
+       echo '</th></tr></form></table>';
+}
+
+// Used for uploading a parentless file
+function renderFileUploadForm ()
+{
+       global $aat, $pageno, $tabno, $nextorder;
+
+       showMessageOrError();
+       startPortlet ('Upload new');
+       echo "<table border=0 cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
+       echo "<tr><th>File</th><th>Comment</th><th></th></tr>\n";
+
+       printOpFormIntro ('addFile', array ('MAX_FILE_SIZE' => convertToBytes(get_cfg_var('upload_max_filesize'))), TRUE);
+       echo "<tr>";
+       echo "<td class=tdleft><input type='file' size='10' name='file' tabindex=100></td>\n";
+       echo "<td class=tdleft><input type='text' size='15' name='comment' tabindex=101></td>\n";
+       echo '<td>';
+       printImageHREF ('CREATE', 'Upload file', TRUE, 102);
+       echo '</td></tr></form>';
+       echo "</table><br>\n";
+       finishPortlet();
+}
+
+function renderFilesByLink ()
+{
+       global $pageno, $tabno, $nextorder, $taglist, $tagtree, $root;
+       assertStringArg ('entity_type', __FUNCTION__, TRUE);
+       $entity_type = $_REQUEST['entity_type'];
+       $tagfilter = getTagFilter();
+       $tagfilter_str = getTagFilterStr ($tagfilter);
+       echo "<table border=0 class=objectview>\n";
+       echo "<tr><td class=pcleft width='25%'>";
+
+       startPortlet ('change type');
+       $linkInfo = getFileLinkInfo();
+       if ($linkInfo === NULL)
+       {
+               showError ('getFileLinkInfo() failed', __FUNCTION__);
+               return;
+       }
+       if (count ($linkInfo) == 0)
+               echo 'No files exist in DB';
+       else
+       {
+               echo '<div align=left><ul>';
+               foreach ($linkInfo as $li)
+               {
+                       echo "<li><a href='".makeHref(array('page'=>$pageno, 'entity_type'=>$li['entity_type']))."${tagfilter_str}'>";
+                       if ($li['entity_type'] == $entity_type)
+                               echo '<strong>';
+                       echo "${li['name']}</a>";
+                       if ($li['entity_type'] == $entity_type)
+                               echo '</strong>';
+                       echo " (${li['count']})";
+                       if ($li['entity_type'] == $entity_type)
+                               echo ' &larr;';
+                       echo "</li>";
+               }
+               echo '</ul></div>';
+       }
+       finishPortlet();
+
+       echo '</td><td class=pcleft>';
+
+       $files = getFileList ($entity_type, $tagfilter, getTFMode());
+       startPortlet ('Files (' . count ($files) . ')');
+       if ($files === NULL)
+       {
+               showError ('getFileList() failed', __FUNCTION__);
+               return;
+       }
+       echo '<br><br><table cellpadding=5 cellspacing=0 align=center class=cooltable>';
+       echo '<tr><th>Name</th><th>Comment</th><th>Size</th><th>Created</th><th>Linked to</th><th>Actions</th></tr>';
+       $order = 'odd';
+       foreach ($files as $file)
+       {
+               printf("<tr class=row_%s valign=top>", $order);
+               echo "<td class='tdleft'><a href='".makeHref(array('page'=>'file', 'file_id'=>$file['id']))."'><strong>${file['name']}</strong></a>";
+               $tags = loadEntityTags ('file', $file['id']);
+               if (count ($tags))
+                       echo '<br><small>' . serializeTags ($tags, makeHref(array('page'=>$pageno, 'tab'=>'default', 'entity_type'=>$entity_type))."&") . '</small>';
+               printf("</td><td class='tdleft'>%s</td>", $file['comment']);
+               printf("<td class='tdleft'>%s</td>", formatFileSize($file['size']));
+               echo "<td class='tdleft'>";
+               $links = getFileLinks($file['id']);
+               if (count ($links))
+                       printf("<small>%s</small>", serializeFileLinks($links));
+               echo '</td>';
+               echo "<td><a href='${root}download.php?file_id=${file['id']}'>";
+               printImageHREF ('download', 'Download file');
+               echo '</a> ';
+               echo "<a href='".makeHrefProcess(array('op'=>'deleteFile', 'file_id'=>$file['id'], 'entity_type'=>$entity_type))."'>";
+               printImageHREF ('delete', 'Delete file');
+               echo '</a></td>';
+               echo "</tr>\n";
+               $order = $nextorder[$order];
+       }
+       echo '</table>';
+       finishPortlet();
+
+       echo "</td><td class=pcright width='25%'>";
+
+       renderTagFilterPortlet ($tagfilter, 'file', 'entity_type', $entity_type);
+       echo "</td></tr></table>\n";
+}
+
+function renderFilesPortlet ($entity_type = NULL, $entity_id = 0)
+{
+       global $root;
+       if ($entity_type == NULL || $entity_id <= 0)
+       {
+               showError ('Invalid entity info', __FUNCTION__);
+               return;
+       }
+
+       $files = getFilesOfEntity ($entity_type, $entity_id);
+       if (count ($files))
+       {
+               startPortlet ('files (' . count ($files) . ')');
+               echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
+               echo "<tr><th>Name</th><th>Size</th><th>Comment</th><th>Actions</th></tr>\n";
+               foreach ($files as $file_id => $file)
+               {
+                       echo '<td class=tdleft>';
+                       renderFileCell ($file);
+                       printf("</td><td class=tdleft>%s</td>", formatFileSize($file['size']));
+                       echo "<td class=tdleft>${file['comment']}</td>";
+                       echo "<td><a href='${root}download.php?file_id=${file_id}'>";
+                       printImageHREF ('download', 'Download file');
+                       echo '</a></td></tr>';
+                       switch ($file['type'])
+                       {
+                               // "These types will be automatically detected if your build of PHP supports them: JPEG, PNG, GIF, WBMP, and GD2."
+                               case 'image/jpeg':
+                               case 'image/png':
+                               case 'image/gif':
+                               case 'image/vnd.wap.wbmp':
+                                       $file = getFile ($file_id);
+                                       $image = imagecreatefromstring ($file['contents']);
+                                       $width = imagesx ($image);
+                                       $height = imagesy ($image);
+                                       if ($width < getConfigVar ('PREVIEW_IMAGE_MAXPXS') and $height < getConfigVar ('PREVIEW_IMAGE_MAXPXS'))
+                                               $resampled = FALSE;
+                                       else
+                                       {
+                                               $ratio = getConfigVar ('PREVIEW_IMAGE_MAXPXS') / max ($width, $height);
+                                               $width = $width * $ratio;
+                                               $height = $height * $ratio;
+                                               $resampled = TRUE;
+                                       }
+                                       echo '<tr><td colspan=4>' . ($resampled ? "<a href='${root}render_image.php?img=view&file_id=${file_id}'>" : '');
+                                       echo "<img width=${width} height=${height} src='${root}render_image.php?img=preview&file_id=${file_id}'>";
+                                       echo ($resampled ? '</a>' : '') . '</td></tr>';
+                                       break;
+                               case 'text/plain':
+                                       if ($file['size'] < getConfigVar ('PREVIEW_TEXT_MAXCHARS'))
+                                       {
+                                               $file = getFile($file_id);
+                                               echo '<tr><td colspan=4><textarea readonly rows=' . getConfigVar ('PREVIEW_TEXT_ROWS');
+                                               echo ' cols=' . getConfigVar ('PREVIEW_TEXT_COLS') . '>';
+                                               echo $file['contents'];
+                                               echo '</textarea></td></tr>';
+                                       }
+                                       break;
+                               default:
+                                       break;
+                       }
+               }
+               echo "</table><br>\n";
+               finishPortlet();
+       }
+}
+
+function renderFilesForEntity ($entity_id = 0)
+{
+       global $root, $page, $pageno, $tabno, $etype_by_pageno;
+       if ($entity_id <= 0)
+       {
+               showError ('Invalid entity info', __FUNCTION__);
+               return;
+       }
+
+       showMessageOrError();
+       // Now derive entity_type and bypass_name from pageno.
+       $entity_type = $etype_by_pageno[$pageno];
+       $id_name = $page[$pageno]['bypass'];
+       
+       startPortlet ('Upload new');
+       echo "<table border=0 cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
+       echo "<tr><th>File</th><th>Comment</th><th></th></tr>\n";
+       printOpFormIntro ('addFile', array ('entity_type' => $entity_type, 'entity_id' => $entity_id, 'MAX_FILE_SIZE' => convertToBytes(get_cfg_var('upload_max_filesize'))), TRUE);
+       echo "<tr>";
+       echo "<td class=tdleft><input type='file' size='10' name='file' tabindex=100></td>\n";
+       echo "<td class=tdleft><input type='text' size='15' name='comment' tabindex=101></td><td>\n";
+       printImageHREF ('CREATE', 'Upload file', TRUE, 102);
+       echo "</td></tr></form>";
+       echo "</table><br>\n";
+       finishPortlet();
+
+       $files = getAllUnlinkedFiles ($entity_type, $entity_id);
+       if (count ($files))
+       {
+               startPortlet ('Use existing');
+               printOpFormIntro ('linkFile');
+               echo "<table border=0 cellspacing=0 cellpadding='5' align='center'>\n";
+               echo '<tr><td class=tdleft>';
+               printSelect ($files, 'file_id');
+               echo '</td><td class=tdleft>';
+               printImageHREF ('LINK', 'Link file', TRUE);
+               echo '</td></tr></table>';
+               echo "</form>\n";
+               finishPortlet();
+       }
+
+       $filelist = getFilesOfEntity ($entity_type, $entity_id);
+       if (count ($filelist))
+       {
+               startPortlet ('Manage linked');
+               echo "<table border=0 cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
+               echo "<tr><th>&nbsp;</th><th>Name</th><th>Comment</th><th>Size</th><th>Actions</th></tr>\n";
+               foreach ($filelist as $file_id => $file)
+               {
+                       echo "<tr valign=top><td><a href='".makeHrefProcess(array('op'=>'deleteFile', 'file_id'=>$file_id, $id_name=>$entity_id, 'name'=>$file['name']))."'>";
+                       printImageHREF ('DESTROY', 'Unlink and delete file');
+                       echo '</a></td><td class=tdleft>';
+                       //printf("<a href='%/?page=file&file_id=%s'><strong>%s</strong></a>", $root, $file_id, $file['name']);
+                       renderFileCell ($file);;
+                       echo "<td class=tdleft>${file['comment']}</td>";
+                       printf("<td class=tdleft>%s</td>", formatFileSize($file['size']));
+                       echo "<td><a href='${root}download.php?file_id=${file_id}'>";
+                       printImageHREF ('DOWNLOAD', 'Download file');
+                       echo '</a> ';
+                       echo "<a href='".makeHrefProcess(array('op'=>'unlinkFile', 'link_id'=>$file['link_id'], $id_name=>$entity_id, 'name'=>$file['name']))."'>";
+                       printImageHREF ('CLEAR', 'Unlink file');
+                       echo '</a> ';
+                       printImageHREF ('SAVE', 'Save changes', TRUE);
+                       echo "</td></form></tr>\n";
+               }
+               echo "</table><br>\n";
+               finishPortlet();
+       }
+}
+
 // Print common operation form prologue, include bypass argument, if
 // appropriate, and some extra hidden inputs, if requested.
-function printOpFormIntro ($opname, $extra = array())
+// Use special encoding for upload forms
+function printOpFormIntro ($opname, $extra = array(), $upload = FALSE)
 {
        global $root, $pageno, $tabno, $page;
-       echo "<form method=post action='${root}process.php?page=${pageno}&tab=${tabno}&op=${opname}'>\n";
+
+       echo "<form method=post name=${opname} action='${root}process.php?page=${pageno}&tab=${tabno}&op=${opname}'";
+       if ($upload)
+               echo " enctype='multipart/form-data'";
+       echo ">\n";
        if (isset ($page[$pageno]['bypass']) and isset ($_REQUEST[$page[$pageno]['bypass']]))
                $extra[$page[$pageno]['bypass']] = $_REQUEST[$page[$pageno]['bypass']];
        foreach ($extra as $inputname => $inputvalue)
                echo "<input type=hidden name=${inputname} value=${inputvalue}>\n";
 }
 
-// This is a two-way formating function:
+// This is a dual-purpose formating function:
 // 1. Replace empty strings with nbsp.
 // 2. Cut strings, which are too long, append "cut here" indicator and provide a mouse hint.
 function niftyString ($string, $maxlen = 30)
@@ -5225,16 +5807,14 @@ function niftyString ($string, $maxlen = 30)
                return '&nbsp;';
        if (mb_strlen ($string) > $maxlen)
                return "<span title='" . htmlspecialchars ($string, ENT_QUOTES, 'UTF-8') . "'>" .
-                       mb_substr ($string, 0, $maxlen - 1) . $cutind . '</span>';
+                       str_replace (' ', '&nbsp;', str_replace ("\t", ' ', mb_substr ($string, 0, $maxlen - 1))) . $cutind . '</span>';
        return $string;
 }
 
 // Iterate over what findRouters() returned and output some text suitable for a TD element.
-// Providing a quasi-static (external) tag cache is advised for calling functions.
-function printRoutersTD ($rlist, &$tagcache = array())
+function printRoutersTD ($rlist)
 {
        global $root;
-       $delim = '';
        $rtrclass = 'tdleft';
        foreach ($rlist as $rtr)
        {
@@ -5247,40 +5827,111 @@ function printRoutersTD ($rlist, &$tagcache = array())
        }
        echo "<td class='${rtrclass}'>";
        foreach ($rlist as $rtr)
-       {
-               echo $delim . $rtr['addr'] . ' ';
-               echo "<a href='${root}?page=object&object_id=${rtr['id']}&hl_ipv4_addr=${rtr['addr']}'>";
-               if (!empty ($rtr['iface']))
-                       echo $rtr['iface'] . '@';
-               echo $rtr['dname'] . '</a>';
-               if (!isset ($tagcache[$rtr['id']]))
-                       $tagcache[$rtr['id']] = loadRackObjectTags ($rtr['id']);
-               if (count ($tagcache[$rtr['id']]))
-               {
-                       echo '<br><small>';
-                       echo serializeTags ($tagcache[$rtr['id']], "${root}?page=objgroup&group_id=0&tab=default&");
-                       echo '</small>';
-               }
-               $delim = '<br><hr>';
-       }
+               renderRouterCell ($rtr['addr'], $rtr['iface'], $rtr['id'], $rtr['dname']);
        echo '</td>';
 }
 
 // Same as for routers, but produce two TD cells to lay the content out better.
-function printIPv4NetInfoTDs ($netinfo, $tdclass = 'tdleft', $indent = 0)
+function printIPv4NetInfoTDs ($netinfo, $tdclass = 'tdleft', $indent = 0, $symbol = 'spacer', $symbolurl = '')
 {
        global $root;
-       $tags = loadIPv4PrefixTags ($netinfo['id']);
-       echo "<td class='${tdclass}'>";
-       for ($i = 0; $i < $indent - 1; $i++)
-               printImageHREF ('spacer');
-       if ($indent)
-               printImageHREF ('verge');
-       echo "<a href='${root}?page=iprange&id=${netinfo['id']}'>${netinfo['ip']}/${netinfo['mask']}</a></td>";
-       echo "<td class='${tdclass}'>" . niftyString ($netinfo['name']);
-       if (count ($tags))
-               echo '<br><small>' . serializeTags ($tags, "${root}?page=ipv4space&tab=default&") . '</small>';
+       $tags = isset ($netinfo['id']) ? loadIPv4PrefixTags ($netinfo['id']) : array();
+       if ($symbol == 'spacer')
+       {
+               $indent++;
+               $symbol = '';
+       }
+       echo "<td class='${tdclass}' style='padding-left: " . ($indent * 16) . "px;'>";
+       if (!empty ($symbol))
+       {
+               if (!empty ($symbolurl))
+                       echo "<a href='${symbolurl}'>";
+               printImageHREF ($symbol, $symbolurl);
+               if (!empty ($symbolurl))
+                       echo '</a>';
+       }
+       if (isset ($netinfo['id']))
+               echo "<a href='${root}?page=ipv4net&id=${netinfo['id']}'>";
+       echo "${netinfo['ip']}/${netinfo['mask']}";
+       if (isset ($netinfo['id']))
+               echo '</a>';
+       echo "</td><td class='${tdclass}'>";
+       if (!isset ($netinfo['id']))
+               printImageHREF ('dragons', 'Here be dragons.');
+       else
+       {
+               echo niftyString ($netinfo['name']);
+               if (count ($tags))
+                       echo '<br><small>' . serializeTags ($tags, "${root}?page=ipv4space&tab=default&") . '</small>';
+       }
+       echo "</td>";
+}
+
+function renderLBCell ($object_id)
+{
+       global $root;
+       $oi = getObjectInfo ($object_id);
+       echo "<table class=slbcell><tr><td>";
+       echo "<a href='${root}?page=object&object_id=${object_id}'>${oi['dname']}</a>";
+       echo "</td></tr><tr><td>";
+       printImageHREF ('LB');
+       echo "</td></tr><tr><td><small>";
+       echo serializeTags (loadRackObjectTags ($object_id));
+       echo "</small></td></tr></table>";
+}
+
+function renderRSPoolCell ($pool_id, $pool_name)
+{
+       global $root;
+       echo "<table class=slbcell><tr><td>";
+       echo "<a href='${root}?page=ipv4rspool&pool_id=${pool_id}'>";
+       echo empty ($pool_name) ? "ANONYMOUS pool [${pool_id}]" : $pool_name;
+       echo "</a></td></tr><tr><td>";
+       printImageHREF ('RS pool');
+       echo "</td></tr><tr><td><small>";
+       echo serializeTags (loadIPv4RSPoolTags ($pool_id));
+       echo "</small></td></tr></table>";
+}
+
+function renderVSCell ($vs_id)
+{
+       global $root;
+       $oinfo = getVServiceInfo ($vs_id);
+       echo "<table class='slbcell vscell'><tr><td rowspan=3>";
+       printImageHREF ('VS');
+       echo "</td><td>";
+       echo "<a href='${root}?page=ipv4vs&vs_id=${vs_id}'>";
+       echo buildVServiceName ($oinfo);
+       echo "</a></td></tr><tr><td>";
+       echo $oinfo['name'];
+       echo '</td></tr><tr><td>';
+       $tags = loadIPv4VSTags ($vs_id);
+       echo count ($tags) ? ("<small>" . serializeTags ($tags) . "</small>") : '&nbsp;';
+       echo "</td></tr></table>";
+}
+
+function renderRouterCell ($dottedquad, $ifname, $object_id, $object_dname)
+{
+       global $root;
+       echo "<table class=slbcell><tr><td rowspan=3>${dottedquad}";
+       if (!empty ($ifname))
+               echo '@' . $ifname;
        echo "</td>";
+       echo "<td><a href='${root}?page=object&object_id=${object_id}&hl_ipv4_addr=${dottedquad}'><strong>${object_dname}</strong></a></td>";
+       echo "</td></tr><tr><td>";
+       printImageHREF ('router');
+       echo "</td></tr><tr><td><small>";
+       echo serializeTags (loadRackObjectTags ($object_id));
+       echo "</small></td></tr></table>";
+}
+
+function renderFileCell ($fileinfo)
+{
+       global $root;
+       printf("<a href='${root}?page=file&file_id=%s'><strong>%s</strong></a>", $fileinfo['file_id'], niftyString ($fileinfo['name']));
+       echo '<br><small>';
+       echo serializeTags (loadFileTags ($fileinfo['file_id']));
+       echo "</small>";
 }
 
 ?>