r2345 - don't allow editing/deleting stock records in dictionary editor
[racktables] / inc / interface.php
index f95de44..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,12 +63,18 @@ $image['config']['height'] = 200;
 $image['reports']['path'] = 'pix/report.png';
 $image['reports']['width'] = 218;
 $image['reports']['height'] = 200;
+$image['download']['path'] = 'pix/download.png';
+$image['download']['width'] = 16;
+$image['download']['height'] = 16;
 $image['link']['path'] = 'pix/tango-network-wired.png';
 $image['link']['width'] = 16;
 $image['link']['height'] = 16;
 $image['add']['path'] = 'pix/tango-list-add.png';
 $image['add']['width'] = 16;
 $image['add']['height'] = 16;
+$image['ADD']['path'] = 'pix/tango-list-add-big.png';
+$image['ADD']['width'] = 32;
+$image['ADD']['height'] = 32;
 $image['delete']['path'] = 'pix/tango-list-remove.png';
 $image['delete']['width'] = 16;
 $image['delete']['height'] = 16;
@@ -124,6 +134,27 @@ $image['node-expanded-static']['height'] = 16;
 $image['dragons']['path'] = 'pix/mitsudragon.png';
 $image['dragons']['width'] = 125;
 $image['dragons']['height'] = 21;
+$image['LB']['path'] = 'pix/loadbalancer.png';
+$image['LB']['width'] = 32;
+$image['LB']['height'] = 32;
+$image['RS pool']['path'] = 'pix/serverpool.png';
+$image['RS pool']['width'] = 48;
+$image['RS pool']['height'] = 16;
+$image['VS']['path'] = 'pix/servicesign.png';
+$image['VS']['width'] = 39;
+$image['VS']['height'] = 62;
+$image['router']['path'] = 'pix/router.png';
+$image['router']['width'] = 32;
+$image['router']['height'] = 32;
+$image['LINK']['path'] = 'pix/tango-emblem-symbolic-link-big.png';
+$image['LINK']['width'] = 32;
+$image['LINK']['height'] = 32;
+$image['favorite']['path'] = 'pix/tango-emblem-favorite.png';
+$image['favorite']['width'] = 16;
+$image['favorite']['height'] = 16;
+$image['computer']['path'] = 'pix/tango-computer.png';
+$image['computer']['width'] = 16;
+$image['computer']['height'] = 16;
 
 // This may be populated later onsite, report rendering function will use it.
 // See the $systemreport for structure.
@@ -149,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>
@@ -168,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>
@@ -213,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)
@@ -319,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']))
@@ -427,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";
@@ -446,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']))
+       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)
                {
-                       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 "<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().
@@ -572,7 +626,7 @@ function printSelect ($rowList, $select_name, $selected_id = NULL, $tabindex = N
                foreach ($other as $dict_key => $dict_value)
                {
                        echo "<option value=${dict_key}";
-                       if ($dict_key === $selected_id)
+                       if ($dict_key == $selected_id)
                                echo ' selected';
                        echo ">${dict_value}</option>";
                }
@@ -585,7 +639,7 @@ function printSelect ($rowList, $select_name, $selected_id = NULL, $tabindex = N
                        foreach ($groupdata as $dict_key => $dict_value)
                        {
                                echo "<option value=${dict_key}";
-                               if ($dict_key === $selected_id)
+                               if ($dict_key == $selected_id)
                                        echo ' selected';
                                echo ">${dict_value}</option>";
                        }
@@ -597,7 +651,7 @@ function printSelect ($rowList, $select_name, $selected_id = NULL, $tabindex = N
                        foreach ($other as $dict_key => $dict_value)
                        {
                                echo "<option value=${dict_key}";
-                               if ($dict_key === $selected_id)
+                               if ($dict_key == $selected_id)
                                        echo ' selected';
                                echo ">${dict_value}</option>";
                        }
@@ -764,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";
+                       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();
 
@@ -776,6 +831,8 @@ function renderRackObject ($object_id = 0)
                finishPortlet ();
        }
 
+       renderFilesPortlet ('object', $object_id);
+
        $ports = getObjectPortsAndLinks ($object_id);
        if (count ($ports))
        {
@@ -839,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);
-                       loadIPv4AddrList ($netinfo);
-                       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, $secondclass);
-                               echo "<td class='${secondclass}'>";
-                               // FIXME: These cals 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>';
+                                       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";
@@ -963,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];
                }
