r2345 - don't allow editing/deleting stock records in dictionary editor
[racktables] / inc / interface.php
index 40915c8..9973f71 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,26 +63,24 @@ $image['config']['height'] = 200;
 $image['reports']['path'] = 'pix/report.png';
 $image['reports']['width'] = 218;
 $image['reports']['height'] = 200;
-$image['useup']['path'] = 'pix/tango-edit-clear.png';
-$image['useup']['width'] = 16;
-$image['useup']['height'] = 16;
+$image['download']['path'] = 'pix/download.png';
+$image['download']['width'] = 16;
+$image['download']['height'] = 16;
 $image['link']['path'] = 'pix/tango-network-wired.png';
 $image['link']['width'] = 16;
 $image['link']['height'] = 16;
-$image['unlink']['path'] = 'pix/tango-edit-clear.png';
-$image['unlink']['width'] = 16;
-$image['unlink']['height'] = 16;
 $image['add']['path'] = 'pix/tango-list-add.png';
 $image['add']['width'] = 16;
 $image['add']['height'] = 16;
+$image['ADD']['path'] = 'pix/tango-list-add-big.png';
+$image['ADD']['width'] = 32;
+$image['ADD']['height'] = 32;
 $image['delete']['path'] = 'pix/tango-list-remove.png';
 $image['delete']['width'] = 16;
 $image['delete']['height'] = 16;
 $image['nodelete']['path'] = 'pix/tango-list-remove-shadow.png';
 $image['nodelete']['width'] = 16;
 $image['nodelete']['height'] = 16;
-$image['grant'] = $image['add'];
-$image['revoke'] = $image['delete'];
 $image['inservice']['path'] = 'pix/tango-emblem-system.png';
 $image['inservice']['width'] = 16;
 $image['inservice']['height'] = 16;
@@ -90,9 +92,6 @@ $image['unblockuser'] = $image['notinservice'];
 $image['find']['path'] = 'pix/tango-system-search.png';
 $image['find']['width'] = 16;
 $image['find']['height'] = 16;
-$image['spacer']['path'] = 'pix/pixel.png';
-$image['spacer']['width'] = 16;
-$image['spacer']['height'] = 16;
 $image['next']['path'] = 'pix/tango-go-next.png';
 $image['next']['width'] = 32;
 $image['next']['height'] = 32;
@@ -120,6 +119,42 @@ $image['CREATE']['height'] = 32;
 $image['DENIED']['path'] = 'pix/tango-dialog-error-big.png';
 $image['DENIED']['width'] = 32;
 $image['DENIED']['height'] = 32;
+$image['apply']['path'] = 'pix/tango-emblem-system.png';
+$image['apply']['width'] = 16;
+$image['apply']['height'] = 16;
+$image['node-collapsed']['path'] = 'pix/node-collapsed.png';
+$image['node-collapsed']['width'] = 16;
+$image['node-collapsed']['height'] = 16;
+$image['node-expanded']['path'] = 'pix/node-expanded.png';
+$image['node-expanded']['width'] = 16;
+$image['node-expanded']['height'] = 16;
+$image['node-expanded-static']['path'] = 'pix/node-expanded-static.png';
+$image['node-expanded-static']['width'] = 16;
+$image['node-expanded-static']['height'] = 16;
+$image['dragons']['path'] = 'pix/mitsudragon.png';
+$image['dragons']['width'] = 125;
+$image['dragons']['height'] = 21;
+$image['LB']['path'] = 'pix/loadbalancer.png';
+$image['LB']['width'] = 32;
+$image['LB']['height'] = 32;
+$image['RS pool']['path'] = 'pix/serverpool.png';
+$image['RS pool']['width'] = 48;
+$image['RS pool']['height'] = 16;
+$image['VS']['path'] = 'pix/servicesign.png';
+$image['VS']['width'] = 39;
+$image['VS']['height'] = 62;
+$image['router']['path'] = 'pix/router.png';
+$image['router']['width'] = 32;
+$image['router']['height'] = 32;
+$image['LINK']['path'] = 'pix/tango-emblem-symbolic-link-big.png';
+$image['LINK']['width'] = 32;
+$image['LINK']['height'] = 32;
+$image['favorite']['path'] = 'pix/tango-emblem-favorite.png';
+$image['favorite']['width'] = 16;
+$image['favorite']['height'] = 16;
+$image['computer']['path'] = 'pix/tango-computer.png';
+$image['computer']['width'] = 16;
+$image['computer']['height'] = 16;
 
 // This may be populated later onsite, report rendering function will use it.
 // See the $systemreport for structure.
@@ -145,16 +180,18 @@ function renderIndex ()
                                                <?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 $root; ?>?page=ipv4space'>IPv4 space<br>
+                                               <?php printImageHREF ('ipv4space'); ?></a></h1>
+                                       </td>
+                                       <td>
+                                               <h1><a href='<?php echo $root; ?>?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 $root; ?>?page=config'>Configuration<br>
+                                               <?php printImageHREF ('config'); ?></a></h1>
                                        </td>
                                        <td>
                                                <h1><a href='<?php echo $root; ?>?page=reports'>Reports<br>
@@ -164,6 +201,7 @@ function renderIndex ()
                                                <h1><a href='<?php echo $root; ?>?page=ipv4slb'>IPv4 SLB<br>
                                                <?php printImageHREF ('ipv4slb'); ?></a></h1>
                                        </td>
+                                       <td>&nbsp;</td>
                                </tr>
                        </table>
                        </div>
@@ -209,6 +247,36 @@ 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></tr></form>";
+       }
+       global $root, $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></tr>\n";
+       }
+       if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
+               printNewItemTR();
+       echo "</table><br>\n";
+       finishPortlet();
+}
+
 function renderRow ($row_id = 0)
 {
        if ($row_id == 0)
@@ -315,16 +383,12 @@ function renderRack ($rack_id = 0, $hl_obj_id = 0)
                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']))
@@ -423,12 +487,13 @@ function renderEditObjectForm ($object_id)
                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";
@@ -442,70 +507,63 @@ function renderEditObjectForm ($object_id)
                echo ' checked';
        echo "></td></tr>\n";
        echo "<tr><td colspan=2><b>Comment:</b><br><textarea name=object_comment rows=10 cols=80>${object['comment']}</textarea></td></tr>";
-       echo "<tr><th class=submit colspan=2>";
-       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='${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'])
+                       {
+                               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().
@@ -541,7 +599,7 @@ function renderEditRackForm ($rack_id)
 }
 
 // This is a helper for creators and editors.
-function printSelect ($rowList, $select_name, $selected_id = 1)
+function printSelect ($rowList, $select_name, $selected_id = NULL, $tabindex = NULL)
 {
        // First collect all data for OPTGROUPs, then ouput it and dump
        // the rest of records as is.
@@ -562,7 +620,7 @@ function printSelect ($rowList, $select_name, $selected_id = 1)
                else
                        $other[$dict_key] = $dict_value;
        }
-       echo "<select name=${select_name}>";
+       echo "<select name=${select_name}" . ($tabindex ? " tabindex=${tabindex}" : '') . '>';
        if (!count ($optgroup))
        {
                foreach ($other as $dict_key => $dict_value)
@@ -618,7 +676,7 @@ function renderRackInfoPortlet ($rackData)
        echo "<tr><th width='50%' class=tdright>Objects:</th><td class=tdleft>";
        echo count (stuffInRackspace ($rackData));
        echo "</td></tr>\n";
-       printTagTRs ("${root}?page=rackspace&");
+       printTagTRs ("${root}?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>';
@@ -760,8 +818,9 @@ function renderRackObject ($object_id = 0)
                echo "<tr><td colspan=2 class=msg_error>Has problems</td></tr>\n";
        foreach (getAttrValues ($object_id, TRUE) as $record)
                if (!empty ($record['value']))
-                       echo "<tr><th width='50%' class=opt_attr_th>${record['name']}:</th><td class=tdleft>${record['a_value']}</td></tr>\n";
-       printTagTRs ("${root}?page=objgroup&group_id=${info['objtype_id']}&");
+                       echo "<tr><th width='50%' class=tdright><span class=sticker>${record['name']}</span>:</th><td class=tdleft>${record['a_value']}</td></tr>\n";
+       printTagTRs ("${root}?page=objgroup&tab=default&group_id=${info['objtype_id']}&");
+       echo "<tr><th width='50%' class=tdright>Actions:</th><td class=tdleft><a href='${root}process.php?op=deleteObject&page=objects&tab=default&object_id=${object_id}&name=${info['name']}' onclick=\"javascript:return confirm('Are you sure you want to delete the object?')\">Delete object</a></td></tr>\n";
        echo "</table><br>\n";
        finishPortlet();
 
@@ -772,6 +831,8 @@ function renderRackObject ($object_id = 0)
                finishPortlet ();
        }
 
+       renderFilesPortlet ('object', $object_id);
+
        $ports = getObjectPortsAndLinks ($object_id);
        if (count ($ports))
        {
@@ -835,42 +896,36 @@ function renderRackObject ($object_id = 0)
                        $class = $alloc['addrinfo']['class'];
                        $secondclass = ($hl_ipv4_addr == $dottedquad) ? 'tdleft port_highlight' : 'tdleft';
                        $netid = getIPv4AddressNetworkId ($dottedquad);
-                       $netinfo = getIPv4NetworkInfo ($netid);
-                       echo "<tr class='${class}' valign=top><td class=tdleft>${alloc['osif']}</td><td class='${secondclass}'>";
-                       echo "<a href='${root}?page=ipaddress&ip=" . $dottedquad . "&hl_object_id=${object_id}'>${dottedquad}</a>";
-                       if (getConfigVar ('EXT_IPV4_VIEW') != 'yes')
+                       if (NULL !== $netid)
                        {
-                               if (NULL === $netid)
-                                       $suffix = '/??';
-                               else
-                                       echo '<small>/' . $netinfo['mask'] . '</small>';
+                               $netinfo = getIPv4NetworkInfo ($netid);
+                               loadIPv4AddrList ($netinfo);
                        }
+                       echo "<tr class='${class}' valign=top><td class=tdleft>${alloc['osif']}</td><td class='${secondclass}'>";
+                       if (NULL !== $netid)
+                               echo "<a href='${root}?page=ipaddress&ip=" . $dottedquad . "&hl_object_id=${object_id}'>${dottedquad}</a>";
+                       else
+                               echo $dottedquad;
+                       if (getConfigVar ('EXT_IPV4_VIEW') != 'yes')
+                               echo '<small>/' . (NULL === $netid ? '??' : $netinfo['mask']) . '</small>';
                        echo '&nbsp;' . $aac[$alloc['type']];
                        if (!empty ($alloc['addrinfo']['name']))
-                               echo '(' . niftyString ($alloc['addrinfo']['name']) . ')';
+                               echo ' (' . niftyString ($alloc['addrinfo']['name']) . ')';
                        echo '</td>';
                        if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
                        {
                                if (NULL === $netid)
-                                       echo '<td colspan=2>?</td>';
+                                       echo '<td colspan=2>N/A</td><td>&nbsp;</td>';
                                else
-                                       printIPv4NetInfoTDs ($netinfo);
-                               echo "<td class='${secondclass}'>";
-                               // FIXME: These cals are really heavy, replace them with a more appropriate dedicated function.
-                               $netdata = getIPv4Network ($netid);
-                               $newline = '';
-                               foreach (findRouters ($netdata['addrlist']) as $router)
                                {
-                                       if ($router['id'] == $object_id)
-                                               continue;
-                                       echo $newline . $router['addr'] . ", <a href='${root}?page=object&object_id=${router['id']}&hl_ipv4_addr=${router['addr']}'>";
-                                       echo (empty ($router['iface']) ? '' : $router['iface'] . '@') . $router['dname'] . '</a>';
-                                       $routertags = loadRackObjectTags ($router['id']);
-                                       if (count ($routertags))
-                                               echo '<br><small>' . serializeTags ($routertags, "${root}?page=objects&tab=default&") . '</small>';
-                                       $newline = '<br>';
+                                       printIPv4NetInfoTDs ($netinfo, $secondclass);
+                                       // filter out self-allocation
+                                       $other_routers = array();
+                                       foreach (findRouters ($netinfo['addrlist']) as $router)
+                                               if ($router['id'] != $object_id)
+                                                       $other_routers[] = $router;
+                                       printRoutersTD ($other_routers);
                                }
-                               echo '</td>';
                        }
                        // peers
                        echo "<td class='${secondclass}'>\n";
@@ -959,16 +1014,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];
                }
