r2411 added deleteRow functionality and fixed a nasty bug in makeHrefProcess()
[racktables] / inc / interface.php
index 7aec59bd4624f8a712d104947a8305dd7fa86689..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,15 +63,33 @@ $image['config']['height'] = 200;
 $image['reports']['path'] = 'pix/report.png';
 $image['reports']['width'] = 218;
 $image['reports']['height'] = 200;
+$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['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;
@@ -77,8 +99,6 @@ $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;
@@ -124,6 +144,27 @@ $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";
@@ -610,7 +677,6 @@ function printSelect ($rowList, $select_name, $selected_id = NULL, $tabindex = N
 // 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&tab=default&");
+       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&tab=default&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']))
@@ -846,7 +900,7 @@ function renderRackObject ($object_id = 0)
                        }
                        echo "<tr class='${class}' valign=top><td class=tdleft>${alloc['osif']}</td><td class='${secondclass}'>";
                        if (NULL !== $netid)
-                               echo "<a href='${root}?page=ipaddress&ip=" . $dottedquad . "&hl_object_id=${object_id}'>${dottedquad}</a>";
+                               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')
@@ -862,21 +916,12 @@ function renderRackObject ($object_id = 0)
                                else
                                {
                                        printIPv4NetInfoTDs ($netinfo, $secondclass);
-                                       echo "<td class='${secondclass}'>";
-                                       // FIXME: These calls are really heavy, replace them with a more appropriate dedicated function.
-                                       $newline = '';
+                                       // filter out self-allocation
+                                       $other_routers = array();
                                        foreach (findRouters ($netinfo['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>';
-                                       }
-                                       echo '</td>';
+                                               if ($router['id'] != $object_id)
+                                                       $other_routers[] = $router;
+                                       printRoutersTD ($other_routers);
                                }
                        }
                        // peers
@@ -891,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>';
@@ -926,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>";
@@ -947,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>";
@@ -966,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];
                }
@@ -1007,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}'>";
@@ -1048,7 +1090,7 @@ function renderPortsForObject ($object_id = 0)
                printImageHREF ('add', 'add a port', TRUE, 104);
                echo "</td></tr></form>";
        }
-       global $root, $pageno, $tabno;
+       global $pageno, $tabno;
        if ($object_id <= 0)
        {
                showError ('Invalid object_id', __FUNCTION__);
@@ -1066,20 +1108,36 @@ function renderPortsForObject ($object_id = 0)
        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']}'>";
+                       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>";
                }
@@ -1087,7 +1145,12 @@ function renderPortsForObject ($object_id = 0)
                {
                        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}'>";
+                       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>";
                }
@@ -1095,9 +1158,7 @@ function renderPortsForObject ($object_id = 0)
                {
                        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";
@@ -1144,7 +1205,7 @@ function renderIPv4ForObject ($object_id = 0)
                printImageHREF ('add', 'allocate', TRUE, 103);
                echo "</td></tr></form>";
        }
-       global $root, $pageno, $tabno, $aat;
+       global $pageno, $tabno, $aat;
        if ($object_id <= 0)
        {
                showError ('Invalid object_id', __FUNCTION__);
@@ -1171,12 +1232,12 @@ function renderIPv4ForObject ($object_id = 0)
                        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><td class=tdleft>";
                if (NULL !== $netid)
-                       echo "<a href='${root}?page=ipaddress&ip=${dottedquad}'>${dottedquad}</a>";
+                       echo "<a href='".makeHref(array('page'=>'ipaddress', 'ip'=>$dottedquad))."'>${dottedquad}</a>";
                else
                        echo $dottedquad;
                if (getConfigVar ('EXT_IPV4_VIEW') != 'yes')
@@ -1199,11 +1260,11 @@ function renderIPv4ForObject ($object_id = 0)
                                {
                                        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 $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, "${root}?page=objects&tab=default&") . '</small>';
+                                               echo '<br><small>' . serializeTags ($routertags, makeHref(array('page'=>'objects', 'tab'=>'default'))."&") . '</small>';
                                        $newline = '<br>';
                                }
                                echo '</td>';
@@ -1222,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>';
@@ -1298,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."),
@@ -1326,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!'),
@@ -1405,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...'),
@@ -1412,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)
@@ -1528,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>";
 