@@ -1036,21 +1084,10 @@ function renderPortsForObject ($object_id = 0)
        {
                printOpFormIntro ('addPort');
                echo "<tr><td>";
-               printImageHREF ('add', 'add a port', TRUE, 104);
+               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>";
-               $types = getPortTypes();
-               $default_port_type = getConfigVar ('default_port_type');
-               // FIXME: isn't this a perfect time for printSelect()?
-               echo "<td><select name='port_type_id' tabindex=102>\n";
-               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 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);
@@ -1079,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'])
                {
@@ -1103,7 +1150,7 @@ function renderPortsForObject ($object_id = 0)
                {
                        echo "<td>&nbsp;</td><td>&nbsp;</td>";
                        echo "<td>";
-                       echo "<a href='javascript:;' onclick='window.open(\"${root}link_helper.php?port=${port['id']}&type=${port['type_id']}&object_id=$object_id&port_name=";
+                       echo "<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');
@@ -1128,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>';
@@ -1150,14 +1189,14 @@ function renderIPv4ForObject ($object_id = 0)
                global $aat;
                printOpFormIntro ('addIPv4Allocation');
                echo "<tr><td>";
-               printImageHREF ('add', 'allocate', TRUE, 99);
+               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');
+               printSelect ($aat, 'bond_type', NULL, 102);
                echo "</td><td>&nbsp;</td><td>";
-               printImageHREF ('add', 'allocate', TRUE, 99);
+               printImageHREF ('add', 'allocate', TRUE, 103);
                echo "</td></tr></form>";
        }
        global $root, $pageno, $tabno, $aat;
@@ -1181,47 +1220,49 @@ function renderIPv4ForObject ($object_id = 0)
        {
                $class = $alloc['addrinfo']['class'];
                $netid = getIPv4AddressNetworkId ($dottedquad);
-               $netinfo = getIPv4NetworkInfo ($netid);
-               loadIPv4AddrList ($netinfo);
+               if (NULL !== $netid)
+               {
+                       $netinfo = getIPv4NetworkInfo ($netid);
+                       loadIPv4AddrList ($netinfo);
+               }
                printOpFormIntro ('updIPv4Allocation', array ('ip' => $dottedquad));
                echo "<tr class='$class' valign=top><td><a href='${root}process.php?op=delIPv4Allocation&page=${pageno}&tab=${tabno}&ip=${dottedquad}&object_id=$object_id'>";
                printImageHREF ('delete', 'Delete this IPv4 address');
                echo "</a></td>";
-               echo "<td class=tdleft><input type='text' name='bond_name' value='${alloc['osif']}' size=10></td>";
-               echo "<td class=tdleft><a href='${root}?page=ipaddress&ip=${dottedquad}'>${dottedquad}</a>";
+               echo "<td class=tdleft><input type='text' name='bond_name' value='${alloc['osif']}' size=10></td><td class=tdleft>";
+               if (NULL !== $netid)
+                       echo "<a href='${root}?page=ipaddress&ip=${dottedquad}'>${dottedquad}</a>";
+               else
+                       echo $dottedquad;
                if (getConfigVar ('EXT_IPV4_VIEW') != 'yes')
-               {
-                       if (NULL === $netid)
-                               $suffix = '/??';
-                       else
-                               echo '<small>/' . $netinfo['mask'] . '</small>';
-               }
-               echo '&nbsp;' . $aac[$alloc['type']];
+                       echo '<small>/' . (NULL === $netid ? '??' : $netinfo['mask']) . '</small>';
                if (!empty ($alloc['addrinfo']['name']))
-                       echo '(' . niftyString ($alloc['addrinfo']['name']) . ')';
+                       echo ' (' . niftyString ($alloc['addrinfo']['name']) . ')';
                echo '</td>';
                // FIXME: this a copy-and-paste from renderRackObject()
                if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
                {
                        if (NULL === $netid)
-                               echo '<td colspan=2>?</td>';
+                               echo '<td colspan=2>N/A</td><td>&nbsp;</td>';
                        else
-                               printIPv4NetInfoTDs ($netinfo);
-                       echo "<td class='${secondclass}'>";
-                       // FIXME: These cals are really heavy, replace them with a more appropriate dedicated function.
-                       $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>';
+                               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>';
                }
                echo '<td>';
                printSelect ($aat, 'bond_type', $alloc['type']);
@@ -1340,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'),
@@ -1419,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...'),
@@ -1757,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');
@@ -1791,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();
@@ -1836,7 +1888,7 @@ function renderObjectGroup ()
                return;
        }
        echo '<br><br><table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
-       echo '<tr><th>Common name</th><th>Visible label</th><th>Asset tag</th><th>Barcode</th><th>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)
        {
@@ -1844,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];
        }