@@ -1028,6 +1080,19 @@ function showMessageOrError ()
 // This function renders a form for port edition.
 function renderPortsForObject ($object_id = 0)
 {
+       function printNewItemTR ()
+       {
+               printOpFormIntro ('addPort');
+               echo "<tr><td>";
+               printImageHREF ('add', 'add a port', TRUE);
+               echo "</td><td><input type=text size=8 name=port_name tabindex=100></td>\n";
+               echo "<td><input type=text size=24 name=port_label tabindex=101></td><td>";
+               printSelect (getPortTypes(), 'port_type_id', getConfigVar ('default_port_type'), 102);
+               echo "<td><input type=text name=port_l2address tabindex=103></td>\n";
+               echo "<td colspan=3>&nbsp;</td><td>";
+               printImageHREF ('add', 'add a port', TRUE, 104);
+               echo "</td></tr></form>";
+       }
        global $root, $pageno, $tabno;
        if ($object_id <= 0)
        {
@@ -1041,6 +1106,8 @@ function renderPortsForObject ($object_id = 0)
        echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
        echo "<tr><th>&nbsp;</th><th>Local name</th><th>Visible label</th><th>Port type</th><th>L2 address</th>";
        echo "<th>Rem. object</th><th>Rem. port</th><th>(Un)link or (un)reserve</th><th>&nbsp;</th></tr>\n";
+       if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
+               printNewItemTR();
        foreach ($ports as $port)
        {
                printOpFormIntro ('editPort', array ('port_id' => $port['id']));
@@ -1049,7 +1116,17 @@ function renderPortsForObject ($object_id = 0)
                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'])
                {
@@ -1058,7 +1135,7 @@ function renderPortsForObject ($object_id = 0)
                        echo "<td><a href='${root}process.php?op=unlinkPort&page=${pageno}&tab=${tabno}&port_id=${port['id']}&object_id=$object_id&port_name=";
                        echo urlencode ($port['name']);
                        echo "&remote_port_name=${port['remote_name']}&remote_object_name=${port['remote_object_name']}'>";
-                       printImageHREF ('unlink', 'Unlink this port');
+                       printImageHREF ('clear', 'Unlink this port');
                        echo "</a></td>";
                }
                elseif (!empty ($port['reservation_comment']))
@@ -1066,14 +1143,14 @@ 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}'>";
-                       printImageHREF ('useup', 'Use up this port');
+                       printImageHREF ('clear', 'Use up this port');
                        echo "</a></td>";
                }
                else
                {
                        echo "<td>&nbsp;</td><td>&nbsp;</td>";
                        echo "<td>";
-                       echo "<a href='javascript:;' onclick='window.open(\"${root}link_helper.php?port=${port['id']}&type=${port['type_id']}&object_id=$object_id&port_name=";
+                       echo "<a href='javascript:;' onclick='window.open(\"${root}port_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\");'>";
                        printImageHREF ('link', 'Link this port');
@@ -1084,24 +1161,8 @@ function renderPortsForObject ($object_id = 0)
                printImageHREF ('save', 'Save changes', TRUE);
                echo "</td></form></tr>\n";
        }
-       printOpFormIntro ('addPort');
-       echo "<tr><td>";
-       printImageHREF ('add', '', TRUE, 104);
-       echo "</td><td><input type=text size=8 name=port_name tabindex=100></td>\n";
-       echo "<td><input type=text size=24 name=port_label tabindex=101></td>";
-       echo "<td><select name='port_type_id' tabindex=102>\n";
-       $types = getPortTypes();
-       $default_port_type = getConfigVar ('default_port_type');
-       foreach ($types as $typeid => $typename)
-       {
-               echo "<option value='${typeid}'";
-               if ($typeid == $default_port_type)
-                       echo " selected";
-               echo ">${typename}</option>\n";
-       }
-       echo "</select></td>";
-       echo "<td><input type=text name=port_l2address tabindex=103></td>\n";
-       echo "<td colspan=4>&nbsp;</td></tr></form>";
+       if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
+               printNewItemTR();
        echo "</table><br>\n";
        finishPortlet();
 
@@ -1114,15 +1175,7 @@ function renderPortsForObject ($object_id = 0)
        echo '<option value=ssv1>SSV:&lt;interface name&gt; &lt;MAC address&gt;</option>';
        echo "</select>";
        echo 'Default port type: ';
-       echo "<select name=port_type>\n";
-       foreach ($types as $typeid => $typename)
-       {
-               echo "<option value='${typeid}'";
-               if ($typeid == $default_port_type)
-                       echo " selected";
-               echo ">${typename}</option>\n";
-       }
-       echo "</select>";
+       printSelect (getPortTypes(), 'port_type', getConfigVar ('default_port_type'), 102);
        echo "<input type=submit value='Parse output'><br>\n";
        echo "<textarea name=input cols=100 rows=50></textarea><br>\n";
        echo '</form>';
@@ -1131,6 +1184,21 @@ function renderPortsForObject ($object_id = 0)
 
 function renderIPv4ForObject ($object_id = 0)
 {
+       function printNewItemTR ()
+       {
+               global $aat;
+               printOpFormIntro ('addIPv4Allocation');
+               echo "<tr><td>";
+               printImageHREF ('add', 'allocate', TRUE);
+               echo "</td>";
+               echo "<td class=tdleft><input type='text' size='10' name='bond_name' tabindex=100></td>\n";
+               echo "<td class=tdleft><input type=text name='ip' tabindex=101></td>\n";
+               echo "<td colspan=3>&nbsp;</td><td>";
+               printSelect ($aat, 'bond_type', NULL, 102);
+               echo "</td><td>&nbsp;</td><td>";
+               printImageHREF ('add', 'allocate', TRUE, 103);
+               echo "</td></tr></form>";
+       }
        global $root, $pageno, $tabno, $aat;
        if ($object_id <= 0)
        {
@@ -1141,18 +1209,62 @@ function renderIPv4ForObject ($object_id = 0)
        startPortlet ('Allocations');
        $alloclist = getObjectIPv4Allocations ($object_id);
        echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
-       echo "<tr><th>&nbsp;</th><th>OS interface</th><th>IP address</th><th>description</th><th>type</th><th>misc</th><th>&nbsp</th></tr>\n";
+       echo '<tr><th>&nbsp;</th><th>OS interface</th><th>IP address</th>';
+       if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
+               echo '<th colspan=2>network</th><th>routed by</th>';
+       echo '<th>type</th><th>misc</th><th>&nbsp</th></tr>';
+
+       if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
+               printNewItemTR();
        foreach ($alloclist as $dottedquad => $alloc)
        {
-               $address_name = niftyString ($alloc['addrinfo']['name']);
                $class = $alloc['addrinfo']['class'];
+               $netid = getIPv4AddressNetworkId ($dottedquad);
+               if (NULL !== $netid)
+               {
+                       $netinfo = getIPv4NetworkInfo ($netid);
+                       loadIPv4AddrList ($netinfo);
+               }
                printOpFormIntro ('updIPv4Allocation', array ('ip' => $dottedquad));
-               echo "<tr class='$class'><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='${root}process.php?op=delIPv4Allocation&page=${pageno}&tab=${tabno}&ip=${dottedquad}&object_id=$object_id'>";
                printImageHREF ('delete', 'Delete this IPv4 address');
                echo "</a></td>";
-               echo "<td class=tdleft><input type='text' name='bond_name' value='${alloc['osif']}' size=10></td>";
-               echo "<td class=tdleft><a href='${root}?page=ipaddress&ip=${dottedquad}'>${dottedquad}</a></td>";
-               echo "<td class='description'>$address_name</td>\n<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>";
+               else
+                       echo $dottedquad;
+               if (getConfigVar ('EXT_IPV4_VIEW') != 'yes')
+                       echo '<small>/' . (NULL === $netid ? '??' : $netinfo['mask']) . '</small>';
+               if (!empty ($alloc['addrinfo']['name']))
+                       echo ' (' . niftyString ($alloc['addrinfo']['name']) . ')';
+               echo '</td>';
+               // FIXME: this a copy-and-paste from renderRackObject()
+               if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
+               {
+                       if (NULL === $netid)
+                               echo '<td colspan=2>N/A</td><td>&nbsp;</td>';
+                       else
+                       {
+                               printIPv4NetInfoTDs ($netinfo);
+                               echo "<td class=tdleft>";
+                               // FIXME: These calls are really heavy, replace them with a more appropriate dedicated function.
+                               $newline = '';
+                               foreach (findRouters ($netinfo['addrlist']) as $router)
+                               {
+                                       if ($router['id'] == $object_id)
+                                               continue;
+                                       echo $newline . $router['addr'] . ", <a href='${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>';
+                       }
+               }
+               echo '<td>';
                printSelect ($aat, 'bond_type', $alloc['type']);
                echo "</td><td>";
                $prefix = '';
@@ -1175,16 +1287,10 @@ function renderIPv4ForObject ($object_id = 0)
                printImageHREF ('save', 'Save changes', TRUE);
                echo "</td></form></tr>\n";
        }
+       if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
+               printNewItemTR();
 
-       printOpFormIntro ('addIPv4Allocation');
-       echo "<tr><td>";
-       printImageHREF ('add', 'Allocate new address', TRUE, 99);
-       echo "</td>";
-       echo "<td class=tdleft><input type='text' size='10' name='bond_name' tabindex=100></td>\n";
-       echo "<td class=tdleft><input type=text name='ip' tabindex=101></td>\n";
-       echo "<td>&nbsp;</td><td>";
-       printSelect ($aat, 'bond_type');
-       echo "</td><td colspan=2>&nbsp;</td></tr></form></table><br>\n";
+       echo "</table><br>\n";
        finishPortlet();
 
 }
@@ -1275,6 +1381,14 @@ 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'),
@@ -1354,6 +1468,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...'),
@@ -1692,6 +1807,7 @@ function renderProblematicObjectsPortlet ()
 function renderObjectSpace ()
 {
        global $root, $taglist, $tagtree;
+       showMessageOrError();
        echo "<table border=0 class=objectview>\n";
        echo "<tr><td class=pcleft width='50%'>";
        startPortlet ('View all by type');
@@ -1726,6 +1842,7 @@ function renderObjectSpace ()
 function renderObjectGroup ()
 {
        global $root, $pageno, $tabno, $nextorder, $taglist, $tagtree;
+       showMessageOrError();
        assertUIntArg ('group_id', __FUNCTION__, TRUE);
        $group_id = $_REQUEST['group_id'];
        $tagfilter = getTagFilter();
@@ -1763,15 +1880,15 @@ function renderObjectGroup ()
 
        echo '</td><td class=pcleft>';
 
-       startPortlet ('Objects');
        $objects = getObjectList ($group_id, $tagfilter, getTFMode());
+       startPortlet ('Objects (' . count ($objects) . ')');
        if ($objects === NULL)
        {
                showError ('getObjectList() failed', __FUNCTION__);
                return;
        }
        echo '<br><br><table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
-       echo '<tr><th>Common name</th><th>Visible label</th><th>Asset tag</th><th>Barcode</th><th>Rack</th></tr>';
+       echo '<tr><th>Common name</th><th>Visible label</th><th>Asset tag</th><th>Barcode</th><th>Row/Rack</th><th></th></tr>';
        $order = 'odd';
        foreach ($objects as $obj)
        {
@@ -1779,14 +1896,20 @@ function renderObjectGroup ()
                        $secondclass = 'tdleft port_highlight';
                else
                        $secondclass = 'tdleft';
-               echo "<tr class=row_${order}><td class='${secondclass}'><a href='${root}?page=object&object_id=${obj['id']}'>${obj['dname']}</a></td>";
-               echo "<td class='${secondclass}'>${obj['label']}</td>";
+               $tags = loadRackObjectTags ($obj['id']);
+               echo "<tr class=row_${order} valign=top><td class='${secondclass}'><a href='${root}?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 "</td><td class='${secondclass}'>${obj['label']}</td>";
                echo "<td class='${secondclass}'>${obj['asset_no']}</td>";
                echo "<td class='${secondclass}'>${obj['barcode']}</td>";
                if ($obj['rack_id'])
-                       echo "<td class='${secondclass}'><a href='${root}?page=rack&rack_id=${obj['rack_id']}'>${obj['Rack_name']}</a></td>";
+                       echo "<td class='${secondclass}'><a href='${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>";
                else
                        echo "<td class='${secondclass}'>Unmounted</td>";
+               echo "<td class='${secondclass}'><a href='${root}process.php?op=deleteObject&page=${pageno}&tab=${tabno}&group_id=${group_id}&object_id=${obj['id']}&name=${obj['dname']}' onclick=\"javascript:return confirm('Are you sure you want to delete the object: ${obj['dname']}?')\">";
+               printImageHREF ('delete', 'Delete object', TRUE);
+               echo "</a></td>";
                echo '</tr>';
                $order = $nextorder[$order];
        }
@@ -1919,9 +2042,9 @@ function renderRackspaceHistory ()
        else
                echo "nothing";
        finishPortlet();
-       
+
        echo '</td></tr><tr><td colspan=2>';
-       
+
        // Bottom portlet with list
 
        startPortlet ('Rackspace allocation history');
@@ -1941,41 +2064,101 @@ function renderRackspaceHistory ()
        }
        echo "</table>\n";
        finishPortlet();
-       
+
        echo '</td></tr></table>';
-       
+}
+
+function renderIPv4SpaceRecords ($tree, &$tagcache, $baseurl, $target = 0, $level = 1)
+{
+       $self = __FUNCTION__;
+       foreach ($tree as $item)
+       {
+               $total = $item['addrt'];
+               if (getConfigVar ('IPV4_TREE_SHOW_USAGE') == 'yes')
+               {
+                       loadIPv4AddrList ($item); // necessary to compute router list and address counter
+                       $used = $item['addrc'];
+               }
+               else
+               {
+                       $item['addrlist'] = array();
+                       $item['addrc'] = 0;
+               }
+               if (isset ($item['id']))
+               {
+                       if ($item['symbol'] == 'node-collapsed')
+                               $expandurl = "${baseurl}&eid=" . $item['id'] . "#netid" . $item['id'];
+                       elseif ($item['symbol'] == 'node-expanded')
+                               $expandurl = $baseurl . ($item['parent_id'] ? "&eid=${item['parent_id']}#netid${item['parent_id']}" : '');
+                       else
+                               $expandurl = '';
+                       echo "<tr valign=top>";
+                       printIPv4NetInfoTDs ($item, 'tdleft', $level, $item['symbol'], $expandurl);
+                       echo "<td class=tdcenter>";
+                       if ($target == $item['id'])
+                               echo "<a name=netid${target}></a>";
+                       if (getConfigVar ('IPV4_TREE_SHOW_USAGE') == 'yes')
+                       {
+                               renderProgressBar ($total ? $used/$total : 0);
+                               echo "<br><small>${used}/${total}</small>";
+                       }
+                       else
+                               echo "<small>${total}</small>";
+                       echo "</td>";
+                       if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
+                               printRoutersTD (findRouters ($item['addrlist']), $tagcache);
+                       echo "</tr>";
+                       if ($item['symbol'] == 'node-expanded' or $item['symbol'] == 'node-expanded-static')
+                               $self ($item['kids'], $tagcache, $baseurl, $target, $level + 1);
+               }
+               else
+               {
+                       echo "<tr valign=top>";
+                       printIPv4NetInfoTDs ($item, 'tdleft sparenetwork', $level, $item['symbol']);
+                       echo "<td class=tdcenter>";
+                       if (getConfigVar ('IPV4_TREE_SHOW_USAGE') == 'yes')
+                       {
+                               renderProgressBar ($used/$total, 'sparenetwork');
+                               echo "<br><small>${used}/${total}</small>";
+                       }
+                       else
+                               echo "<small>${total}</small>";
+                       echo "</td><td>&nbsp;</td></tr>";
+               }
+       }
 }
 
 function renderIPv4Space ()
 {
-       global $root, $pageno;
+       global $root, $pageno, $tabno;
+       $tagfilter = getTagFilter();
+       $netlist = getIPv4NetworkList ($tagfilter, getTFMode());
+       $netcount = count ($netlist);
+       // expand request can take either natural values or "ALL". Zero means no expanding.
+       $eid = isset ($_REQUEST['eid']) ? $_REQUEST['eid'] : 0;
+       $tree = prepareIPv4Tree ($netlist, $eid);
 
        echo "<table border=0 class=objectview>\n";
        echo "<tr><td class=pcleft>";
-
-       $tagfilter = getTagFilter();
-       $addrspaceList = getAddressspaceList ($tagfilter, getTFMode());
-       $tagcache = array();
-       startPortlet ('networks (' . count ($addrspaceList) . ')');
-       echo "<table class='widetable' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
-       echo "<tr><th>prefix</th><th>name/tags</th><th>%% used</th>";
+       startPortlet ("networks (${netcount})");
+       echo '<h4>';
+       if ($eid === 0)
+               echo 'auto-collapsing at threshold ' . getConfigVar ('TREE_THRESHOLD') . " (<a href='${root}?page=${pageno}&tab=${tabno}&eid=ALL'>expand all</a>)";
+       elseif ($eid === 'ALL')
+               echo "expanding all (<a href='${root}?page=${pageno}&tab=${tabno}'>auto-collapse</a>)";
+       else
+       {
+               $netinfo = getIPv4NetworkInfo ($eid);
+               echo "expanding ${netinfo['ip']}/${netinfo['mask']} (<a href='${root}?page=${pageno}&tab=${tabno}'>auto-collapse</a> / <a href='${root}?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";
-       foreach ($addrspaceList as $iprange)
-       {
-               $netdata = getIPv4Network ($iprange['id']);
-               $total = ($netdata['ip_bin'] | $netdata['mask_bin_inv']) - ($netdata['ip_bin'] & $netdata['mask_bin']) + 1;
-               $used = count ($netdata['addrlist']);
-               echo "<tr valign=top>";
-               printIPv4NetInfoTDs ($netdata);
-               echo "<td class=tdcenter>";
-               renderProgressBar ($used/$total);
-               echo "<br><small>${used}/${total}</small></td>";
-               if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
-                       printRoutersTD (findRouters ($netdata['addrlist']), $tagcache);
-               echo "</tr>";
-       }
+       $tagcache = array();
+       $baseurl = "${root}?page=${pageno}&tab=${tabno}" . getTagFilterStr ($tagfilter);
+       renderIPv4SpaceRecords ($tree, $tagcache, $baseurl, $eid);
        echo "</table>\n";
        finishPortlet();
        echo '</td><td class=pcright>';
@@ -2014,17 +2197,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='${root}?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)
                        {
@@ -2034,9 +2219,9 @@ 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 $vsdata['lblist'][$lb_object_id]['name'] . '</a>)';
+//                                     echo " (<a href='${root}?page=ipv4rspool&pool_id=";
+//                                     echo $vsdata['lblist'][$lb_object_id]['id'] . "'>";
+//                                     echo $vsdata['lblist'][$lb_object_id]['name'] . '</a>)';
                                }
                                echo '</td>';
                        }
@@ -2053,6 +2238,18 @@ function renderIPv4SpaceEditor ()
        global $root, $pageno, $tabno;
        showMessageOrError();
 
+       // IPv4 validator
+?>
+       <script type="text/javascript">
+       function init() {
+               document.add_new_range.range.setAttribute('match', "^\\d\\d?\\d?\\.\\d\\d?\\d?\\.\\d\\d?\\d?\\.\\d\\d?\\d?\\/\\d\\d?$");
+
+               Validate.init();
+       }
+       window.onload=init;
+       </script>
+<?php
+
        startPortlet ("Add new");
        echo '<table border=0 cellpadding=10 align=center>';
        // This form requires a name, so JavaScript validator can find it.
@@ -2068,36 +2265,27 @@ function renderIPv4SpaceEditor ()
        // inputs column
        echo "<th class=tdright>prefix</th><td class=tdleft><input type=text name='range' size=18 class='live-validate' tabindex=1></td>";
        echo "<tr><th class=tdright>name</th><td class=tdleft><input type=text name='name' size='20' tabindex=2></td></tr>";
-       echo "<tr><th class=tdright>connected network</th><td class=tdleft><input type=checkbox name='is_bcast' tabindex=3 checked></td></tr>";
+       echo "<tr><th class=tdright>connected network</th><td class=tdleft><input type=checkbox name='is_bcast' tabindex=3></td></tr>";
        echo "<tr><td colspan=2>";
        printImageHREF ('CREATE', 'Add a new network', TRUE, 4);
        echo '</td></tr>';
        echo "</form></table><br><br>\n";
        finishPortlet();
 
-       $addrspaceList = getAddressspaceList();
-       if (count ($addrspaceList))
+       $addrspaceList = getIPv4NetworkList();
+       $netcount = count ($addrspaceList);
+       if ($netcount)
        {
-               startPortlet ('Manage existing (' . count ($addrspaceList) . ')');
+               startPortlet ("Manage existing (${netcount})");
                echo "<table class='widetable' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
                echo "<tr><th>&nbsp;</th><th>prefix</th><th>name</th><th>&nbsp;</th></tr>";
-               foreach ($addrspaceList as $iprange)
+               foreach ($addrspaceList as $netinfo)
                {
-                       $netdata = getIPv4Network ($iprange['id']);
-                       $usedips = count ($netdata['addrlist']);
-                       $totalips = ($netdata['ip_bin'] | $netdata['mask_bin_inv']) - ($netdata['ip_bin'] & $netdata['mask_bin']) + 1;
-                       echo "<form method=post action='${root}process.php?page=${pageno}&tab=${tabno}&op=updIPv4Prefix&id=${iprange['id']}'>";
-                       echo '<tr valign=top><td>';
-                       if ($usedips == 0)
-                       {
-                               echo "<a href='${root}process.php?op=delIPv4Prefix&page=${pageno}&tab=${tabno}&id=${iprange['id']}'>";
-                               printImageHREF ('delete', 'Delete this IP range');
-                               echo "</a>";
-                       }
-                       else
-                               printImageHREF ('nodelete', 'There are IP addresses allocated or reserved');
-                       echo "</td>\n<td class=tdleft>${netdata['ip']}/${netdata['mask']}</td>";
-                       echo "<td><input type=text name=name size=40 value='${netdata['name']}'>";
+                       echo "<form method=post action='${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']}'>";
+                       printImageHREF ('delete', 'Delete this IP range');
+                       echo "</a></td>\n<td class=tdleft>${netinfo['ip']}/${netinfo['mask']}</td>";
+                       echo "<td><input type=text name=name size=40 value='${netinfo['name']}'>";
                        echo "</td><td>";
                        printImageHREF ('save', 'Save changes', TRUE);
                        echo "</td></tr></form>\n";
@@ -2186,7 +2374,8 @@ function renderIPv4Network ($id)
        else
                $page=0;
 
-       $range = getIPv4Network ($id);
+       $range = getIPv4NetworkInfo ($id);
+       loadIPv4AddrList ($range);
        echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
        echo "<tr><td colspan=2 align=center><h1>${range['ip']}/${range['mask']}</h1><h2>${range['name']}</h2></td></tr>\n";
 
@@ -2200,10 +2389,41 @@ function renderIPv4Network ($id)
        renderProgressBar ($used/$total);
        echo "&nbsp;${used}/${total}</td></tr>\n";
 
+       if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
+       {
+               // Build a backtrace from all parent networks.
+               $clen = $range['mask'];
+               $backtrace = array();
+               while (NULL !== ($upperid = getIPv4AddressNetworkId ($range['ip'], $clen)))
+               {
+                       $upperinfo = getIPv4NetworkInfo ($upperid);
+                       $clen = $upperinfo['mask'];
+                       $backtrace[] = $upperid;
+               }
+               $arrows = count ($backtrace);
+               foreach (array_reverse ($backtrace) as $ancestorid)
+               {
+                       $ainfo = getIPv4NetworkInfo ($ancestorid);
+                       echo "<tr><th width='50%' class=tdright>";
+                       for ($i = 0; $i < $arrows; $i++)
+                               echo '&uarr;';
+                       $arrows--;
+                       echo "</th><td class=tdleft><a href='${root}?page=${pageno}&tab=${tabno}&id=${ainfo['id']}'>${ainfo['ip']}/${ainfo['mask']}</a></td></tr>";
+               }
+               echo "<tr><th width='50%' class=tdright>&rarr;</th>";
+               echo "<td class=tdleft>${range['ip']}/${range['mask']}</td></tr>";
+               // FIXME: get and display nested networks
+               // $theitem = pickLeaf ($ipv4tree, $id);
+       }
+
        echo "<tr><th width='50%' class=tdright>Netmask:</th><td class=tdleft>";
        echo $netmaskbylen[$range['mask']];
        echo "</td></tr>\n";
 
+       echo "<tr><th width='50%' class=tdright>Netmask:</th><td class=tdleft>";
+       printf ('0x%08X', binMaskFromDec ($range['mask']));
+       echo "</td></tr>\n";
+
        echo "<tr><th width='50%' class=tdright>Wildcard bits:</th><td class=tdleft>";
        echo $wildcardbylen[$range['mask']];
        echo "</td></tr>\n";
@@ -2216,9 +2436,11 @@ function renderIPv4Network ($id)
                echo "</tr>\n";
        }
 
-       printTagTRs ("${root}?page=ipv4space&");
+       printTagTRs ("${root}?page=ipv4space&tab=default&");
        echo "</table><br>\n";
        finishPortlet();
+
+       renderFilesPortlet ('ipv4net', $id);
        echo "</td>\n";
 
        echo "<td class=pcright>";
@@ -2305,7 +2527,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='${root}?page=ipv4rspool&pool_id=${ref['rspool_id']}'>";
                        echo "${ref['rspool_name']}</a>";
                        $delim = '; ';
                }
@@ -2329,7 +2551,6 @@ function renderIPv4NetworkProperties ($id)
        echo "<tr><td colspan=2 class=tdcenter>";
        printImageHREF ('SAVE', 'Save changes', TRUE);
        echo "</td></form></tr></table>\n";
-
 }
 
 function renderIPv4Address ($dottedquad)
@@ -2356,7 +2577,7 @@ function renderIPv4Address ($dottedquad)
 
        echo "<td class=pcright>";
 
-       if (!empty ($address['class']))
+       if (isset ($address['class']))
        {
                startPortlet ('allocations');
                echo "<table class='widetable' cellpadding=5 cellspacing=0 border=0 align='center' width='100%'>\n";
@@ -2409,7 +2630,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='${root}?page=ipv4rspool&pool_id=${rsinfo['rspool_id']}'>";
                        echo $rsinfo['rspool_name'] . "</a></td></tr>\n";
                }
                echo "</table><br><br>";
@@ -2422,7 +2643,7 @@ function renderIPv4Address ($dottedquad)
                echo "<table class='widetable' cellpadding=5 cellspacing=0 border=0 align='center' width='100%'>\n";
                echo "<tr><th>proto</th><th>from</th><th>to</th><th>comment</th></tr>\n";
                foreach ($address['outpf'] as $rule)
-                       echo "<tr><td>${rule['proto']}</td><td>${rule['localip']}:${rule['localport']}</td><td>${rule['remoteip']}:${rule['localport']}</td><td>${rule['description']}</td></tr>";
+                       echo "<tr><td>${rule['proto']}</td><td>${rule['localip']}:${rule['localport']}</td><td>${rule['remoteip']}:${rule['remoteport']}</td><td>${rule['description']}</td></tr>";
                echo "</table>";
                finishPortlet();
        }
@@ -2433,7 +2654,7 @@ function renderIPv4Address ($dottedquad)
                echo "<table class='widetable' cellpadding=5 cellspacing=0 border=0 align='center' width='100%'>\n";
                echo "<tr><th>proto</th><th>from</th><th>to</th><th>comment</th></tr>\n";
                foreach ($address['inpf'] as $rule)
-                       echo "<tr><td>${rule['proto']}</td><td>${rule['localip']}:${rule['localport']}</td><td>${rule['remoteip']}:${rule['localport']}</td><td>${rule['description']}</td></tr>";
+                       echo "<tr><td>${rule['proto']}</td><td>${rule['localip']}:${rule['localport']}</td><td>${rule['remoteip']}:${rule['remoteport']}</td><td>${rule['description']}</td></tr>";
                echo "</table>";
                finishPortlet();
        }
@@ -2470,48 +2691,88 @@ function renderIPv4AddressProperties ($dottedquad)
 function renderIPv4AddressAllocations ($dottedquad)
 {
        showMessageOrError();
+       function printNewItemTR ()
+       {
+               global $aat;
+               printOpFormIntro ('addIPv4Allocation');
+               echo "<tr><td>";
+               printImageHREF ('add', 'allocate', TRUE);
+               echo "</td><td><select name='object_id' tabindex=100>";
+
+               foreach (explode (',', getConfigVar ('IPV4_PERFORMERS')) as $type) 
+                       foreach (getNarrowObjectList ($type) as $object)
+                               echo "<option value='${object['id']}'>${object['dname']}</option>";
+
+               echo "</select></td><td><input type=text tabindex=101 name=bond_name size=10></td><td>";
+               printSelect ($aat, 'bond_type', NULL, 102);
+               echo "</td><td>";
+               printImageHREF ('add', 'allocate', TRUE, 103);
+               echo "</td></form></tr>";
+       }
        global $pageno, $tabno, $root, $aat;
 
        $address = getIPv4Address ($dottedquad);
-       $class = $address['class'];
+
        echo "<center><h1>${dottedquad}</h1></center>\n";
        echo "<table class='widetable' cellpadding=5 cellspacing=0 border=0 align='center'>\n";
        echo "<tr><th>&nbsp;</th><th>object</th><th>OS interface</th><th>allocation type</th><th>&nbsp;</th></tr>\n";
 
-       if ($address['reserved'] == 'yes')
-               echo "<tr class='${class}'><td colspan=3>&nbsp;</td><td class=tdleft><strong>RESERVED</strong></td><td>&nbsp;</td></tr>";
-       foreach ($address['allocs'] as $bond)
+       if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
+               printNewItemTR();
+       if (isset ($address['class']))
        {
-               echo "<tr class='$class'>";
-               printOpFormIntro ('updIPv4Allocation', array ('object_id' => $bond['object_id']));
-               echo "<td><a href='${root}process.php?op=delIPv4Allocation&page=${pageno}&tab=${tabno}&ip=${dottedquad}&object_id=${bond['object_id']}'>";
-               printImageHREF ('delete', 'Unallocate address');
-               echo "</a></td>";
-               echo "<td><a href='${root}?page=object&object_id=${bond['object_id']}&hl_ipv4_addr=${dottedquad}'>${bond['object_name']}</td>";
-               echo "<td><input type='text' name='bond_name' value='${bond['name']}' size=10></td><td>";
-               printSelect ($aat, 'bond_type', $bond['type']);
-               echo "</td><td>";
-               printImageHREF ('save', 'Save changes', TRUE);
-               echo "</td></form></tr>\n";
+               $class = $address['class'];
+               if ($address['reserved'] == 'yes')
+                       echo "<tr class='${class}'><td colspan=3>&nbsp;</td><td class=tdleft><strong>RESERVED</strong></td><td>&nbsp;</td></tr>";
+               foreach ($address['allocs'] as $bond)
+               {
+                       echo "<tr class='$class'>";
+                       printOpFormIntro ('updIPv4Allocation', array ('object_id' => $bond['object_id']));
+                       echo "<td><a href='${root}process.php?op=delIPv4Allocation&page=${pageno}&tab=${tabno}&ip=${dottedquad}&object_id=${bond['object_id']}'>";
+                       printImageHREF ('delete', 'Unallocate address');
+                       echo "</a></td>";
+                       echo "<td><a href='${root}?page=object&object_id=${bond['object_id']}&hl_ipv4_addr=${dottedquad}'>${bond['object_name']}</td>";
+                       echo "<td><input type='text' name='bond_name' value='${bond['name']}' size=10></td><td>";
+                       printSelect ($aat, 'bond_type', $bond['type']);
+                       echo "</td><td>";
+                       printImageHREF ('save', 'Save changes', TRUE);
+                       echo "</td></form></tr>\n";
+               }
        }
-       printOpFormIntro ('addIPv4Allocation');
-       echo "<td>";
-       printImageHREF ('add', 'new allocation', TRUE);
-       echo "</td><td><select name='object_id'>";
-
-       foreach (explode (',', getConfigVar ('IPV4_PERFORMERS')) as $type) 
-               foreach (getNarrowObjectList ($type) as $object)
-                       echo "<option value='${object['id']}'>${object['dname']}</option>";
-
-       echo "</select></td><td><input type='text' name='bond_name' value='' size=10></td><td>";
-       printSelect ($aat, 'bond_type');
-       echo "</td><td>&nbsp;</td></form></tr>";
+       if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
+               printNewItemTR();
        echo "</table><br><br>";
-
 }
 
 function renderNATv4ForObject ($object_id = 0)
 {
+       function printNewItemTR ($alloclist)
+       {
+               global $root;
+               printOpFormIntro ('addNATv4Rule');
+               echo "<tr align='center'><td>";
+               printImageHREF ('add', 'Add new NAT rule', TRUE);
+               echo '</td><td>';
+               printSelect (array ('TCP' => 'TCP', 'UDP' => 'UDP'), 'proto');
+               echo "<select name='localip' tabindex=1>";
+
+               foreach ($alloclist as $dottedquad => $alloc)
+               {
+                       $name = empty ($alloc['addrinfo']['name']) ? '' : (' (' . niftyString ($alloc['addrinfo']['name']) . ')');
+                       $osif = empty ($alloc['osif']) ? '' : ($alloc['osif'] . ': ');
+                       echo "<option value='${dottedquad}'>${osif}${dottedquad}${name}</option>";
+               }
+
+               echo "</select>:<input type='text' name='localport' size='4' tabindex=2></td>";
+               echo "<td><input type='text' name='remoteip' id='remoteip' size='10' tabindex=3>";
+               echo "<a href='javascript:;' onclick='window.open(\"${root}/find_object_ip_helper.php\", \"findobjectip\", \"height=700, width=400, location=no, menubar=no, resizable=yes, scrollbars=no, status=no, titlebar=no, toolbar=no\");'>";
+               printImageHREF ('find', 'Find object');
+               echo "</a>";
+               echo ":<input type='text' name='remoteport' size='4' tabindex=4></td><td></td>";
+               echo "<td colspan=1><input type='text' name='description' size='20' tabindex=5></td><td>";
+               printImageHREF ('add', 'Add new NAT rule', TRUE, 6);
+               echo "</td></tr></form>";
+       }
        global $pageno, $tabno, $root;
        
        $info = getObjectInfo ($object_id);
@@ -2523,6 +2784,8 @@ function renderNATv4ForObject ($object_id = 0)
        echo "<table class='widetable' cellpadding=5 cellspacing=0 border=0 align='center'>\n";
        echo "<tr><th></th><th>Match endpoint</th><th>Translate to</th><th>Target object</th><th>Comment</th><th>&nbsp;</th></tr>\n";
 
+       if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
+               printNewItemTR ($alloclist);
        foreach ($forwards['out'] as $pf)
        {
                $class = 'trerror';
@@ -2568,28 +2831,8 @@ function renderNATv4ForObject ($object_id = 0)
                printImageHREF ('save', 'Save changes', TRUE);
                echo "</td></form></tr>";
        }
-       printOpFormIntro ('addNATv4Rule');
-       echo "<tr align='center'><td>";
-       printImageHREF ('add', 'Add new NAT rule', TRUE);
-       echo '</td><td>';
-       printSelect (array ('TCP' => 'TCP', 'UDP' => 'UDP'), 'proto');
-       echo "<select name='localip' tabindex=1>";
-
-       foreach ($alloclist as $dottedquad => $alloc)
-       {
-               $name = empty ($alloc['addrinfo']['name']) ? '' : (' (' . niftyString ($alloc['addrinfo']['name']) . ')');
-               $osif = empty ($alloc['osif']) ? '' : ($alloc['osif'] . ': ');
-               echo "<option value='${dottedquad}'>${osif}${dottedquad}${name}</option>";
-       }
-
-       echo "</select>:<input type='text' name='localport' size='4' tabindex=2></td>";
-       echo "<td><input type='text' name='remoteip' id='remoteip' size='10' tabindex=3>";
-       echo "<a href='javascript:;' onclick='window.open(\"${root}/find_object_ip_helper.php\", \"findobjectip\", \"height=700, width=400, location=no, menubar=no, resizable=yes, scrollbars=no, status=no, titlebar=no, toolbar=no\");'>";
-       printImageHREF ('find', 'Find object');
-       echo "</a>";
-       echo ":<input type='text' name='remoteport' size='4' tabindex=4></td><td></td>";
-       echo "<td colspan=1><input type='text' name='description' size='20' tabindex=5></td><td>&nbsp;</td></tr>";
-       echo "</form>";
+       if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
+               printNewItemTR ($alloclist);
 
        echo "</table><br><br>";
 
@@ -2759,9 +3002,11 @@ function renderAddMultipleObjectsForm ()
 
 function printGreeting ()
 {
-       global $remote_username, $accounts, $root;
+       global $root, $remote_username, $accounts, $root;
        $account = $accounts[$remote_username];
-       $person = empty ($account['user_realname']) ? $account['user_name'] : $account['user_realname'];
+       $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.";
 }
 
@@ -2783,11 +3028,11 @@ 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
+               // 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.
        {
@@ -2802,8 +3047,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))
                {
@@ -2812,8 +3057,19 @@ function renderSearchResults ()
                        $summary['ipv4addressbydq'][] = $terms;
                }
        }