@@ -1701,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";
@@ -1726,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";
@@ -1742,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');
@@ -1758,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();
@@ -1776,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();
@@ -1797,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>";
@@ -1822,7 +1850,7 @@ function renderObjectGroup ()
                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>Row/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)
        {
@@ -1831,14 +1859,13 @@ function renderObjectGroup ()
                else
                        $secondclass = 'tdleft';
                $tags = loadRackObjectTags ($obj['id']);
-               echo "<tr class=row_${order} valign=top><td class='${secondclass}'><a href='${root}?page=object&object_id=${obj['id']}'><strong>${obj['dname']}</strong></a>";
+               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, "${root}?page=${pageno}&tab=default&group_id=${group_id}&") . '</small>';
+                       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=row&row_id=${obj['row_id']}'>${obj['Row_name']}</a>/<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>';
@@ -1889,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:
@@ -1930,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.
@@ -1987,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";
@@ -2005,8 +2032,16 @@ function renderIPv4SpaceRecords ($tree, &$tagcache, $baseurl, $target = 0, $leve
        foreach ($tree as $item)
        {
                $total = $item['addrt'];
-               loadIPv4AddrList ($item); // necessary to compute router list and address counter
-               $used = $item['addrc'];
+               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')
@@ -2020,8 +2055,14 @@ function renderIPv4SpaceRecords ($tree, &$tagcache, $baseurl, $target = 0, $leve
                        echo "<td class=tdcenter>";
                        if ($target == $item['id'])
                                echo "<a name=netid${target}></a>";
-                       renderProgressBar ($total ? $used/$total : 0);
-                       echo "<br><small>${used}/${total}</small></td>";
+                       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>";
@@ -2033,33 +2074,51 @@ function renderIPv4SpaceRecords ($tree, &$tagcache, $baseurl, $target = 0, $leve
                        echo "<tr valign=top>";
                        printIPv4NetInfoTDs ($item, 'tdleft sparenetwork', $level, $item['symbol']);
                        echo "<td class=tdcenter>";
-                       renderProgressBar ($used/$total, 'sparenetwork');
-                       echo "<br><small>${used}/${total}</small></td>";
-                       echo "<td>&nbsp;</td></tr>";
+                       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, $tabno;
+       global $pageno, $tabno;
        $tagfilter = getTagFilter();
        $netlist = getIPv4NetworkList ($tagfilter, getTFMode());
+
+
        $netcount = count ($netlist);
-       $tree = prepareIPv4Tree ($netlist, isset ($_REQUEST['eid']) ? $_REQUEST['eid'] : 0);
-       unset ($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>";
        startPortlet ("networks (${netcount})");
-       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>";
+       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";
        $tagcache = array();
-       $baseurl = "${root}?page=${pageno}&tab=${tabno}" . getTagFilterStr ($tagfilter);
-       renderIPv4SpaceRecords ($tree, $tagcache, $baseurl, isset ($_REQUEST['eid']) ? $_REQUEST['eid'] : 0);
+       $baseurl = makeHref(array('page'=>$pageno, 'tab'=>$tabno)) . getTagFilterStr ($tagfilter);
+       renderIPv4SpaceRecords ($tree, $tagcache, $baseurl, $eid);
        echo "</table>\n";
        finishPortlet();
        echo '</td><td class=pcright>';
@@ -2069,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();
 
@@ -2098,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)
                        {
@@ -2118,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>';
@@ -2134,7 +2194,7 @@ function renderIPv4SLB ()
 
 function renderIPv4SpaceEditor ()
 {
-       global $root, $pageno, $tabno;
+       global $pageno, $tabno;
        showMessageOrError();
 
        // IPv4 validator
@@ -2153,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();
@@ -2180,8 +2238,8 @@ function renderIPv4SpaceEditor ()
                echo "<tr><th>&nbsp;</th><th>prefix</th><th>name</th><th>&nbsp;</th></tr>";
                foreach ($addrspaceList as $netinfo)
                {
-                       echo "<form method=post action='${root}process.php?page=${pageno}&tab=${tabno}&op=updIPv4Prefix&id=${netinfo['id']}'>";
-                       echo "<tr valign=top><td><a href='${root}process.php?op=delIPv4Prefix&page=${pageno}&tab=${tabno}&id=${netinfo['id']}'>";
+                       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']}'>";
@@ -2196,7 +2254,7 @@ function renderIPv4SpaceEditor ()
 
 function renderIPv4Network ($id)
 {
-       global $root, $pageno, $tabno, $aac2;
+       global $pageno, $tabno, $aac2;
        $netmaskbylen = array
        (
                32 => '255.255.255.255',
@@ -2290,27 +2348,27 @@ function renderIPv4Network ($id)
 
        if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
        {
-               $ipv4netlist = getIPv4NetworkList (array(), getTFMode());
-               $ipv4tree = treeFromList ($ipv4netlist);
-               $backtrace = array_reverse (traceEntity ($ipv4tree, $id));
-               $arrows = count ($backtrace) - 1;
-               foreach ($backtrace as $ancestorid)
+               // 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);
-                       if ($ancestorid == $id)
-                       {
-                               echo "<tr><th width='50%' class=tdright>&rarr;</th>";
-                               echo "<td class=tdleft>${ainfo['ip']}/${ainfo['mask']}</td></tr>";
-                       }
-                       else
-                       {
-                               echo "<tr><th width='50%' class=tdright>";
-                               for ($i = 0; $i < $arrows; $i++)
-                                       echo '&uarr;';
-                               $arrows--;
-                               echo "</th><td class=tdleft><a href='${root}?page=${pageno}&tab=${tabno}&id=${ainfo['id']}'>${ainfo['ip']}/${ainfo['mask']}</a></td></tr>";
-                       }
+                       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);
        }
@@ -2335,9 +2393,11 @@ function renderIPv4Network ($id)
                echo "</tr>\n";
        }
 
-       printTagTRs ("${root}?page=ipv4space&tab=default&");
+       printTagTRs (makeHref(array('page'=>'ipv4space', 'tab'=>'default'))."&");
        echo "</table><br>\n";
        finishPortlet();
+
+       renderFilesPortlet ('ipv4net', $id);
        echo "</td>\n";
 
        echo "<td class=pcright>";
@@ -2361,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>";
 
@@ -2376,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 = '';
@@ -2395,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 = '; ';
@@ -2410,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 = '; ';
                }
@@ -2424,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 = '; ';
                }
@@ -2452,7 +2511,7 @@ function renderIPv4NetworkProperties ($id)
 
 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";
@@ -2489,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";
                }
@@ -2507,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";
                }
@@ -2527,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>";
@@ -2606,7 +2664,7 @@ function renderIPv4AddressAllocations ($dottedquad)
                printImageHREF ('add', 'allocate', TRUE, 103);
                echo "</td></form></tr>";
        }
-       global $pageno, $tabno, $root, $aat;
+       global $pageno, $tabno, $aat;
 
        $address = getIPv4Address ($dottedquad);
 
@@ -2625,10 +2683,10 @@ function renderIPv4AddressAllocations ($dottedquad)
                {
                        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']}'>";
+                       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='${root}?page=object&object_id=${bond['object_id']}&hl_ipv4_addr=${dottedquad}'>${bond['object_name']}</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>";
@@ -2643,6 +2701,7 @@ function renderIPv4AddressAllocations ($dottedquad)
 
 function renderNATv4ForObject ($object_id = 0)
 {
+       global $root;
        function printNewItemTR ($alloclist)
        {
                global $root;
@@ -2670,7 +2729,7 @@ function renderNATv4ForObject ($object_id = 0)
                printImageHREF ('add', 'Add new NAT rule', TRUE, 6);
                echo "</td></tr></form>";
        }
-       global $pageno, $tabno, $root;
+       global $pageno, $tabno;
        
        $info = getObjectInfo ($object_id);
        $forwards = getNATv4ForObject ($object_id);
@@ -2694,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
@@ -2739,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>";
        }
 
@@ -2899,12 +2977,10 @@ function renderAddMultipleObjectsForm ()
 
 function printGreeting ()
 {
-       global $root, $remote_username, $accounts, $root;
-       $account = $accounts[$remote_username];
-       $person = "<a href='${root}?page=myaccount&tab=default'>";
-       $person .= empty ($account['user_realname']) ? $account['user_name'] : $account['user_realname'];
-       $person .= '</a>';
-       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 ()
@@ -2925,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)
@@ -2944,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))
                {
@@ -2954,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.
        {
@@ -2999,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>";
@@ -3015,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;
@@ -3035,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>";
@@ -3043,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;
        }
@@ -3057,7 +3156,7 @@ 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)
                                        {
                                                $tags = loadRackObjectTags ($obj['id']);
@@ -3075,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>";
@@ -3090,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&tab=default&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>";
@@ -3110,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];
@@ -3121,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']}'>";
@@ -3135,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']}'>";
@@ -3146,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;
                        }
        }
 }