@@ -2016,8 +2074,16 @@ function renderIPv4SpaceRecords ($tree, &$tagcache, $baseurl, $target = 0, $leve
        foreach ($tree as $item)
        {
                $total = $item['addrt'];
-               loadIPv4AddrList ($item); // necessary to compute router list and address counter
-               $used = $item['addrc'];
+               if (getConfigVar ('IPV4_TREE_SHOW_USAGE') == 'yes')
+               {
+                       loadIPv4AddrList ($item); // necessary to compute router list and address counter
+                       $used = $item['addrc'];
+               }
+               else
+               {
+                       $item['addrlist'] = array();
+                       $item['addrc'] = 0;
+               }
                if (isset ($item['id']))
                {
                        if ($item['symbol'] == 'node-collapsed')
@@ -2031,8 +2097,14 @@ function renderIPv4SpaceRecords ($tree, &$tagcache, $baseurl, $target = 0, $leve
                        echo "<td class=tdcenter>";
                        if ($target == $item['id'])
                                echo "<a name=netid${target}></a>";
-                       renderProgressBar ($total ? $used/$total : 0);
-                       echo "<br><small>${used}/${total}</small></td>";
+                       if (getConfigVar ('IPV4_TREE_SHOW_USAGE') == 'yes')
+                       {
+                               renderProgressBar ($total ? $used/$total : 0);
+                               echo "<br><small>${used}/${total}</small>";
+                       }
+                       else
+                               echo "<small>${total}</small>";
+                       echo "</td>";
                        if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
                                printRoutersTD (findRouters ($item['addrlist']), $tagcache);
                        echo "</tr>";
@@ -2044,9 +2116,14 @@ function renderIPv4SpaceRecords ($tree, &$tagcache, $baseurl, $target = 0, $leve
                        echo "<tr valign=top>";
                        printIPv4NetInfoTDs ($item, 'tdleft sparenetwork', $level, $item['symbol']);
                        echo "<td class=tdcenter>";
-                       renderProgressBar ($used/$total, 'sparenetwork');
-                       echo "<br><small>${used}/${total}</small></td>";
-                       echo "<td>&nbsp;</td></tr>";
+                       if (getConfigVar ('IPV4_TREE_SHOW_USAGE') == 'yes')
+                       {
+                               renderProgressBar ($used/$total, 'sparenetwork');
+                               echo "<br><small>${used}/${total}</small>";
+                       }
+                       else
+                               echo "<small>${total}</small>";
+                       echo "</td><td>&nbsp;</td></tr>";
                }
        }
 }
@@ -2057,20 +2134,31 @@ function renderIPv4Space ()
        $tagfilter = getTagFilter();
        $netlist = getIPv4NetworkList ($tagfilter, getTFMode());
        $netcount = count ($netlist);
-       $tree = prepareIPv4Tree ($netlist, isset ($_REQUEST['eid']) ? $_REQUEST['eid'] : 0);
-       unset ($netlist);
+       // expand request can take either natural values or "ALL". Zero means no expanding.
+       $eid = isset ($_REQUEST['eid']) ? $_REQUEST['eid'] : 0;
+       $tree = prepareIPv4Tree ($netlist, $eid);
 
        echo "<table border=0 class=objectview>\n";
        echo "<tr><td class=pcleft>";
        startPortlet ("networks (${netcount})");
-       echo "<table class='widetable' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
-       echo "<tr><th>prefix</th><th>name/tags</th><th>%% used</th>";
+       echo '<h4>';
+       if ($eid === 0)
+               echo 'auto-collapsing at threshold ' . getConfigVar ('TREE_THRESHOLD') . " (<a href='${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";
        $tagcache = array();
        $baseurl = "${root}?page=${pageno}&tab=${tabno}" . getTagFilterStr ($tagfilter);
-       renderIPv4SpaceRecords ($tree, $tagcache, $baseurl, isset ($_REQUEST['eid']) ? $_REQUEST['eid'] : 0);
+       renderIPv4SpaceRecords ($tree, $tagcache, $baseurl, $eid);
        echo "</table>\n";
        finishPortlet();
        echo '</td><td class=pcright>';
@@ -2109,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)
                        {
@@ -2129,7 +2219,7 @@ function renderIPv4SLB ()
                                else
                                {
                                        echo $vsdata['lblist'][$lb_object_id]['size'];
-//                                     echo " (<a href='${root}?page=ipv4rsp&pool_id=";
+//                                     echo " (<a href='${root}?page=ipv4rspool&pool_id=";
 //                                     echo $vsdata['lblist'][$lb_object_id]['id'] . "'>";
 //                                     echo $vsdata['lblist'][$lb_object_id]['name'] . '</a>)';
                                }
@@ -2299,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";
@@ -2318,6 +2439,8 @@ function renderIPv4Network ($id)
        printTagTRs ("${root}?page=ipv4space&tab=default&");
        echo "</table><br>\n";
        finishPortlet();
+
+       renderFilesPortlet ('ipv4net', $id);
        echo "</td>\n";
 
        echo "<td class=pcright>";
@@ -2404,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 = '; ';
                }
@@ -2507,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>";
@@ -2574,16 +2697,16 @@ function renderIPv4AddressAllocations ($dottedquad)
                printOpFormIntro ('addIPv4Allocation');
                echo "<tr><td>";
                printImageHREF ('add', 'allocate', TRUE);
-               echo "</td><td><select name='object_id'>";
+               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' name='bond_name' value='' size=10></td><td>";
-               printSelect ($aat, 'bond_type');
+               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);
+               printImageHREF ('add', 'allocate', TRUE, 103);
                echo "</td></form></tr>";
        }
        global $pageno, $tabno, $root, $aat;