-       else
-       // Search for objects, addresses, networks, virtual services and RS pools by their description.
+       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.
        {
                $tmp = getObjectSearchResults ($terms);
                if (count ($tmp))
@@ -2857,6 +3113,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>";
@@ -2873,19 +3136,19 @@ function renderSearchResults ()
                        case 'ipv4addressbydq':
                                $parentnet = getIPv4AddressNetworkId ($record);
                                if ($parentnet !== NULL)
-                                       echo "<script language='Javascript'>document.location='${root}?page=iprange&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&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;
@@ -2893,7 +3156,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>";
@@ -2901,6 +3164,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;
        }
@@ -2915,11 +3181,14 @@ function renderSearchResults ()
                                case 'object':
                                        startPortlet ("<a href='${root}?page=objects'>Objects</a>");
                                        echo '<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
-                                       echo '<tr><th>Common name</th><th>Visible label</th><th>Asset tag</th><th>barcode</th></tr>';
+                                       echo '<tr><th>Common name</th><th>Visible label</th><th>Asset tag</th><th>Barcode</th></tr>';
                                        foreach ($what as $obj)
                                        {
-                                               echo "<tr class=row_${order}><td><a href=\"${root}?page=object&object_id=${obj['id']}\">${obj['dname']}</a></td>";
-                                               echo "<td>${obj['label']}</td>";
+                                               $tags = loadRackObjectTags ($obj['id']);
+                                               echo "<tr class=row_${order} valign=top><td class=tdleft><a href=\"${root}?page=object&object_id=${obj['id']}\">${obj['dname']}</a>";
+                                               if (count ($tags))
+                                                       echo '<br><small>' . serializeTags ($tags) . '</small>';
+                                               echo "</td><td>${obj['label']}</td>";
                                                echo "<td>${obj['asset_no']}</td>";
                                                echo "<td>${obj['barcode']}</td></tr>";
                                                $order = $nextorder[$order];
@@ -2930,7 +3199,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>";
@@ -2945,13 +3214,13 @@ function renderSearchResults ()
                                        startPortlet ('IPv4 addresses');
                                        echo '<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
                                        // FIXME: address, parent network, routers (if extended view is enabled)
-                                       echo '<tr><th>Address</th><th>Descritpion</th></tr>';
+                                       echo '<tr><th>Address</th><th>Description</th></tr>';
                                        foreach ($what as $addr)
                                        {
                                                echo "<tr class=row_${order}><td class=tdleft>";
                                                $parentnet = getIPv4AddressNetworkId ($addr['ip']);
                                                if ($parentnet !== NULL)
-                                                       echo "<a href='${root}?page=iprange&id=${parentnet}&hl_ipv4_addr=${addr['ip']}'>${addr['ip']}</a></td>";
+                                                       echo "<a href='${root}?page=ipv4net&tab=default&id=${parentnet}&hl_ipv4_addr=${addr['ip']}'>${addr['ip']}</a></td>";
                                                else
                                                        echo "<a href='${root}?page=ipaddress&ip=${addr['ip']}'>${addr['ip']}</a></td>";
                                                echo "<td class=tdleft>${addr['name']}</td></tr>";
@@ -2965,7 +3234,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];
@@ -2976,7 +3245,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']}'>";
@@ -2990,7 +3259,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']}'>";
@@ -3001,6 +3270,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;
                        }
        }
 }