@@ -3215,7 +3328,7 @@ function renderUserListEditor ()
        function printNewItemTR ()
        {
                printOpFormIntro ('createUser');
-               echo "<tr><td>&nbsp;</td><td><input type=text size=16 name=username tabindex=100></td>\n";
+               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);
@@ -3225,28 +3338,13 @@ function renderUserListEditor ()
        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);
@@ -3270,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();
@@ -3337,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.
@@ -3353,77 +3452,107 @@ 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
@@ -3439,7 +3568,7 @@ function renderChaptersEditor ()
                printImageHREF ('add', 'Add new', TRUE, 101);
                echo '</td></tr></form>';
        }
-       global $root, $pageno, $tabno;
+       global $pageno, $tabno;
        showMessageOrError();
        $dict = getDict();
        echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
@@ -3459,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>";
                }
@@ -3525,7 +3654,7 @@ function renderEditAttributesForm ()
                printImageHREF ('add', 'Create attribute', TRUE, 102);
                echo '</td></tr></form>';
        }
-       global $root, $pageno, $tabno;
+       global $pageno, $tabno;
        $attrMap = getAttrMap();
        showMessageOrError();
        startPortlet ('Optional attributes');
@@ -3537,7 +3666,7 @@ function renderEditAttributesForm ()
        {
                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>";
@@ -3580,7 +3709,7 @@ function renderEditAttrMapForm ()
                echo '</td></tr>';
                echo '</form>';
        }