@@ -2647,7 +2770,7 @@ function renderNATv4ForObject ($object_id = 0)
                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);
+               printImageHREF ('add', 'Add new NAT rule', TRUE, 6);
                echo "</td></tr></form>";
        }
        global $pageno, $tabno, $root;
@@ -2905,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.
        {
@@ -2924,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))
                {
@@ -2934,6 +3057,17 @@ function renderSearchResults ()
                        $summary['ipv4addressbydq'][] = $terms;
                }
        }
+       elseif (preg_match (RE_IP4_NET, $terms))
+       // Search for IPv4 network
+       {
+               list ($base, $len) = explode ('/', $terms);
+               if (NULL !== ($tmp = getIPv4AddressNetworkId ($base, $len + 1)))
+               {
+                       $nhits++;
+                       $lasthit = 'ipv4network';
+                       $summary['ipv4network'][] = getIPv4NetworkInfo ($tmp);
+               }
+       }
        else
        // Search for objects, addresses, networks, virtual services and RS pools by their description.
        {
@@ -2979,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>";
@@ -2995,19 +3136,19 @@ function renderSearchResults ()
                        case 'ipv4addressbydq':
                                $parentnet = getIPv4AddressNetworkId ($record);
                                if ($parentnet !== NULL)
-                                       echo "<script language='Javascript'>document.location='${root}?page=iprange&tab=default&id=${parentnet}&hl_ipv4_addr=${record}';//</script>";
+                                       echo "<script language='Javascript'>document.location='${root}?page=ipv4net&tab=default&id=${parentnet}&hl_ipv4_addr=${record}';//</script>";
                                else
                                        echo "<script language='Javascript'>document.location='${root}?page=ipaddress&ip=${record}';//</script>";
                                break;
                        case 'ipv4addressbydescr':
                                $parentnet = getIPv4AddressNetworkId ($record['ip']);
                                if ($parentnet !== NULL)
-                                       echo "<script language='Javascript'>document.location='${root}?page=iprange&tab=default&id=${parentnet}&hl_ipv4_addr=${record['ip']}';//</script>";
+                                       echo "<script language='Javascript'>document.location='${root}?page=ipv4net&tab=default&id=${parentnet}&hl_ipv4_addr=${record['ip']}';//</script>";
                                else
                                        echo "<script language='Javascript'>document.location='${root}?page=ipaddress&ip=${record['ip']}';//</script>";
                                break;
                        case 'ipv4network':
-                               echo "<script language='Javascript'>document.location='${root}?page=iprange";
+                               echo "<script language='Javascript'>document.location='${root}?page=ipv4net";
                                echo "&id=${record['id']}";
                                echo "';//</script>";
                                break;
@@ -3015,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>";
@@ -3023,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;
        }