@@ -3040,7 +3323,7 @@ function renderAtomGrid ($data)
 
 function renderUserList ()
 {
-       global $nextorder, $accounts, $root;
+       global $nextorder, $accounts, $root, $pageno;
        echo "<table border=0 class=objectview>\n";
        echo "<tr><td class=pcleft>";
        startPortlet ('User accounts');
@@ -3050,9 +3333,12 @@ function renderUserList ()
        $tagfilter = getTagFilter();
        foreach (getUserAccounts ($tagfilter, getTFMode()) as $user)
        {
-               echo "<tr class=row_${order}><td class=tdleft><a href='${root}?page=user&user_id=${user['user_id']}'>";
-               echo "${user['user_name']}</a></td>";
-               echo "<td class=tdleft>${user['user_realname']}</td></li>";
+               echo "<tr class=row_${order} valign=top><td class=tdleft><a href='${root}?page=user&user_id=${user['user_id']}'>";
+               echo "${user['user_name']}</a>";
+               $usertags = loadUserTags ($user['user_id']);
+               if (count ($usertags))
+                       echo '<br><small>' . serializeTags ($usertags, "${root}?page=${pageno}&tab=default&") . '</small>';
+               echo "</td><td class=tdleft>${user['user_realname']}</td></li>";
                $order = $nextorder[$order];
        }
        echo '</table>';
@@ -3064,11 +3350,22 @@ function renderUserList ()
 
 function renderUserListEditor ()
 {
+       function printNewItemTR ()
+       {
+               printOpFormIntro ('createUser');
+               echo "<tr><td>&nbsp;</td><td><input type=text size=16 name=username tabindex=100></td>\n";
+               echo "<td><input type=text size=24 name=realname tabindex=101></td>";
+               echo "<td><input type=password size=64 name=password tabindex=102></td><td>";
+               printImageHREF ('create', 'Add new account', TRUE, 103);
+               echo "</td></tr></form>";
+       }
        global $root, $pageno, $tabno, $accounts;
        startPortlet ('User accounts');
        showMessageOrError();
        echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
        echo "<tr><th>status (click to change)</th><th>Username</th><th>Real name</th><th>Password</th><th>&nbsp;</th></tr>\n";
+       if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
+               printNewItemTR();
        foreach ($accounts as $account)
        {
                printOpFormIntro ('updateUser', array ('user_id' => $account['user_id']));
@@ -3093,12 +3390,8 @@ function renderUserListEditor ()
                printImageHREF ('save', 'Save changes', TRUE);
                echo "</td></form></tr>\n";
        }
-       printOpFormIntro ('createUser');
-       echo "<td>&nbsp;</td><td><input type=text size=16 name=username tabindex=100></td>\n";
-       echo "<td><input type=text size=24 name=realname tabindex=101></td>";
-       echo "<td><input type=password size=64 name=password tabindex=102></td><td>";
-       printImageHREF ('create', 'Add new account', TRUE, 103);
-       echo "</td></tr></form>";
+       if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
+               printNewItemTR();
        echo "</table><br>\n";
        finishPortlet();
 }
@@ -3182,6 +3475,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.
@@ -3202,7 +3496,7 @@ function renderDictionary ()
        foreach ($dict as $chapter_no => $chapter)
        {
                $order = 'odd';
-               echo "<tr><th>Chapter</th><th>refs</th><th>Word</th></tr>\n";
+               echo "<tr><th>Chapter</th><th>status</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>";
@@ -3217,8 +3511,12 @@ function renderDictionary ()
                                        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";
+                               echo '<td>';
+                               printImageHREF (($key <= MAX_DICT_KEY) ? 'computer' : 'favorite');
+                               echo '&nbsp;';
+                               if ($chapter['refcnt'][$key])
+                                       echo $chapter['refcnt'][$key];
+                               echo "</td><td><div title='key=${key}'>${value}</div></td></tr>\n";
                                $order = $nextorder[$order];
                        }
                }