-       global $root, $pageno, $tabno;
+       global $pageno, $tabno;
        $attrMap = getAttrMap();
        showMessageOrError();
        startPortlet ('Attribute map');
@@ -3596,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>';
@@ -3997,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();
@@ -4037,7 +4144,7 @@ function renderLVSConfig ($object_id = 0)
 
 function renderVirtualService ($vsid)
 {
-       global $root, $nextorder;
+       global $nextorder;
        if ($vsid <= 0)
        {
                showError ('Invalid argument', __FUNCTION__);
@@ -4055,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&tab=default&");
+       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 ();
@@ -4079,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
@@ -4102,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>';
                }
@@ -4133,7 +4236,7 @@ function renderProgressBar ($percentage = 0, $theme = '')
 
 function renderRSPoolServerForm ($pool_id = 0)
 {
-       global $root, $pageno, $tabno, $nextorder;
+       global $pageno, $tabno, $nextorder;
        if ($pool_id <= 0)
        {
                showError ('Invalid pool_id', __FUNCTION__);
@@ -4151,8 +4254,7 @@ 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>";
@@ -4212,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);
@@ -4229,17 +4331,14 @@ 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);
@@ -4270,7 +4369,7 @@ function renderRSPoolLBForm ($pool_id = 0)
 
 function renderVServiceLBForm ($vs_id = 0)
 {
-       global $root, $pageno, $tabno, $nextorder;
+       global $pageno, $tabno, $nextorder;
        showMessageOrError();
        $vsinfo = getVServiceInfo ($vs_id);
 
@@ -4283,14 +4382,15 @@ 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);
                                echo "</td></tr></form>\n";
@@ -4323,7 +4423,7 @@ function renderVServiceLBForm ($vs_id = 0)
 
 function renderRSPool ($pool_id = 0)
 {
-       global $root;
+       global $nextorder;
        if ($pool_id <= 0)
        {
                showError ('Invalid pool_id', __FUNCTION__);
@@ -4341,22 +4441,22 @@ 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&tab=default&");
+       printTagTRs (makeHref(array('page'=>'ipv4rsplist', 'tab'=>'default'))."&");
        if (!empty ($poolInfo['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>${poolInfo['vsconfig']}</pre></td></tr>\n";
+               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 class=tdleft>&nbsp;</td></tr>\n";
-               echo "<tr><td class=tdleft colspan=2><pre>${poolInfo['rsconfig']}</pre></td></tr>\n";
+               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();
@@ -4364,16 +4464,17 @@ function renderRSPool ($pool_id = 0)
        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();
@@ -4390,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();
@@ -4401,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";
@@ -4413,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];
        }
@@ -4435,7 +4530,7 @@ function renderVSList ()
 
 function renderVSListEditForm ()
 {
-       global $root, $pageno, $tabno, $nextorder;
+       global $pageno, $tabno, $nextorder;
        showMessageOrError();
        $protocols = array ('TCP' => 'TCP', 'UDP' => 'UDP');
 
@@ -4478,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>';
                }
@@ -4500,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)
@@ -4518,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>";
@@ -4539,7 +4634,7 @@ function renderRSPoolList ()
 
 function editRSPools ()
 {
-       global $root, $pageno, $tabno, $nextorder;
+       global $pageno, $tabno, $nextorder;
        showMessageOrError();
 
        startPortlet ('Add new');
@@ -4571,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>';
                }
@@ -4589,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";
@@ -4603,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";
@@ -4620,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();
@@ -4629,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];
@@ -4675,7 +4770,7 @@ function renderLivePTR ($id = 0)
                $page = $_REQUEST['pg'];
        else
                $page=0;
-       global $root, $pageno, $tabno;
+       global $pageno, $tabno;
        $maxperpage = getConfigVar ('IPV4_ADDRS_PER_PAGE');
        $range = getIPv4NetworkInfo ($id);
        loadIPv4AddrList ($range);
@@ -4701,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
@@ -4748,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}>";
@@ -4817,9 +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 style='padding-left: " . ($level * 16) . "px;'>";
-       echo "<a href='${root}?page=objgroup&group_id=0&tagfilter[]=${taginfo['id']}'>";
+       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] . ')';
@@ -4830,7 +4924,7 @@ function renderTagRowForCloud ($taginfo, $realm, $level = 0)
 
 function renderTagRowForEditor ($taginfo, $level = 0)
 {
-       global $root, $pageno, $tabno, $taglist;
+       global $pageno, $tabno, $taglist;
        if (!count ($taginfo['kids']))
                $level++; // Idem
        echo "<tr><td align=left style='padding-left: " . ($level * 16) . "px;'>";
@@ -4840,11 +4934,11 @@ function renderTagRowForEditor ($taginfo, $level = 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>";
@@ -4897,13 +4991,13 @@ function renderTagTreeEditor ()
                global $taglist;
                printOpFormIntro ('createTag');
                echo "<tr><td class=tdleft>";
-               printImageHREF ('add', 'Create tag', TRUE);
+               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 ('add', 'Create tag', TRUE, 102);
+               printImageHREF ('create', 'Create tag', TRUE, 102);
                echo "</td></tr></form>\n";
        }
        global $taglist, $tagtree;
@@ -4951,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.
@@ -4973,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)
-{
-       renderEntityTagChainEditor ('ip4rspool', 'pool_id', $id);
-}
-
-function renderUserTags ($id)
+function renderEntityTags ($entity_id = 0)
 {
-       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>';
@@ -5035,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";
        }
 }