@@ -3037,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];
@@ -3052,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>";
@@ -3067,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>";
@@ -3087,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];
@@ -3098,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']}'>";
@@ -3112,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']}'>";
@@ -3123,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;
                        }
        }
 }
@@ -3314,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.
@@ -3334,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>";
@@ -3349,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];
                        }
                }
@@ -3367,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
@@ -3413,7 +3590,7 @@ function renderChaptersEditor ()
                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);
+               printImageHREF ('add', 'Add new', TRUE, 101);
                echo '</td></tr></form>';
        }
        global $root, $pageno, $tabno;
@@ -3492,14 +3669,14 @@ function renderEditAttributesForm ()
                printOpFormIntro ('add');
                echo '<tr><td>';
                printImageHREF ('add', 'Create attribute', TRUE);
-               echo "</td><td><input type=text name=attr_name></td>";
-               echo '<td><select name=attr_type>';
+               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);
+               printImageHREF ('add', 'Create attribute', TRUE, 102);
                echo '</td></tr></form>';
        }
        global $root, $pageno, $tabno;
@@ -3536,7 +3713,7 @@ function renderEditAttrMapForm ()
                printOpFormIntro ('add');
                echo '<tr><td>';
                printImageHREF ('add', '', TRUE);
-               echo "</td><td><select name=attr_id>";
+               echo "</td><td><select name=attr_id tabindex=100>";
                $shortType['uint'] = 'U';
                $shortType['float'] = 'F';
                $shortType['string'] = 'S';
@@ -3545,15 +3722,16 @@ function renderEditAttrMapForm ()
                        echo "<option value=${attr['id']}>[" . $shortType[$attr['type']] . "] ${attr['name']}</option>";
                echo "</select></td>";
                echo '<td>';
-               printSelect (getObjectTypeList(), 'objtype_id');
+               printSelect (getObjectTypeList(), 'objtype_id', NULL, 101);
                echo '</td>';
                $dict = getDict();
-               echo '<td><select name=chapter_no>';
+               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>';
-               echo '</tr>';
+               echo '</select></td><td>';
+               printImageHREF ('add', '', TRUE, 103);
+               echo '</td></tr>';
                echo '</form>';
        }
        global $root, $pageno, $tabno;
@@ -3561,7 +3739,7 @@ function renderEditAttrMapForm ()
        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)
@@ -3948,18 +4126,21 @@ function renderSNMPPortFinder ($object_id = 0)
                showError ('Invalid argument', __FUNCTION__);
                return;
        }
-// FIXME: check if SNMP PHP extension is available!
        printOpFormIntro ('querySNMPData');