@@ -3235,36 +3533,47 @@ function renderDictionaryEditor ()
        foreach ($dict as $chapter_no => $chapter)
        {
                $order = 'odd';
-               echo "<tr><th>Chapter</th><th>&nbsp;</th><th>Word</th><th>&nbsp;</th></tr>\n";
+               echo "<tr><th>Chapter</th><th>&nbsp;</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>";
+               echo "<td>&nbsp;</td><td>";
                printImageHREF ('add', 'Add new', TRUE);
                echo "</td>";
-               echo "<td class=tdright><input type=text name=dict_value size=32></td>";
+               echo "<td class=tdleft><input type=text name=dict_value size=32></td>";
                echo "<td>&nbsp;</td>";
                echo '</tr></form>';
                $order = $nextorder[$order];
                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 ('chapter_no' => $chapter['no'], 'dict_key' => $key));
+                               printImageHREF ('favorite');
+                               echo "<td>";
+                               // Prevent deleting words currently used somewhere.
+                               if ($chapter['refcnt'][$key])
+                                       printImageHREF ('nodelete', 'referenced ' . $chapter['refcnt'][$key] . ' time(s)');
+                               else
+                               {
+                                       echo "<a href='${root}process.php?page=${pageno}&tab=${tabno}&op=del&chapter_no=${chapter['no']}&dict_key=${key}'>";
+                                       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']
        } // foreach ($dict
@@ -3275,11 +3584,22 @@ function renderDictionaryEditor ()
 // to delete a non-empty chapter.
 function renderChaptersEditor ()
 {
+       function printNewItemTR ()
+       {
+               printOpFormIntro ('add');
+               echo '<tr><td>';
+               printImageHREF ('add', 'Add new', TRUE);
+               echo "</td><td><input type=text name=chapter_name tabindex=100></td><td>&nbsp;</td><td>";
+               printImageHREF ('add', 'Add new', TRUE, 101);
+               echo '</td></tr></form>';
+       }
        global $root, $pageno, $tabno;
        showMessageOrError();
        $dict = getDict();
        echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
        echo '<tr><th>&nbsp;</th><th>Chapter name</th><th>Words</th><th>&nbsp;</th></tr>';
+       if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
+               printNewItemTR();
        foreach ($dict as $chapter)
        {
                $wordcount = count ($chapter['word']);
@@ -3307,12 +3627,8 @@ function renderChaptersEditor ()
                echo '</td></tr>';
                echo '</form>';
        }
-       printOpFormIntro ('add');
-       echo '<tr><td>';
-       printImageHREF ('add', 'Add new', TRUE);
-       echo "</td><td colspan=3><input type=text name=chapter_name></td>";
-       echo '</tr>';
-       echo '</form>';
+       if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
+               printNewItemTR();
        echo "</table>\n";
 }
 
@@ -3348,12 +3664,29 @@ function renderAttributes ()
 
 function renderEditAttributesForm ()
 {
+       function printNewItemTR ()
+       {
+               printOpFormIntro ('add');
+               echo '<tr><td>';
+               printImageHREF ('add', 'Create attribute', TRUE);
+               echo "</td><td><input type=text tabindex=100 name=attr_name></td>";
+               echo '<td><select name=attr_type tabindex=101>';
+               echo '<option value=uint>uint</option>';
+               echo '<option value=float>float</option>';
+               echo '<option value=string>string</option>';
+               echo '<option value=dict>dict</option>';
+               echo '</select></td><td>';
+               printImageHREF ('add', 'Create attribute', TRUE, 102);
+               echo '</td></tr></form>';
+       }
        global $root, $pageno, $tabno;
        $attrMap = getAttrMap();
        showMessageOrError();
        startPortlet ('Optional attributes');
        echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
        echo '<tr><th>&nbsp;</th><th>Name</th><th>Type</th><th>&nbsp;</th></tr>';
+       if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
+               printNewItemTR();
        foreach ($attrMap as $attr)
        {
                printOpFormIntro ('upd', array ('attr_id' => $attr['id']));
@@ -3367,30 +3700,48 @@ function renderEditAttributesForm ()
                echo '</td></tr>';
                echo '</form>';
        }
-       printOpFormIntro ('add');
-       echo '<tr><td>';
-       printImageHREF ('add', '', TRUE);
-       echo "</td><td><input type=text name=attr_name></td>";
-       echo '<td><select name=attr_type>';
-       echo '<option value=uint>uint</option>';
-       echo '<option value=float>float</option>';
-       echo '<option value=string>string</option>';
-       echo '<option value=dict>dict</option>';
-       echo '</select></td>';
-       echo '</tr>';
-       echo '</form>';
+       if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
+               printNewItemTR();
        echo "</table>\n";
        finishPortlet();
 }
 
 function renderEditAttrMapForm ()
 {
+       function printNewItemTR ($attrMap)
+       {
+               printOpFormIntro ('add');
+               echo '<tr><td>';
+               printImageHREF ('add', '', TRUE);
+               echo "</td><td><select name=attr_id tabindex=100>";
+               $shortType['uint'] = 'U';
+               $shortType['float'] = 'F';
+               $shortType['string'] = 'S';
+               $shortType['dict'] = 'D';
+               foreach ($attrMap as $attr)
+                       echo "<option value=${attr['id']}>[" . $shortType[$attr['type']] . "] ${attr['name']}</option>";
+               echo "</select></td>";
+               echo '<td>';
+               printSelect (getObjectTypeList(), 'objtype_id', NULL, 101);
+               echo '</td>';
+               $dict = getDict();
+               echo '<td><select name=chapter_no tabindex=102>';
+               foreach ($dict as $chapter)
+                       if (!$chapter['sticky'])
+                               echo "<option value='${chapter['no']}'>${chapter['name']}</option>";
+               echo '</select></td><td>';
+               printImageHREF ('add', '', TRUE, 103);
+               echo '</td></tr>';
+               echo '</form>';
+       }
        global $root, $pageno, $tabno;
        $attrMap = getAttrMap();
        showMessageOrError();
        startPortlet ('Attribute map');
        echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
-       echo '<tr><th>&nbsp;</th><th>Attribute name</th><th>Object type</th><th>Dictionary chapter</th></tr>';
+       echo '<tr><th>&nbsp;</th><th>Attribute name</th><th>Object type</th><th>Dictionary chapter</th><th>&nbsp;</th></tr>';
+       if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
+               printNewItemTR ($attrMap);
        foreach ($attrMap as $attr)
        {
                if (count ($attr['application']) == 0)
@@ -3414,28 +3765,8 @@ function renderEditAttrMapForm ()
                        echo "</td></tr>\n";
                }
        }
-       printOpFormIntro ('add');
-       echo '<tr><td>';
-       printImageHREF ('add', '', TRUE);
-       echo "</td><td><select name=attr_id>";
-       $shortType['uint'] = 'U';
-       $shortType['float'] = 'F';
-       $shortType['string'] = 'S';
-       $shortType['dict'] = 'D';
-       foreach ($attrMap as $attr)
-               echo "<option value=${attr['id']}>[" . $shortType[$attr['type']] . "] ${attr['name']}</option>";
-       echo "</select></td>";
-       echo '<td>';
-       printSelect (getObjectTypeList(), 'objtype_id');
-       echo '</td>';
-       $dict = getDict();
-       echo '<td><select name=chapter_no>';
-       foreach ($dict as $chapter)
-               if (!$chapter['sticky'])
-                       echo "<option value='${chapter['no']}'>${chapter['name']}</option>";
-       echo '</select></td>';
-       echo '</tr>';
-       echo '</form>';
+       if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
+               printNewItemTR ($attrMap);
        echo "</table>\n";
        finishPortlet();
 }
@@ -3451,7 +3782,7 @@ function printImageHREF ($tag, $title = '', $do_input = FALSE, $tabindex = 0)
                        "<input type=image name=submit class=icon " .
                        "src='${root}${img['path']}' " .
                        "border=0 " .
-                       ($tabindex ? '' : "tabindex=${tabindex}") .
+                       ($tabindex ? "tabindex=${tabindex}" : '') .
                        (empty ($title) ? '' : " title='${title}'") . // JT: Add title to input hrefs too
                        ">";
        else
@@ -3472,10 +3803,9 @@ function getFaviconURL ()
        return $root . 'pix/racktables.ico';
 }
 
-// FIXME: stack the report sections somehow, so they can register themselves.
 function renderSystemReports ()
 {
-       $systemreports = array
+       $tmp = array
        (
                array
                (
@@ -3484,31 +3814,19 @@ function renderSystemReports ()
                        'func' => 'getDictStats'
                ),
                array
-               (
-                       'title' => 'IPv4',
-                       'type' => 'counters',
-                       'func' => 'getIPv4Stats'
-               ),
-               array
                (
                        'title' => 'Rackspace',
                        'type' => 'counters',
                        'func' => 'getRackspaceStats'
                ),
                array
-               (
-                       'title' => 'RackCode',
-                       'type' => 'counters',
-                       'func' => 'getRackCodeStats'
-               ),
-               array
                (
                        'title' => 'Tags top-50',
                        'type' => 'custom',
                        'func' => 'renderTagStats'
                ),
        );
-       renderReports ($systemreports);
+       renderReports ($tmp);
 }
 
 function renderLocalReports ()
@@ -3517,6 +3835,46 @@ function renderLocalReports ()
        renderReports ($localreports);
 }
 
+function renderRackCodeReports ()
+{
+       $tmp = array
+       (
+               array
+               (
+                       'title' => 'Stats',
+                       'type' => 'counters',
+                       'func' => 'getRackCodeStats'
+               ),
+               array
+               (
+                       'title' => 'Warnings',
+                       'type' => 'messages',
+                       'func' => 'getRackCodeWarnings'
+               ),
+       );
+       renderReports ($tmp);
+}
+
+function renderIPv4Reports ()
+{
+       $tmp = array
+       (
+               array
+               (
+                       'title' => 'Stats',
+                       'type' => 'counters',
+                       'func' => 'getIPv4Stats'
+               ),
+               array
+               (
+                       'title' => 'Lost addresses',
+                       'type' => 'custom',
+                       'func' => 'getLostIPv4Addresses'
+               ),
+       );
+       renderReports ($tmp);
+}
+
 function renderReports ($what)
 {
        if (!count ($what))
@@ -3531,6 +3889,10 @@ function renderReports ($what)
                                foreach ($item['func'] () as $header => $data)
                                        echo "<tr><td class=tdright>${header}:</td><td class=tdleft>${data}</td></tr>\n";
                                break;
+                       case 'messages':
+                               foreach ($item['func'] () as $msg)
+                                       echo "<tr class='msg_${msg['class']}'><td class=tdright>${msg['header']}:</td><td class=tdleft>${msg['text']}</td></tr>\n";
+                               break;
                        case 'custom':
                                echo "<tr><td colspan=2>";
                                $item['func'] ();
@@ -3758,23 +4120,27 @@ function renderVLANMembership ($object_id = 0)
 
 function renderSNMPPortFinder ($object_id = 0)
 {
+       showMessageOrError();
        if ($object_id <= 0)
        {
                showError ('Invalid argument', __FUNCTION__);
                return;
        }
-// FIXME: check if SNMP PHP extension is available!
        printOpFormIntro ('querySNMPData');
-?>
-<p align=center>
-This asset has no ports listed, that's why you see this form. If you supply SNMP community,
-I can try automatic data harvesting on the asset. As soon as at least one port is added,
-this tab will not be seen any more. Good luck.<br>
-<input type=text name=community value='public'>
-<input type=submit name='do_scan' value='Go!'> 
-</form>
-</p>
-<?php
+       if (!extension_loaded ('snmp'))
+       {
+               echo "<div class=msg_error>The PHP SNMP extension is not loaded.  Cannot continue.</div>";
+       }
+       else
+       {
+               echo "<p align=center>
+This object has no ports listed, that's why you see this form. If you supply a SNMP community,
+I can try to automatically harvest the data. As soon as at least one port is added,
+this tab will not be seen any more. Good luck.<br>\n";
+               echo "<input type=text name=community value='public'>\n";
+               echo "<input type=submit name='do_scan' value='Go!'> \n";
+               echo "</form></p>\n";
+       }
 }
 
 function renderUIResetForm()
@@ -3785,27 +4151,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();
@@ -3845,16 +4190,16 @@ function renderVirtualService ($vsid)
        echo "<tr><th width='50%' class=tdright>Protocol:</th><td class=tdleft>${vsinfo['proto']}</td></tr>\n";
        echo "<tr><th width='50%' class=tdright>Virtual IP address:</th><td class=tdleft><a href='${root}?page=ipaddress&tab=default&ip=${vsinfo['vip']}'>${vsinfo['vip']}</a></td></tr>\n";
        echo "<tr><th width='50%' class=tdright>Virtual port:</th><td class=tdleft>${vsinfo['vport']}</td></tr>\n";
-       printTagTRs ("${root}?page=ipv4vslist&");
+       printTagTRs ("${root}?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 ();
@@ -3867,19 +4212,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
@@ -3890,14 +4232,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>';
                }
@@ -3911,11 +4252,12 @@ function renderVirtualService ($vsid)
        echo '</tr><table>';
 }
 