@@ -5108,10 +5185,10 @@ function renderTagSelect ()
                echo "No tags defined";
                return;
        }
-       echo '<select name=taglist[] multiple size=' . getConfigVar ('MAXSELSIZE') . '>';
+       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)
@@ -5135,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)
@@ -5168,15 +5245,14 @@ 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);
                        echo "</td></tr></form>\n";
@@ -5224,9 +5300,9 @@ function renderEditVService ($vsid)
 
 function dump ($var)
 {
-       echo '<pre>';
+       echo '<div align=left><pre>';
        print_r ($var);
-       echo '</pre>';
+       echo '</pre></div>';
 }
 
 function renderRackCodeViewer ()
@@ -5262,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 ()
@@ -5317,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>";
 }
@@ -5347,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)
@@ -5374,11 +5812,9 @@ function niftyString ($string, $maxlen = 30)
 }
 
 // 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)
        {
@@ -5391,22 +5827,7 @@ 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>';
 }
 
@@ -5430,7 +5851,7 @@ function printIPv4NetInfoTDs ($netinfo, $tdclass = 'tdleft', $indent = 0, $symbo
                        echo '</a>';
        }
        if (isset ($netinfo['id']))
-               echo "<a href='${root}?page=iprange&id=${netinfo['id']}'>";
+               echo "<a href='${root}?page=ipv4net&id=${netinfo['id']}'>";
        echo "${netinfo['ip']}/${netinfo['mask']}";
        if (isset ($netinfo['id']))
                echo '</a>';
@@ -5446,4 +5867,71 @@ function printIPv4NetInfoTDs ($netinfo, $tdclass = 'tdleft', $indent = 0, $symbo
        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>";
+}
+
 ?>