-?>
-<p align=center>
-This asset has no ports listed, that's why you see this form. If you supply SNMP community,
-I can try automatic data harvesting on the asset. As soon as at least one port is added,
-this tab will not be seen any more. Good luck.<br>
-<input type=text name=community value='public'>
-<input type=submit name='do_scan' value='Go!'> 
-</form>
-</p>
-<?php
+       if (!extension_loaded ('snmp'))
+       {
+               echo "<div class=msg_error>The PHP SNMP extension is not loaded.  Cannot continue.</div>";
+       }
+       else
+       {
+               echo "<p align=center>
+This object has no ports listed, that's why you see this form. If you supply a SNMP community,
+I can try to automatically harvest the data. As soon as at least one port is added,
+this tab will not be seen any more. Good luck.<br>\n";
+               echo "<input type=text name=community value='public'>\n";
+               echo "<input type=submit name='do_scan' value='Go!'> \n";
+               echo "</form></p>\n";
+       }
 }
 
 function renderUIResetForm()
@@ -3970,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();
@@ -4033,13 +4193,13 @@ function renderVirtualService ($vsid)
        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 ();
@@ -4052,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
@@ -4075,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>';
                }
@@ -4202,17 +4358,14 @@ function renderRSPoolLBForm ($pool_id = 0)
                foreach ($poolInfo['lblist'] as $object_id => $vslist)
                        foreach ($vslist as $vs_id => $configs)
                        {
-                               $oi = getObjectInfo ($object_id);
                                printOpFormIntro ('updLB', array ('vs_id' => $vs_id, 'object_id' => $object_id));
                                echo "<tr valign=top class=row_${order}><td><a href='${root}process.php?page=${pageno}&tab=${tabno}&op=delLB&pool_id=${pool_id}&object_id=${object_id}&vs_id=${vs_id}'>";
                                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);
@@ -4256,14 +4409,15 @@ function renderVServiceLBForm ($vs_id = 0)
                foreach ($vsinfo['rspool'] as $pool_id => $rspinfo)
                        foreach ($rspinfo['lblist'] as $object_id => $configs)
                        {
-                               $oi = getObjectInfo ($object_id);
                                printOpFormIntro ('updLB', array ('pool_id' => $pool_id, 'object_id' => $object_id));
                                echo "<tr valign=top class=row_${order}><td><a href='${root}process.php?page=${pageno}&tab=${tabno}&op=delLB&pool_id=${pool_id}&object_id=${object_id}&vs_id=${vs_id}'>";
                                printImageHREF ('delete', 'Unconfigure');
                                echo "</a></td>";
-                               echo "<td class=tdleft><a href='${root}?page=object&object_id=${object_id}'>${oi['dname']}</a></td>";
-                               echo "<td class=tdleft><a href='${root}?page=ipv4rsp&pool_id=${pool_id}'>${rspinfo['name']}</a></td>";
-                               echo "<td><textarea name=vsconfig>${configs['vsconfig']}</textarea></td>";
+                               echo "<td class=tdleft>";
+                               renderLBCell ($object_id);
+                               echo "</td><td class=tdleft>";
+                               renderRSPoolCell ($pool_id, $rspinfo['name']);
+                               echo "</td><td><textarea name=vsconfig>${configs['vsconfig']}</textarea></td>";
                                echo "<td><textarea name=rsconfig>${configs['rsconfig']}</textarea></td><td>";
                                printImageHREF ('SAVE', 'Save changes', TRUE);
                                echo "</td></tr></form>\n";
@@ -4296,7 +4450,7 @@ function renderVServiceLBForm ($vs_id = 0)
 
 function renderRSPool ($pool_id = 0)
 {
-       global $root;
+       global $root, $nextorder;
        if ($pool_id <= 0)
        {
                showError ('Invalid pool_id', __FUNCTION__);
@@ -4314,7 +4468,7 @@ 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";
@@ -4323,13 +4477,13 @@ function renderRSPool ($pool_id = 0)
        printTagTRs ("${root}?page=ipv4rsplist&tab=default&");
        if (!empty ($poolInfo['vsconfig']))
        {
-               echo "<tr><th width='50%' class=tdright>VS configuration:</th><td class=tdleft>&nbsp;</td></tr>\n";
-               echo "<tr><td class=tdleft colspan=2><pre>${poolInfo['vsconfig']}</pre></td></tr>\n";
+               echo "<tr><th width='50%' class=tdright>VS configuration:</th><td>&nbsp;</td></tr>\n";
+               echo "<tr><td colspan=2 class='dashed slbconf'>${poolInfo['vsconfig']}</td></tr>\n";
        }
        if (!empty ($poolInfo['rsconfig']))
        {
-               echo "<tr><th width='50%' class=tdright>RS configuration:</th><td class=tdleft>&nbsp;</td></tr>\n";
-               echo "<tr><td class=tdleft colspan=2><pre>${poolInfo['rsconfig']}</pre></td></tr>\n";
+               echo "<tr><th width='50%' class=tdright>RS configuration:</th><td>&nbsp;</td></tr>\n";
+               echo "<tr><td colspan=2 class='dashed slbconf'>${poolInfo['rsconfig']}</td></tr>\n";
        }
        echo "</table>";
        finishPortlet();
@@ -4337,16 +4491,17 @@ function renderRSPool ($pool_id = 0)
        startPortlet ('Load balancers (' . count ($poolInfo['lblist']) . ')');
        echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
        echo "<tr><th>VS</th><th>LB</th><th>VS config</th><th>RS config</th></tr>";
+       $order = 'odd';
        foreach ($poolInfo['lblist'] as $object_id => $vslist)
                foreach ($vslist as $vs_id => $configs)
        {
-               $oi = getObjectInfo ($object_id);
-               $vi = getVServiceInfo ($vs_id);
-               echo "<tr valign=top><td class=tdleft><a href='${root}?page=ipv4vs&vs_id=${vs_id}'>";
-               echo buildVServiceName ($vi);
-               echo "</a></td><td class=tdleft><a href='${root}?page=object&object_id=${object_id}'>${oi['dname']}</a></td>";
-               echo "<td class=tdleft><pre>${configs['vsconfig']}</pre></td>";
-               echo "<td class=tdleft><pre>${configs['rsconfig']}</pre></td></tr>\n";
+               echo "<tr valign=top class=row_${order}><td class=tdleft><a href='${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();
@@ -4364,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();
@@ -4386,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];
        }
@@ -4491,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))
                {
@@ -4576,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')
@@ -4870,7 +5019,7 @@ function renderTagTreeEditor ()
                global $taglist;
                printOpFormIntro ('createTag');
                echo "<tr><td class=tdleft>";
-               printImageHREF ('add', 'Create tag', TRUE, 102);
+               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)
@@ -4946,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);
@@ -5008,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";
        }
 }