-function renderProgressBar ($percentage = 0)
+function renderProgressBar ($percentage = 0, $theme = '')
 {
        global $root;
        $done = ((int) ($percentage * 100));
-       echo "<img width=100 height=10 border=0 title='${done}%' src='${root}render_image.php?img=progressbar&done=${done}'>";
+       echo "<img width=100 height=10 border=0 title='${done}%' src='${root}render_image.php?img=progressbar&done=${done}";
+       echo (empty ($theme) ? '' : "&theme=${theme}") . "'>";
 }
 
 function renderRSPoolServerForm ($pool_id = 0)
@@ -3944,7 +4286,7 @@ function renderRSPoolServerForm ($pool_id = 0)
                        echo "</td><td><input type=text name=rsip value='${rs['rsip']}'></td>";
                        echo "<td><input type=text name=rsport size=5 value='${rs['rsport']}'></td>";
                        echo "<td><textarea name=rsconfig>${rs['rsconfig']}</textarea></td><td>";
-                       printImageHREF ('save', 'Save changes', TRUE);
+                       printImageHREF ('SAVE', 'Save changes', TRUE);
                        echo "</td></tr></form>\n";
                        $order = $nextorder[$order];
                }
@@ -3985,11 +4327,12 @@ function renderRSPoolServerForm ($pool_id = 0)
        echo "</td><td>Format: ";
        $formats = array
        (
+               'ssv_1' => 'SSV: &lt;IP address&gt;',
+               'ssv_2' => 'SSV: &lt;IP address&gt; &lt;port&gt;',
                'ipvs_2' => 'ipvsadm -l -n (address and port)',
                'ipvs_3' => 'ipvsadm -l -n (address, port and weight)',
-               'ssv_2' => 'SSV: &lt;IP address&gt; &lt;port&gt;'
        );
-       printSelect ($formats, 'format');
+       printSelect ($formats, 'format', 'ssv_1');
        echo "</td><td><input type=submit value=Parse></td></tr>\n";
        echo "<tr><td colspan=3><textarea name=rawtext cols=100 rows=50></textarea></td></tr>\n";
        echo "</table>\n";
@@ -4015,20 +4358,17 @@ function renderRSPoolLBForm ($pool_id = 0)
                foreach ($poolInfo['lblist'] as $object_id => $vslist)
                        foreach ($vslist as $vs_id => $configs)
                        {
-                               $oi = getObjectInfo ($object_id);
                                printOpFormIntro ('updLB', array ('vs_id' => $vs_id, 'object_id' => $object_id));
                                echo "<tr valign=top class=row_${order}><td><a href='${root}process.php?page=${pageno}&tab=${tabno}&op=delLB&pool_id=${pool_id}&object_id=${object_id}&vs_id=${vs_id}'>";
                                printImageHREF ('delete', 'Unconfigure');
                                echo "</a></td>";
-                               echo "<td class=tdleft><a href='${root}?page=object&object_id=${object_id}'>${oi['dname']}</a></td>";
-                               echo "<td class=tdleft><a href='${root}?page=ipv4vs&vs_id=${vs_id}'>";
-                               $vsinfo = getVServiceInfo ($vs_id);
-                               echo buildVServiceName ($vsinfo) . '</a>';
-                               if (!empty ($vsinfo['name']))
-                                       echo " (${vsinfo['name']})";
+                               echo "<td class=tdleft>";
+                               renderLBCell ($object_id);
+                               echo "</td><td class=tdleft>";
+                               renderVSCell ($vs_id);
                                echo "</td><td><textarea name=vsconfig>${configs['vsconfig']}</textarea></td>";
                                echo "<td><textarea name=rsconfig>${configs['rsconfig']}</textarea></td><td>";
-                               printImageHREF ('save', 'Save changes', TRUE);
+                               printImageHREF ('SAVE', 'Save changes', TRUE);
                                echo "</td></tr></form>\n";
                                $order = $nextorder[$order];
                        }
@@ -4044,12 +4384,12 @@ function renderRSPoolLBForm ($pool_id = 0)
                foreach (getNarrowObjectList ($type) as $object)
                        echo "<option value='${object['id']}'>${object['dname']}</option>";
        echo "</select> ";
-       printSelect ($vs_list, 'vs_id');
+       printSelect ($vs_list, 'vs_id', NULL, 2);
        echo "</td><td>";
-       printImageHREF ('add', 'Configure LB', TRUE, 2);
+       printImageHREF ('add', 'Configure LB', TRUE, 5);
        echo "</td></tr>\n";
-       echo "<tr><th>VS config</th><td colspan=2><textarea name=vsconfig rows=10 cols=80></textarea></td></tr>";
-       echo "<tr><th>RS config</th><td colspan=2><textarea name=rsconfig rows=10 cols=80></textarea></td></tr>";
+       echo "<tr><th>VS config</th><td colspan=2><textarea tabindex=3 name=vsconfig rows=10 cols=80></textarea></td></tr>";
+       echo "<tr><th>RS config</th><td colspan=2><textarea tabindex=4 name=rsconfig rows=10 cols=80></textarea></td></tr>";
        echo "</form></table>\n";
        finishPortlet();
 }
@@ -4069,16 +4409,17 @@ function renderVServiceLBForm ($vs_id = 0)
                foreach ($vsinfo['rspool'] as $pool_id => $rspinfo)
                        foreach ($rspinfo['lblist'] as $object_id => $configs)
                        {
-                               $oi = getObjectInfo ($object_id);
                                printOpFormIntro ('updLB', array ('pool_id' => $pool_id, 'object_id' => $object_id));
                                echo "<tr valign=top class=row_${order}><td><a href='${root}process.php?page=${pageno}&tab=${tabno}&op=delLB&pool_id=${pool_id}&object_id=${object_id}&vs_id=${vs_id}'>";
                                printImageHREF ('delete', 'Unconfigure');
                                echo "</a></td>";
-                               echo "<td class=tdleft><a href='${root}?page=object&object_id=${object_id}'>${oi['dname']}</a></td>";
-                               echo "<td class=tdleft><a href='${root}?page=ipv4rsp&pool_id=${pool_id}'>${rspinfo['name']}</a></td>";
-                               echo "<td><textarea name=vsconfig>${configs['vsconfig']}</textarea></td>";
+                               echo "<td class=tdleft>";
+                               renderLBCell ($object_id);
+                               echo "</td><td class=tdleft>";
+                               renderRSPoolCell ($pool_id, $rspinfo['name']);
+                               echo "</td><td><textarea name=vsconfig>${configs['vsconfig']}</textarea></td>";
                                echo "<td><textarea name=rsconfig>${configs['rsconfig']}</textarea></td><td>";
-                               printImageHREF ('save', 'Save changes', TRUE);
+                               printImageHREF ('SAVE', 'Save changes', TRUE);
                                echo "</td></tr></form>\n";
                                $order = $nextorder[$order];
                        }
@@ -4097,19 +4438,19 @@ function renderVServiceLBForm ($vs_id = 0)
                foreach (getNarrowObjectList ($type) as $object)
                        echo "<option value='${object['id']}'>${object['dname']}</option>";
        echo "</select> ";
-       printSelect ($rsplist, 'pool_id');
+       printSelect ($rsplist, 'pool_id', NULL, 2);
        echo "</td><td>";
-       printImageHREF ('add', 'Configure LB', TRUE, 2);
+       printImageHREF ('add', 'Configure LB', TRUE, 5);
        echo "</td></tr>\n";
-       echo "<tr><th>VS config</th><td colspan=2><textarea name=vsconfig rows=10 cols=80></textarea></td></tr>";
-       echo "<tr><th>RS config</th><td colspan=2><textarea name=rsconfig rows=10 cols=80></textarea></td></tr>";
+       echo "<tr><th>VS config</th><td colspan=2><textarea tabindex=3 name=vsconfig rows=10 cols=80></textarea></td></tr>";
+       echo "<tr><th>RS config</th><td colspan=2><textarea tabindex=4 name=rsconfig rows=10 cols=80></textarea></td></tr>";
        echo "</form></table>\n";
        finishPortlet();
 }
 
 function renderRSPool ($pool_id = 0)
 {
-       global $root;
+       global $root, $nextorder;
        if ($pool_id <= 0)
        {
                showError ('Invalid pool_id', __FUNCTION__);
@@ -4127,29 +4468,40 @@ function renderRSPool ($pool_id = 0)
                echo "<tr><td colspan=2 align=center><h1>{$poolInfo['name']}</h1></td></tr>";
        echo "<tr><td class=pcleft>\n";
 
-       startPortlet ('Configuration');
+       startPortlet ('Summary');
        echo "<table border=0 cellspacing=0 cellpadding=3 width='100%'>\n";
        if (!empty ($poolInfo['name']))
                echo "<tr><th width='50%' class=tdright>Pool name:</th><td class=tdleft>${poolInfo['name']}</td></tr>\n";
        echo "<tr><th width='50%' class=tdright>Real servers:</th><td class=tdleft>" . count ($poolInfo['rslist']) . "</td></tr>\n";
        echo "<tr><th width='50%' class=tdright>Load balancers:</th><td class=tdleft>" . count ($poolInfo['lblist']) . "</td></tr>\n";
-       printTagTRs ("${root}?page=ipv4rsplist&");
+       printTagTRs ("${root}?page=ipv4rsplist&tab=default&");
+       if (!empty ($poolInfo['vsconfig']))
+       {
+               echo "<tr><th width='50%' class=tdright>VS configuration:</th><td>&nbsp;</td></tr>\n";
+               echo "<tr><td colspan=2 class='dashed slbconf'>${poolInfo['vsconfig']}</td></tr>\n";
+       }
+       if (!empty ($poolInfo['rsconfig']))
+       {
+               echo "<tr><th width='50%' class=tdright>RS configuration:</th><td>&nbsp;</td></tr>\n";
+               echo "<tr><td colspan=2 class='dashed slbconf'>${poolInfo['rsconfig']}</td></tr>\n";
+       }
        echo "</table>";
        finishPortlet();
 
        startPortlet ('Load balancers (' . count ($poolInfo['lblist']) . ')');
        echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
        echo "<tr><th>VS</th><th>LB</th><th>VS config</th><th>RS config</th></tr>";
+       $order = 'odd';
        foreach ($poolInfo['lblist'] as $object_id => $vslist)
                foreach ($vslist as $vs_id => $configs)
        {
-               $oi = getObjectInfo ($object_id);
-               $vi = getVServiceInfo ($vs_id);
-               echo "<tr valign=top><td class=tdleft><a href='${root}?page=ipv4vs&vs_id=${vs_id}'>";
-               echo buildVServiceName ($vi);
-               echo "</a></td><td class=tdleft><a href='${root}?page=object&object_id=${object_id}'>${oi['dname']}</a></td>";
-               echo "<td class=tdleft><pre>${configs['vsconfig']}</pre></td>";
-               echo "<td class=tdleft><pre>${configs['rsconfig']}</pre></td></tr>\n";
+               echo "<tr valign=top class=row_${order}><td class=tdleft><a href='${root}?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();
@@ -4167,7 +4519,7 @@ function renderRSPool ($pool_id = 0)
                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 class=tdleft>${rs['rsport']}</td><td class=slbconf>${rs['rsconfig']}</td></tr>\n";
        }
        echo "</table>\n";
        finishPortlet();
@@ -4189,16 +4541,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];
        }
@@ -4266,7 +4612,7 @@ function renderVSListEditForm ()
                echo "<td class=tdleft><input type=text name=name value='${vsinfo['name']}'></td>";
                echo "<td><textarea name=vsconfig>${vsinfo['vsconfig']}</textarea></td>";
                echo "<td><textarea name=rsconfig>${vsinfo['rsconfig']}</textarea></td><td>";
-               printImageHREF ('save', 'save changes', TRUE);
+               printImageHREF ('SAVE', 'save changes', TRUE);
                echo "</td></tr></form>\n";
                $order = $nextorder[$order];
        }
@@ -4294,7 +4640,7 @@ 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='${root}?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))
                {
@@ -4379,7 +4725,7 @@ 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='${root}?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')
@@ -4453,7 +4799,8 @@ function renderLivePTR ($id = 0)
                $page=0;
        global $root, $pageno, $tabno;
        $maxperpage = getConfigVar ('IPV4_ADDRS_PER_PAGE');
-       $range = getIPv4Network ($id);
+       $range = getIPv4NetworkInfo ($id);
+       loadIPv4AddrList ($range);
        echo "<center><h1>${range['ip']}/${range['mask']}</h1><h2>${range['name']}</h2></center>\n";
 
        echo "<table class=objview border=0 width='100%'><tr><td class=pcleft>";