@@ -5144,12 +5298,11 @@ 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);
                        echo "</td></tr></form>\n";
@@ -5197,9 +5350,9 @@ function renderEditVService ($vsid)
 
 function dump ($var)
 {
-       echo '<pre>';
+       echo '<div align=left><pre>';
        print_r ($var);
-       echo '</pre>';
+       echo '</pre></div>';
 }
 
 function renderRackCodeViewer ()
@@ -5233,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');
@@ -5251,21 +5406,24 @@ function renderUser ($user_id)
        $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 ()
@@ -5297,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>";
 }
@@ -5320,19 +5478,339 @@ 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, append "cut here" indicator and provide a mouse hint.
 function niftyString ($string, $maxlen = 30)
@@ -5347,11 +5825,9 @@ function niftyString ($string, $maxlen = 30)
 }
 
 // Iterate over what findRouters() returned and output some text suitable for a TD element.
-// Providing a quasi-static (external) tag cache is advised for calling functions.
-function printRoutersTD ($rlist, &$tagcache = array())
+function printRoutersTD ($rlist)
 {
        global $root;
-       $delim = '';
        $rtrclass = 'tdleft';
        foreach ($rlist as $rtr)
        {
@@ -5364,22 +5840,7 @@ function printRoutersTD ($rlist, &$tagcache = array())
        }
        echo "<td class='${rtrclass}'>";
        foreach ($rlist as $rtr)
-       {
-               echo $delim . $rtr['addr'] . ' ';
-               echo "<a href='${root}?page=object&object_id=${rtr['id']}&hl_ipv4_addr=${rtr['addr']}'>";
-               if (!empty ($rtr['iface']))
-                       echo $rtr['iface'] . '@';
-               echo $rtr['dname'] . '</a>';
-               if (!isset ($tagcache[$rtr['id']]))
-                       $tagcache[$rtr['id']] = loadRackObjectTags ($rtr['id']);
-               if (count ($tagcache[$rtr['id']]))
-               {
-                       echo '<br><small>';
-                       echo serializeTags ($tagcache[$rtr['id']], "${root}?page=objgroup&group_id=0&tab=default&");
-                       echo '</small>';
-               }
-               $delim = '<br><hr>';
-       }
+               renderRouterCell ($rtr['addr'], $rtr['iface'], $rtr['id'], $rtr['dname']);
        echo '</td>';
 }
 