@@ -4577,11 +4924,13 @@ function renderAutoPortsForm ($object_id = 0)
 
 function renderTagRowForViewer ($taginfo, $level = 0)
 {
-       echo '<tr><td align=left>';
-       echo '<div title="id = ' . $taginfo['id'] . '">';
-       for ($i = 0; $i < $level; $i++)
-               printImageHREF ('spacer');
-       echo $taginfo['tag'] . '</div>';
+       if (!count ($taginfo['kids']))
+               $level++; // Shift instead of placing a spacer. This won't impact any nested nodes.
+       echo "<tr><td align=left style='padding-left: " . ($level * 16) . "px;'>";
+       if (count ($taginfo['kids']))
+               printImageHREF ('node-expanded-static');
+       echo '<span title="id = ' . $taginfo['id'] . '">';
+       echo $taginfo['tag'] . '</span>';
        echo "</td></tr>\n";
        foreach ($taginfo['kids'] as $kid)
                renderTagRowForViewer ($kid, $level + 1);
@@ -4591,9 +4940,7 @@ function renderTagRowForViewer ($taginfo, $level = 0)
 function renderTagRowForCloud ($taginfo, $realm, $level = 0)
 {
        global $root;
-       echo '<tr><td align=left>';
-       for ($i = 0; $i < $level; $i++)
-               printImageHREF ('spacer');
+       echo "<tr><td align=left style='padding-left: " . ($level * 16) . "px;'>";
        echo "<a href='${root}?page=objgroup&group_id=0&tagfilter[]=${taginfo['id']}'>";
        echo $taginfo['tag'] . '</a>';
        if (isset ($taginfo['refcnt'][$realm]))
@@ -4606,9 +4953,11 @@ function renderTagRowForCloud ($taginfo, $realm, $level = 0)
 function renderTagRowForEditor ($taginfo, $level = 0)
 {
        global $root, $pageno, $tabno, $taglist;
-       echo '<tr><td class=tdleft>';
-       for ($i = 0; $i < $level; $i++)
-               printImageHREF ('spacer');
+       if (!count ($taginfo['kids']))
+               $level++; // Idem
+       echo "<tr><td align=left style='padding-left: " . ($level * 16) . "px;'>";
+       if (count ($taginfo['kids']))
+               printImageHREF ('node-expanded-static');
        $nrefs = 0;
        foreach ($taginfo['refcnt'] as $part)
                $nrefs += $part;
@@ -4665,49 +5014,58 @@ function renderTagCloud ($realm = '')
 
 function renderTagTreeEditor ()
 {
+       function printNewItemTR ()
+       {
+               global $taglist;
+               printOpFormIntro ('createTag');
+               echo "<tr><td class=tdleft>";
+               printImageHREF ('add', '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);
+               echo "</td></tr></form>\n";
+       }
        global $taglist, $tagtree;
        showMessageOrError();
-       echo "<table class=objview border=0 width='100%'><tr><td class=pcleft>";
-       startPortlet ('tag tree');
-       echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
-       echo "<tr><th>&nbsp;</th><th>tag</th><th>parent</th><th>&nbsp;</th></tr>\n";
-       foreach ($tagtree as $taginfo)
-               renderTagRowForEditor ($taginfo);
-       printOpFormIntro ('createTag');
-       echo "<tr><td class=tdleft>";
-       printImageHREF ('grant', 'Create tag', TRUE, 102);
-       echo '</td><td><input type=text name=tag_name tabindex=100></td><td><select name=parent_id tabindex=101>';
-       echo "<option value=0>-- NONE --</option>\n";
-       foreach ($taglist as $taginfo)
-               echo "<option value=${taginfo['id']}>${taginfo['tag']}</option>";
-       echo "</select></td><td>&nbsp;</td></tr>";
-       echo "</form>\n";
-       echo '</table>';
-       finishPortlet();
-
-       echo "</td><td><td class=pcright>";
 
-       startPortlet ('fallen leaves');
-       echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
-       echo "<tr><th>tag</th><th>parent</th><th>&nbsp;</th></tr>\n";
-       foreach (getOrphanedTags() as $taginfo)
+       $otags = getOrphanedTags();
+       if (count ($otags))
        {
-               echo '<tr><td>';
-               printOpFormIntro ('updateTag', array ('tag_id' => $taginfo['id'], 'tag_name' => $taginfo['tag']));
-               echo "${taginfo['tag']}</td><td><select name=parent_id>";
-               echo "<option value=0>-- NONE --</option>\n";
-               foreach ($taglist as $tlinfo)
+               startPortlet ('fallen leaves');
+               echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
+               echo "<tr><th>tag</th><th>parent</th><th>&nbsp;</th></tr>\n";
+               foreach ($otags as $taginfo)
                {
-                       echo "<option value=${tlinfo['id']}" . ($tlinfo['id'] == $taglist[$taginfo['id']]['parent_id'] ? ' selected' : '');
-                       echo ">${tlinfo['tag']}</option>";
+                       printOpFormIntro ('updateTag', array ('tag_id' => $taginfo['id'], 'tag_name' => $taginfo['tag']));
+                       echo "<tr><td>${taginfo['tag']}</td><td><select name=parent_id>";
+                       echo "<option value=0>-- NONE --</option>\n";
+                       foreach ($taglist as $tlinfo)
+                       {
+                               echo "<option value=${tlinfo['id']}" . ($tlinfo['id'] == $taglist[$taginfo['id']]['parent_id'] ? ' selected' : '');
+                               echo ">${tlinfo['tag']}</option>";
+                       }
+                       echo "</select></td><td>";
+                       printImageHREF ('save', 'Save changes', TRUE);
+                       echo "</form></td></tr>\n";
                }
-               echo "</select></td><td>";
-               printImageHREF ('save', 'Save changes', TRUE);
-               echo "</form></td></tr>\n";
+               echo '</table>';
+               finishPortlet();
        }
+
+       startPortlet ('tag tree');
+       echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
+       echo "<tr><th>&nbsp;</th><th>tag</th><th>parent</th><th>&nbsp;</th></tr>\n";
+       if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
+               printNewItemTR();
+       foreach ($tagtree as $taginfo)
+               renderTagRowForEditor ($taginfo);
+       if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
+               printNewItemTR();
        echo '</table>';
        finishPortlet();
-       echo "</td></tr></table>";
 }
 
 // Output a sequence of OPTION elements, selecting those, which are present on the
@@ -4737,6 +5095,11 @@ function renderTagOptionForFilter ($taginfo, $tagfilter, $realm, $level = 0)
                renderTagOptionForFilter ($kid, $tagfilter, $realm, $level + 1);
 }
 
+function renderFileTags ($id)
+{
+       renderEntityTagChainEditor ('file', 'file_id', $id);
+}
+
 function renderObjectTags ($id)
 {
        renderEntityTagChainEditor ('object', 'object_id', $id);
@@ -4776,9 +5139,11 @@ function renderEntityTagChainEditor ($entity_realm = '', $bypass_name, $entity_i
                return;
        }
        showMessageOrError();
-       startPortlet ('Tag list (' . count ($expl_tags) . ')');
+       startPortlet ('Tag list');
+       if (count ($expl_tags))
+               echo '<h3>(' . serializeTags ($expl_tags) . ')</h3>';
        echo '<table border=0 align=center>';
-       echo '<tr><td colspan=2><h3>hold Ctrl to select more, than one</h3></td></tr>';
+       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)
@@ -4797,17 +5162,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";
        }
 }
@@ -4833,19 +5198,21 @@ function renderTagFilterPortlet ($tagfilter, $realm, $bypass_name = '', $bypass_
        }
        echo '<table border=0 align=center>';
 
+       if (count ($tagfilter))
+               echo '<h3>(' . serializeTags (buildTagChainFromIds ($tagfilter)) . ')</h3>';
        echo "<form method=get>\n";
        echo "<input type=hidden name=page value=${pageno}>\n";
        echo "<input type=hidden name=tab value=${tabno}>\n";
        if ($bypass_name != '')
                echo "<input type=hidden name=${bypass_name} value='${bypass_value}'>\n";
-       echo '<tr><td colspan=2><select name=tagfilter[] multiple>';
+       echo '<tr><td colspan=2><select name=tagfilter[] multiple size=' . getConfigVar ('MAXSELSIZE') . '>';
        foreach ($objectivetags as $taginfo)
-               renderTagOptionForFilter ($taginfo, $tagfilter, $realm);
+               renderTagOptionForFilter ($taginfo, complementByKids ($tagfilter), $realm);
        echo '</select></td></tr><tr><td>';
 //     $tfmode = getTFMode();
 //     echo '<input type=radio name=tfmode value=all' . ($tfmode == 'all' ? ' checked' : '') . '>all ';
 //     echo '<input type=radio name=tfmode value=any' . ($tfmode == 'any' ? ' checked' : '') . '>any ';
-       printImageHREF ('save', 'apply', TRUE);
+       printImageHREF ('apply', 'Apply filter', TRUE);
        echo "</form></td><td>";
 
        // "reset"
@@ -4854,7 +5221,6 @@ function renderTagFilterPortlet ($tagfilter, $realm, $bypass_name = '', $bypass_
        echo "<input type=hidden name=tab value=${tabno}>\n";
        if ($bypass_name != '')
                echo "<input type=hidden name=${bypass_name} value='${bypass_value}'>\n";
-       echo "<input type=hidden name=tagfilter[] value=''>";
        printImageHREF ('clear', 'reset', TRUE);
        echo '</form></td></tr></table>';
        finishPortlet();
@@ -4869,7 +5235,7 @@ function renderTagSelect ()
                echo "No tags defined";
                return;
        }
-       echo '<select name=taglist[] multiple>';
+       echo '<select name=taglist[] multiple size=' . getConfigVar ('MAXSELSIZE') . '>';
        foreach ($tagtree as $taginfo)
                renderTagOption ($taginfo);
        echo '</select><br>';
@@ -4908,14 +5274,14 @@ function renderObjectSLB ($object_id)
        echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
        printOpFormIntro ('addLB');
        echo "<tr valign=top><th>VS / RS pool</th><td class=tdleft>";
-       printSelect ($vs_list, 'vs_id');
+       printSelect ($vs_list, 'vs_id', NULL, 1);
        echo "</td><td>";
-       printSelect ($rsplist, 'pool_id');
+       printSelect ($rsplist, 'pool_id', NULL, 2);
        echo "</td><td>";
-       printImageHREF ('add', 'Configure LB', TRUE, 2);
+       printImageHREF ('add', 'Configure LB', TRUE, 5);
        echo "</td></tr>\n";
-       echo "<tr><th>VS config</th><td colspan=2><textarea name=vsconfig rows=10 cols=80></textarea></td></tr>";
-       echo "<tr><th>RS config</th><td colspan=2><textarea name=rsconfig rows=10 cols=80></textarea></td></tr>";
+       echo "<tr><th>VS config</th><td colspan=2><textarea tabindex=3 name=vsconfig rows=10 cols=80></textarea></td></tr>";
+       echo "<tr><th>RS config</th><td colspan=2><textarea tabindex=4 name=rsconfig rows=10 cols=80></textarea></td></tr>";
        echo "</form></table>\n";
        finishPortlet();
 
@@ -4932,14 +5298,13 @@ function renderObjectSLB ($object_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}'>";
                        printImageHREF ('delete', 'Unconfigure');
                        echo "</a></td>";
-                       echo "</td><td class=tdleft><a href='${root}?page=ipv4vs&vs_id=${vs_id}'>";
-                       echo buildVServiceName ($vsinfo) . "</a>";
-                       if (!empty ($vsinfo['name']))
-                               echo '<br>' . $vsinfo['name'];
-                       echo "</td><td class=tdleft>" . $rsplist[$vsinfo['pool_id']] . "</td>";
-                       echo "<td><textarea name=vsconfig>${vsinfo['vsconfig']}</textarea></td>";
+                       echo "</td><td class=tdleft>";
+                       renderVSCell ($vs_id);
+                       echo "</td><td class=tdleft>";
+                       renderRSPoolCell ($vsinfo['pool_id'], $rsplist[$vsinfo['pool_id']]);
+                       echo "</td><td><textarea name=vsconfig>${vsinfo['vsconfig']}</textarea></td>";
                        echo "<td><textarea name=rsconfig>${vsinfo['rsconfig']}</textarea></td><td>";
-                       printImageHREF ('save', 'Save changes', TRUE);
+                       printImageHREF ('SAVE', 'Save changes', TRUE);
                        echo "</td></tr></form>\n";
                        $order = $nextorder[$order];
                }
@@ -4969,31 +5334,38 @@ function renderEditVService ($vsid)
        $vsinfo = getVServiceInfo ($vsid);
        printOpFormIntro ('updIPv4VS');
        echo '<table border=0 align=center>';
-       echo "<tr><th class=tdright>VIP:</th><td class=tdleft><input type=text name=vip value='${vsinfo['vip']}'></td></tr>\n";
-       echo "<tr><th class=tdright>port:</th><td class=tdleft><input type=text name=vport value='${vsinfo['vport']}'></td></tr>\n";
+       echo "<tr><th class=tdright>VIP:</th><td class=tdleft><input tabindex=1 type=text name=vip value='${vsinfo['vip']}'></td></tr>\n";
+       echo "<tr><th class=tdright>port:</th><td class=tdleft><input tabindex=2 type=text name=vport value='${vsinfo['vport']}'></td></tr>\n";
        echo "<tr><th class=tdright>proto:</th><td class=tdleft>";
        printSelect (array ('TCP' => 'TCP', 'UDP' => 'UDP'), 'proto', $vsinfo['proto']);
        echo "</td></tr>\n";