@@ -5403,7 +5864,7 @@ function printIPv4NetInfoTDs ($netinfo, $tdclass = 'tdleft', $indent = 0, $symbo
                        echo '</a>';
        }
        if (isset ($netinfo['id']))
-               echo "<a href='${root}?page=iprange&id=${netinfo['id']}'>";
+               echo "<a href='${root}?page=ipv4net&id=${netinfo['id']}'>";
        echo "${netinfo['ip']}/${netinfo['mask']}";
        if (isset ($netinfo['id']))
                echo '</a>';
@@ -5419,4 +5880,62 @@ function printIPv4NetInfoTDs ($netinfo, $tdclass = 'tdleft', $indent = 0, $symbo
        echo "</td>";
 }
 
+function renderLBCell ($object_id)
+{
+       global $root;
+       $oi = getObjectInfo ($object_id);
+       echo "<table class=slbcell><tr><td>";
+       echo "<a href='${root}?page=object&object_id=${object_id}'>${oi['dname']}</a>";
+       echo "</td></tr><tr><td>";
+       printImageHREF ('LB');
+       echo "</td></tr><tr><td><small>";
+       echo serializeTags (loadRackObjectTags ($object_id));
+       echo "</small></td></tr></table>";
+}
+
+function renderRSPoolCell ($pool_id, $pool_name)
+{
+       global $root;
+       echo "<table class=slbcell><tr><td>";
+       echo "<a href='${root}?page=ipv4rspool&pool_id=${pool_id}'>";
+       echo empty ($pool_name) ? "ANONYMOUS pool [${pool_id}]" : $pool_name;
+       echo "</a></td></tr><tr><td>";
+       printImageHREF ('RS pool');
+       echo "</td></tr><tr><td><small>";
+       echo serializeTags (loadIPv4RSPoolTags ($pool_id));
+       echo "</small></td></tr></table>";
+}
+
+function renderVSCell ($vs_id)
+{
+       global $root;
+       $oinfo = getVServiceInfo ($vs_id);
+       echo "<table class='slbcell vscell'><tr><td rowspan=3>";
+       printImageHREF ('VS');
+       echo "</td><td>";
+       echo "<a href='${root}?page=ipv4vs&vs_id=${vs_id}'>";
+       echo buildVServiceName ($oinfo);
+       echo "</a></td></tr><tr><td>";
+       echo $oinfo['name'];
+       echo '</td></tr><tr><td>';
+       $tags = loadIPv4VSTags ($vs_id);
+       echo count ($tags) ? ("<small>" . serializeTags ($tags) . "</small>") : '&nbsp;';
+       echo "</td></tr></table>";
+}
+
+function renderRouterCell ($dottedquad, $ifname, $object_id, $object_dname)
+{
+       global $root;
+       echo "<table class=slbcell><tr><td rowspan=3>${dottedquad}";
+       if (!empty ($ifname))
+               echo '@' . $ifname;
+       echo "</td>";
+       echo "<td><a href='${root}?page=object&object_id=${object_id}&hl_ipv4_addr=${dottedquad}'><strong>${object_dname}</strong></a></td>";
+       echo "</td></tr><tr><td>";
+       printImageHREF ('router');
+       echo "</td></tr><tr><td><small>";
+       echo serializeTags (loadRackObjectTags ($object_id));
+       echo "</small></td></tr></table>";
+}
+
 ?>