-       echo "<tr><th class=tdright>name:</th><td class=tdleft><input type=text name=name value='${vsinfo['name']}'></td></tr>\n";
-       echo "<tr><th class=tdright>VS config:</th><td class=tdleft><textarea name=vsconfig rows=20 cols=80>${vsinfo['vsconfig']}</textarea></td></tr>\n";
-       echo "<tr><th class=tdright>RS config:</th><td class=tdleft><textarea name=rsconfig rows=20 cols=80>${vsinfo['rsconfig']}</textarea></td></tr>\n";
+       echo "<tr><th class=tdright>name:</th><td class=tdleft><input tabindex=4 type=text name=name value='${vsinfo['name']}'></td></tr>\n";
+       echo "<tr><th class=tdright>VS config:</th><td class=tdleft><textarea tabindex=5 name=vsconfig rows=20 cols=80>${vsinfo['vsconfig']}</textarea></td></tr>\n";
+       echo "<tr><th class=tdright>RS config:</th><td class=tdleft><textarea tabindex=6 name=rsconfig rows=20 cols=80>${vsinfo['rsconfig']}</textarea></td></tr>\n";
        echo "<tr><th class=submit colspan=2>";
-       printImageHREF ('SAVE', 'Save changes', TRUE);
+       printImageHREF ('SAVE', 'Save changes', TRUE, 7);
        echo "</td></tr>\n";
        echo "</table></form>\n";
 }
 
 function dump ($var)
 {
-       echo '<pre>';
+       echo '<div align=left><pre>';
        print_r ($var);
-       echo '</pre>';
+       echo '</pre></div>';
 }
 
 function renderRackCodeViewer ()
 {
        $text = loadScript ('RackCode');
-       dump ($text);
+       echo '<table width="100%" border=0>';
+       $lineno = 1;
+       foreach (explode ("\n", $text) as $line)
+       {
+               echo "<tr><td class=tdright><a name=line${lineno}>${lineno}</a></td>";
+               echo "<td class=tdleft>${line}</td></tr>";
+               $lineno++;
+       }
 }
 
 function renderRackCodeEditor ()
@@ -5002,9 +5374,11 @@ function renderRackCodeEditor ()
        showMessageOrError();
        printOpFormIntro ('saveRackCode');
        echo '<table border=0 align=center>';
-       echo "<tr><td><textarea rows=50 cols=80 name=rackcode>" . $text . "</textarea></td></tr>\n";
+       echo "<tr><td><textarea rows=40 cols=100 name=rackcode id=RCTA class='codepress rackcode'>";
+       echo $text . "</textarea></td></tr>\n";
        echo "<tr><td align=center>";
-       printImageHREF ('SAVE', 'Save changes', TRUE);
+       echo "<input type='submit' value='Save' onclick='RCTA.toggleEditor();'>";
+//     printImageHREF ('SAVE', 'Save changes', TRUE);
        echo "</td></tr>";
        echo '</table>';
        echo "</form>";
@@ -5012,12 +5386,14 @@ function renderRackCodeEditor ()
 
 function renderUser ($user_id)
 {
-       global $accounts, $expl_tags, $impl_tags;
+       global $accounts, $expl_tags, $impl_tags, $root;
        $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>';
+       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>';
+       echo '<tr><th class=tdright>Enabled:</th><td class=tdleft>';
        // This is weird, some other image titles have to be used.
        if ($accounts[$username]['user_enabled'] == 'yes')
                printImageHREF ('blockuser', 'enabled');
@@ -5027,23 +5403,27 @@ function renderUser ($user_id)
        // 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&";
        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 ()
@@ -5075,16 +5455,16 @@ function renderAccessDenied ()
        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 "<tr><th width='50%' class=tdright><span class=tagheader>This user tags</span>:</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 "</table>\n";
        echo "</body></html>";
 }
@@ -5098,37 +5478,356 @@ function renderMyAccount ()
        echo "</table>";
 }
 
+// File-related functions
+function renderFileSpace ()
+{
+       global $root, $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='${root}?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 $root, $nextorder, $aac;
+       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']));
+       printTagTRs ("${root}?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='${root}?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";
+}
+
+// Used for uploading a parentless file
+function renderFileUploadForm ()
+{
+       global $aat, $root, $pageno, $tabno, $nextorder;
+
+       showMessageOrError();
+       startPortlet ('Files');
+       echo "<table border=0 cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
+       echo "<tr><th>&nbsp;</th><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><td>";
+       printImageHREF ('add', 'Upload file', TRUE, 99);
+       echo "</td>";
+       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 ('add', 'Upload file', TRUE, 99);
+       echo '</td></tr></form>';
+       echo "</table><br>\n";
+       finishPortlet();
+}
+
+function renderFilesByLink ()
+{
+       global $root, $pageno, $tabno, $nextorder, $taglist, $tagtree;
+       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='${root}?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);
+               printf("<td class='tdleft'><a href='%/?page=file&file_id=%s'><strong>%s</strong></a>", $root, $file['id'], $file['name']);
+               $tags = loadEntityTags ('file', $file['id']);
+               if (count ($tags))
+                       echo '<br><small>' . serializeTags ($tags, "${root}?page=${pageno}&tab=default&parent_id=${parent_id}&") . '</small>';
+               printf("</td><td class='tdleft'>%s</td>", $file['comment']);
+               printf("<td class='tdleft'>%s</td>", formatFileSize($file['size']));
+               printf("<td class='tdleft'>%s</td>", formatTimestamp($file['ctime']));
+               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='${root}process.php?op=deleteFile&page=${pageno}&tab=${tabno}&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');
+               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)
+               {
+                       printf("<td class=tdleft><a href='%/?page=file&file_id=%s'><strong>%s</strong></a></td>", $root, $file_id, $file['name']);
+                       printf("<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>';
+               }
+               echo "</table><br>\n";
+               finishPortlet();
+       }
+}
+
+function renderFilesForEntity ($entity_type = NULL, $id_name = NULL, $entity_id = 0)
+{
+       global $root, $pageno, $tabno;
+       if ($entity_type == NULL || $id_name == NULL || $entity_id <= 0)
+       {
+               showError ('Invalid entity info', __FUNCTION__);
+               return;
+       }
+
+       showMessageOrError();
+       startPortlet ('Upload new');
+       echo "<table border=0 cellspacing=0 cellpadding='5' align='center'>\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)
+               {
+                       printOpFormIntro ('updateFile', array ('file_id' => $file_id, 'link_id' => $file['link_id'], 'name' => $file['name']));
+                       echo "<tr valign=top><td><a href='${root}process.php?op=deleteFile&page=${pageno}&tab=${tabno}&file_id=${file_id}&${id_name}=${entity_id}&name=${file['name']}'>";
+                       printImageHREF ('delete', 'Unlink and delete file');
+                       echo '</a></td>';
+                       printf("<td class='tdleft'><a href='%/?page=file&file_id=%s'><strong>%s</strong></a>", $root, $file_id, $file['name']);
+                       echo "<td class=tdleft><input type='text' name='comment' value='${file['comment']}' size=15></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='${root}process.php?op=unlinkFile&page=${pageno}&tab=${tabno}&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();
+       }
+}
+
+// Set of wrapper functions to reduce duplicate code
+// No data validation, that is done by renderFilesForEntity ()
+function renderFilesForObject ($object_id) {
+       renderFilesForEntity('object', 'object_id', $object_id);
+}
+
+function renderFilesForIPv4Network ($range_id) {
+       renderFilesForEntity('ipv4net', 'id', $range_id);
+}
+
+function renderFilesForRack ($rack_id) {
+       renderFilesForEntity('rack', 'rack_id', $rack_id);
+}
+
+function renderFilesForRSPool ($pool_id) {
+       renderFilesForEntity('ipv4rspool', 'pool_id', $pool_id);
+}
+
+function renderFilesForVService ($vs_id) {
+       renderFilesForEntity('ipv4vs', 'vs_id', $vs_id);
+}
+
+function renderFilesForUser ($user_id) {
+       renderFilesForEntity('user', 'user_id', $user_id);
+}
+
 // 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 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, and append "cut here" indicator.
+// 2. Cut strings, which are too long, append "cut here" indicator and provide a mouse hint.
 function niftyString ($string, $maxlen = 30)
 {
        $cutind = '&hellip;'; // length is 1
        if (empty ($string))
                return '&nbsp;';
        if (mb_strlen ($string) > $maxlen)
-               return mb_substr ($string, 0, $maxlen - 1) . $cutind;
+               return "<span title='" . htmlspecialchars ($string, ENT_QUOTES, 'UTF-8') . "'>" .
+                       str_replace (' ', '&nbsp;', str_replace ("\t", ' ', mb_substr ($string, 0, $maxlen - 1))) . $cutind . '</span>';
        return $string;
 }
 
 // Iterate over what findRouters() returned and output some text suitable for a TD element.
-// Providing a quasi-static (external) tag cache is advised for calling functions.
-function printRoutersTD ($rlist, &$tagcache = array())
+function printRoutersTD ($rlist)
 {
        global $root;
-       $delim = '';
        $rtrclass = 'tdleft';
        foreach ($rlist as $rtr)
        {
@@ -5141,35 +5840,102 @@ function printRoutersTD ($rlist, &$tagcache = array())
        }
        echo "<td class='${rtrclass}'>";
        foreach ($rlist as $rtr)
-       {
-               echo $delim . $rtr['addr'] . ' ';
-               echo "<a href='${root}?page=object&object_id=${rtr['id']}&hl_ipv4_addr=${rtr['addr']}'>";
-               if (!empty ($rtr['iface']))
-                       echo $rtr['iface'] . '@';
-               echo $rtr['dname'] . '</a>';
-               if (!isset ($tagcache[$rtr['id']]))
-                       $tagcache[$rtr['id']] = loadRackObjectTags ($rtr['id']);
-               if (count ($tagcache[$rtr['id']]))
-               {
-                       echo '<br><small>';
-                       echo serializeTags ($tagcache[$rtr['id']], "${root}?page=objgroup&group_id=0&tab=default&");
-                       echo '</small>';
-               }
-               $delim = '<br><hr>';
-       }
+               renderRouterCell ($rtr['addr'], $rtr['iface'], $rtr['id'], $rtr['dname']);
        echo '</td>';
 }
 
 // Same as for routers, but produce two TD cells to lay the content out better.
-function printIPv4NetInfoTDs ($netinfo, $tdclass = 'tdleft')
+function printIPv4NetInfoTDs ($netinfo, $tdclass = 'tdleft', $indent = 0, $symbol = 'spacer', $symbolurl = '')
+{
+       global $root;
+       $tags = isset ($netinfo['id']) ? loadIPv4PrefixTags ($netinfo['id']) : array();
+       if ($symbol == 'spacer')
+       {
+               $indent++;
+               $symbol = '';
+       }
+       echo "<td class='${tdclass}' style='padding-left: " . ($indent * 16) . "px;'>";
+       if (!empty ($symbol))
+       {
+               if (!empty ($symbolurl))
+                       echo "<a href='${symbolurl}'>";
+               printImageHREF ($symbol, $symbolurl);
+               if (!empty ($symbolurl))
+                       echo '</a>';
+       }
+       if (isset ($netinfo['id']))
+               echo "<a href='${root}?page=ipv4net&id=${netinfo['id']}'>";
+       echo "${netinfo['ip']}/${netinfo['mask']}";
+       if (isset ($netinfo['id']))
+               echo '</a>';
+       echo "</td><td class='${tdclass}'>";
+       if (!isset ($netinfo['id']))
+               printImageHREF ('dragons', 'Here be dragons.');
+       else
+       {
+               echo niftyString ($netinfo['name']);
+               if (count ($tags))
+                       echo '<br><small>' . serializeTags ($tags, "${root}?page=ipv4space&tab=default&") . '</small>';
+       }
+       echo "</td>";
+}
+
+function renderLBCell ($object_id)
+{
+       global $root;
+       $oi = getObjectInfo ($object_id);
+       echo "<table class=slbcell><tr><td>";
+       echo "<a href='${root}?page=object&object_id=${object_id}'>${oi['dname']}</a>";
+       echo "</td></tr><tr><td>";
+       printImageHREF ('LB');
+       echo "</td></tr><tr><td><small>";
+       echo serializeTags (loadRackObjectTags ($object_id));
+       echo "</small></td></tr></table>";
+}
+
+function renderRSPoolCell ($pool_id, $pool_name)
+{
+       global $root;
+       echo "<table class=slbcell><tr><td>";
+       echo "<a href='${root}?page=ipv4rspool&pool_id=${pool_id}'>";
+       echo empty ($pool_name) ? "ANONYMOUS pool [${pool_id}]" : $pool_name;
+       echo "</a></td></tr><tr><td>";
+       printImageHREF ('RS pool');
+       echo "</td></tr><tr><td><small>";
+       echo serializeTags (loadIPv4RSPoolTags ($pool_id));
+       echo "</small></td></tr></table>";
+}
+
+function renderVSCell ($vs_id)
+{
+       global $root;
+       $oinfo = getVServiceInfo ($vs_id);
+       echo "<table class='slbcell vscell'><tr><td rowspan=3>";
+       printImageHREF ('VS');
+       echo "</td><td>";
+       echo "<a href='${root}?page=ipv4vs&vs_id=${vs_id}'>";
+       echo buildVServiceName ($oinfo);
+       echo "</a></td></tr><tr><td>";
+       echo $oinfo['name'];
+       echo '</td></tr><tr><td>';
+       $tags = loadIPv4VSTags ($vs_id);
+       echo count ($tags) ? ("<small>" . serializeTags ($tags) . "</small>") : '&nbsp;';
+       echo "</td></tr></table>";
+}
+
+function renderRouterCell ($dottedquad, $ifname, $object_id, $object_dname)
 {
        global $root;
-       $tags = loadIPv4PrefixTags ($netinfo['id']);
-       echo "<td class=${tdclass}><a href='${root}?page=iprange&id=${netinfo['id']}'>${netinfo['ip']}/${netinfo['mask']}</a></td>";
-       echo "<td class=${tdclass}>" . niftyString ($netinfo['name']);
-       if (count ($tags))
-               echo '<br><small>' . serializeTags ($tags, "${root}?page=ipv4space&tab=default&") . '</small>';
+       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>";
 }
 
 ?>