r2776 - renderIPv4Address(): don't try displaying tags, there are none
[racktables] / inc / interface.php
index bbd4238db0d2038cf0aead4274e03a2c118c3910..63b415ff3f6c72545cbf3e135f9597b5f4222cec 100644 (file)
@@ -69,9 +69,15 @@ $image['download']['height'] = 16;
 $image['DOWNLOAD']['path'] = 'pix/download-big.png';
 $image['DOWNLOAD']['width'] = 32;
 $image['DOWNLOAD']['height'] = 32;
-$image['link']['path'] = 'pix/tango-network-wired.png';
-$image['link']['width'] = 16;
-$image['link']['height'] = 16;
+$image['plug']['path'] = 'pix/tango-network-wired.png';
+$image['plug']['width'] = 16;
+$image['plug']['height'] = 16;
+$image['cut']['path'] = 'pix/tango-edit-cut-16x16.png';
+$image['cut']['width'] = 16;
+$image['cut']['height'] = 16;
+$image['CUT']['path'] = 'pix/tango-edit-cut-32x32.png';
+$image['CUT']['width'] = 32;
+$image['CUT']['height'] = 32;
 $image['add']['path'] = 'pix/tango-list-add.png';
 $image['add']['width'] = 16;
 $image['add']['height'] = 16;
@@ -81,13 +87,16 @@ $image['ADD']['height'] = 32;
 $image['delete']['path'] = 'pix/tango-list-remove.png';
 $image['delete']['width'] = 16;
 $image['delete']['height'] = 16;
-$image['destroy']['path'] = 'pix/tango-edit-delete.png';
+$image['destroy']['path'] = 'pix/tango-user-trash-16x16.png';
 $image['destroy']['width'] = 16;
 $image['destroy']['height'] = 16;
-$image['nodestroy']['path'] = 'pix/tango-edit-delete-gray.png';
+$image['nodestroy']['path'] = 'pix/tango-user-trash-16x16-gray.png';
 $image['nodestroy']['width'] = 16;
 $image['nodestroy']['height'] = 16;
-$image['DESTROY']['path'] = 'pix/tango-edit-delete-big.png';
+$image['NODESTROY']['path'] = 'pix/tango-user-trash-32x32-gray.png';
+$image['NODESTROY']['width'] = 32;
+$image['NODESTROY']['height'] = 32;
+$image['DESTROY']['path'] = 'pix/tango-user-trash-32x32.png';
 $image['DESTROY']['width'] = 32;
 $image['DESTROY']['height'] = 32;
 $image['nodelete']['path'] = 'pix/tango-list-remove-shadow.png';
@@ -114,6 +123,9 @@ $image['clear']['height'] = 16;
 $image['CLEAR']['path'] = 'pix/tango-edit-clear-big.png';
 $image['CLEAR']['width'] = 32;
 $image['CLEAR']['height'] = 32;
+$image['CLEAR gray']['path'] = 'pix/tango-edit-clear-gray-32x32.png';
+$image['CLEAR gray']['width'] = 32;
+$image['CLEAR gray']['height'] = 32;
 $image['save']['path'] = 'pix/tango-document-save.png';
 $image['save']['width'] = 16;
 $image['save']['height'] = 16;
@@ -156,15 +168,30 @@ $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['ATTACH']['path'] = 'pix/crystal-attach-32x32.png';
+$image['ATTACH']['width'] = 32;
+$image['ATTACH']['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;
+$image['empty file']['path'] = 'pix/crystal-file-empty-32x32.png';
+$image['empty file']['width'] = 32;
+$image['empty file']['height'] = 32;
+$image['text file']['path'] = 'pix/crystal-file-text-32x32.png';
+$image['text file']['width'] = 32;
+$image['text file']['height'] = 32;
+$image['image file']['path'] = 'pix/crystal-file-image-32x32.png';
+$image['image file']['width'] = 32;
+$image['image file']['height'] = 32;
+$image['NET']['path'] = 'pix/crystal-network-32x32.png';
+$image['NET']['width'] = 32;
+$image['NET']['height'] = 32;
+$image['USER']['path'] = 'pix/crystal-edit-user-32x32.png';
+$image['USER']['width'] = 32;
+$image['USER']['height'] = 32;
 
 // This may be populated later onsite, report rendering function will use it.
 // See the $systemreport for structure.
@@ -178,10 +205,25 @@ $CodePressMap = array
        'js' => 'javascript',
 );
 
+$attrtypes = array
+(
+       'uint' => '[U] unsigned integer',
+       'float' => '[F] floating point',
+       'string' => '[S] string',
+       'dict' => '[D] dictionary record'
+);
+
+// Rack thumbnail image width summands: "front", "interior" and "rear" elements w/o surrounding border.
+$rtwidth = array
+(
+       0 => 9,
+       1 => 21,
+       2 => 9
+);
+
 // Main menu.
 function renderIndex ()
 {
-       global $root;
 ?>
 <table border=0 cellpadding=0 cellspacing=0 width='100%'>
        <tr>
@@ -194,7 +236,7 @@ function renderIndex ()
                                                <?php printImageHREF ('rackspace'); ?></a></h1>
                                        </td>
                                        <td>
-                                               <h1><a href='<?php echo makeHref(array('page'=>'objects')) ?>'>Objects<br>
+                                               <h1><a href='<?php echo makeHref(array('page'=>'depot')) ?>'>Objects<br>
                                                <?php printImageHREF ('objects'); ?></a></h1>
                                        </td>
                                        <td>
@@ -231,36 +273,47 @@ function renderIndex ()
 
 function renderRackspace ()
 {
-       $tagfilter = getTagFilter();
-       $tagfilter_str = getTagFilterStr ($tagfilter);
-       showMessageOrError();
        echo "<table class=objview border=0 width='100%'><tr><td class=pcleft>";
-       renderTagFilterPortlet ($tagfilter, 'rack');
+       $cellfilter = getCellFilter();
+       renderCellFilterPortlet ($cellfilter, 'rack');
        echo '</td><td class=pcright>';
        echo '<table border=0 cellpadding=10 cellpadding=1>';
        // generate thumb gallery
-       $rackrowList = getRackspace ($tagfilter);
        global $nextorder;
        $rackwidth = getRackImageWidth();
+       // Zero value effectively disables the limit.
+       $maxPerRow = getConfigVar ('RACKS_PER_ROW');
        $order = 'odd';
-       foreach ($rackrowList as $rackrow)
+       foreach (getRackRows() as $row_id => $row_name)
        {
+               $rackList = filterCellList (listCells ('rack', $row_id), $cellfilter['expression']);
+               if (!count ($rackList) and count ($cellfilter['expression']))
+                       continue;
+               $rackListIdx = 0;
                echo "<tr class=row_${order}><th class=tdleft>";
-               echo "<a href='".makeHref(array('page'=>'row', 'row_id'=>$rackrow['row_id']))."${tagfilter_str}'>";
-               echo "${rackrow['row_name']}</a></th>";
-               $rackList = getRacksForRow ($rackrow['row_id'], $tagfilter);
-               echo "<td><table border=0 cellspacing=5><tr>";
-               foreach ($rackList as $rack)
-               {
-                       echo "<td align=center><a href='".makeHref(array('page'=>'rack', 'rack_id'=>$rack['id']))."'>";
-                       echo "<img border=0 width=${rackwidth} height=";
-                       echo getRackImageHeight ($rack['height']);
-                       echo " title='${rack['height']} units'";
-                       echo "src='render_image.php?img=minirack&rack_id=${rack['id']}'>";
-                       echo "<br>${rack['name']}</a></td>";
-               }
+               echo "<a href='".makeHref(array('page'=>'row', 'row_id'=>$row_id))."${cellfilter['urlextra']}'>";
+               echo "${row_name}</a></th><td><table border=0 cellspacing=5><tr>";
+               if (!count ($rackList))
+                       echo "<td>(empty row)</td>";
+               else
+                       foreach ($rackList as $rack)
+                       {
+                               if ($rackListIdx > 0 and $maxPerRow > 0 and $rackListIdx % $maxPerRow == 0)
+                               {
+                                       echo '</tr></table></tr>';
+                                       echo "<tr class=row_${order}><th class=tdleft>${row_name} (continued)";
+                                       echo "</th><td><table border=0 cellspacing=5><tr>";
+                                       $order = $nextorder[$order];
+                               }
+                               echo "<td align=center><a href='".makeHref(array('page'=>'rack', 'rack_id'=>$rack['id']))."'>";
+                               echo "<img border=0 width=${rackwidth} height=";
+                               echo getRackImageHeight ($rack['height']);
+                               echo " title='${rack['height']} units'";
+                               echo "src='render_image.php?img=minirack&rack_id=${rack['id']}'>";
+                               echo "<br>${rack['name']}</a></td>";
+                               $rackListIdx++;
+                       }
                echo "</tr></table></tr>\n";
-               $order = $nextorder[$order];
        }
        echo "</table>\n";
        echo "</td></tr></table>\n";
@@ -271,31 +324,32 @@ function renderRackspaceRowEditor ()
        function printNewItemTR ()
        {
                printOpFormIntro ('addRow');
-               echo "<tr><td><input type=text name=name tabindex=100></td><td>";
+               echo "<tr><td>";
+               printImageHREF ('create', 'Add new row', TRUE);
+               echo "</td><td><input type=text name=name tabindex=100></td><td>";
                printImageHREF ('create', 'Add new row', TRUE, 101);
-               echo "</td><td></td></tr></form>";
+               echo "</td></tr></form>";
        }
-       global $pageno, $tabno;
        startPortlet ('Rows');
-       showMessageOrError();
        echo "<table border=0 cellspacing=0 cellpadding=5 align=center class=widetable>\n";
-       echo "<tr><th>Name</th></tr>\n";
+       echo "<tr><th>&nbsp;</th><th>Name</th><th>&nbsp;</th></tr>\n";
        if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
                printNewItemTR();
-       $rackrowList = getRackspace ();
-       foreach ($rackrowList as $rackrow)
+       foreach (getRackRows() as $row_id => $row_name)
        {
-               printOpFormIntro ('updateRow', array ('row_id' => $rackrow['row_id']));
-               echo "<tr><td><input type=text name=name value='${rackrow['row_name']}'></td><td>";
-               printImageHREF ('save', 'Save changes', TRUE);
-               echo "</td></form><td>";
-               if ($rackrow['count'] == 0)
+               echo "<tr><td>";
+               if ($rc = count (listCells ('rack', $row_id)))
+                       printImageHREF ('nodestroy', "${rc} rack(s) here");
+               else
                {
-                       echo "<a href=\"".makeHrefProcess(array('op'=>'delete', 'row_id'=>$rackrow['row_id'], 'name'=>$rackrow['row_name']))."\">";
-                       printImageHREF ('delete', 'Delete row');
+                       echo "<a href=\"".makeHrefProcess(array('op'=>'delete', 'row_id'=>$row_id))."\">";
+                       printImageHREF ('destroy', 'Delete row');
                        echo "</a>";
                }
-               echo "</td></tr>\n";
+               printOpFormIntro ('updateRow', array ('row_id' => $row_id));
+               echo "</td><td><input type=text name=name value='${row_name}'></td><td>";
+               printImageHREF ('save', 'Save changes', TRUE);
+               echo "</form></td></tr>\n";
        }
        if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
                printNewItemTR();
@@ -303,20 +357,15 @@ function renderRackspaceRowEditor ()
        finishPortlet();
 }
 
-function renderRow ($row_id = 0)
+function renderRow ($row_id)
 {
-       if ($row_id == 0)
-       {
-               showError ('Invalid row_id', __FUNCTION__);
-               return;
-       }
        if (($rowInfo = getRackRowInfo ($row_id)) == NULL)
        {
                showError ('getRackRowInfo() failed', __FUNCTION__);
                return;
        }
-       $tagfilter = getTagFilter();
-       $rackList = getRacksForRow ($row_id, $tagfilter);
+       $cellfilter = getCellFilter();
+       $rackList = filterCellList (listCells ('rack', $row_id), $cellfilter['expression']);
        // Main layout starts.
        echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
 
@@ -331,29 +380,36 @@ function renderRow ($row_id = 0)
        echo "</td></tr>\n";
        echo "</table><br>\n";
        finishPortlet();
+       renderCellFilterPortlet ($cellfilter, 'rack', 'row_id', $row_id);
 
-       echo "</td><td class=pcright rowspan=2>";
+       echo "</td><td class=pcright>";
 
        global $nextorder;
        $rackwidth = getRackImageWidth() * getConfigVar ('ROW_SCALE');
+       // Maximum number of racks per row is proportionally less, but at least 1.
+       $maxPerRow = max (floor (getConfigVar ('RACKS_PER_ROW') / getConfigVar ('ROW_SCALE')), 1);
+       $rackListIdx = 0;
        $order = 'odd';
        startPortlet ('Racks');
        echo "<table border=0 cellspacing=5 align='center'><tr>";
        foreach ($rackList as $rack)
        {
+               if ($rackListIdx % $maxPerRow == 0)
+               {
+                       if ($rackListIdx > 0)
+                               echo '</tr>';
+                       echo '<tr>';
+               }
                echo "<td align=center class=row_${order}><a href='".makeHref(array('page'=>'rack', 'rack_id'=>$rack['id']))."'>";
                echo "<img border=0 width=${rackwidth} height=" . (getRackImageHeight ($rack['height']) * getConfigVar ('ROW_SCALE'));
                echo " title='${rack['height']} units'";
                echo "src='render_image.php?img=minirack&rack_id=${rack['id']}'>";
                echo "<br>${rack['name']}</a></td>";
                $order = $nextorder[$order];
+               $rackListIdx++;
        }
        echo "</tr></table>\n";
        finishPortlet();
-       echo "</td></tr>";
-
-       echo "<tr><td class=pcleft>";
-       renderTagFilterPortlet ($tagfilter, 'rack', 'row_id', $row_id);
        echo "</td></tr></table>";
 }
 
@@ -371,20 +427,87 @@ function showError ($info = '', $location = 'N/A')
        echo "Go back or try starting from <a href='".makeHref()."'>index page</a>.<br></div>\n";
 }
 
-// This function renders rack as HTML table.
-function renderRack ($rack_id = 0, $hl_obj_id = 0)
+// This function assures that specified argument was passed
+// and is a number greater than zero.
+function assertUIntArg ($argname, $caller = 'N/A', $allow_zero = FALSE)
 {
-       if ($rack_id == 0)
+       if (!isset ($_REQUEST[$argname]))
        {
-               showError ('Invalid rack_id', __FUNCTION__);
-               return;
+               showError ("Parameter '${argname}' is missing (calling function is [${caller}]).", __FUNCTION__);
+               die();
        }
-       if (($rackData = getRackData ($rack_id)) == NULL)
+       if (!is_numeric ($_REQUEST[$argname]))
        {
-               showError ('getRackData() failed', __FUNCTION__);
-               return;
+               showError ("Parameter '${argname}' is not a number (calling function is [${caller}]).", __FUNCTION__);
+               die();
        }
-       global $pageno, $tabno;
+       if ($_REQUEST[$argname] < 0)
+       {
+               showError ("Parameter '${argname}' is less than zero (calling function is [${caller}]).", __FUNCTION__);
+               die();
+       }
+       if (!$allow_zero and $_REQUEST[$argname] == 0)
+       {
+               showError ("Parameter '${argname}' is equal to zero (calling function is [${caller}]).", __FUNCTION__);
+               die();
+       }
+}
+
+// This function assures that specified argument was passed
+// and is a non-empty string.
+function assertStringArg ($argname, $caller = 'N/A', $ok_if_empty = FALSE)
+{
+       if (!isset ($_REQUEST[$argname]))
+       {
+               showError ("Parameter '${argname}' is missing (calling function is [${caller}]).", __FUNCTION__);
+               die();
+       }
+       if (!is_string ($_REQUEST[$argname]))
+       {
+               showError ("Parameter '${argname}' is not a string (calling function is [${caller}]).", __FUNCTION__);
+               die();
+       }
+       if (!$ok_if_empty and empty ($_REQUEST[$argname]))
+       {
+               showError ("Parameter '${argname}' is an empty string (calling function is [${caller}]).", __FUNCTION__);
+               die();
+       }
+}
+
+function assertBoolArg ($argname, $caller = 'N/A', $ok_if_empty = FALSE)
+{
+       if (!isset ($_REQUEST[$argname]))
+       {
+               showError ("Parameter '${argname}' is missing (calling function is [${caller}]).", __FUNCTION__);
+               die();
+       }
+       if (!is_string ($_REQUEST[$argname]) or $_REQUEST[$argname] != 'on')
+       {
+               showError ("Parameter '${argname}' is not a string (calling function is [${caller}]).", __FUNCTION__);
+               die();
+       }
+       if (!$ok_if_empty and empty ($_REQUEST[$argname]))
+       {
+               showError ("Parameter '${argname}' is an empty string (calling function is [${caller}]).", __FUNCTION__);
+               die();
+       }
+}
+
+function assertIPv4Arg ($argname, $caller = 'N/A', $ok_if_empty = FALSE)
+{
+       assertStringArg ($argname, $caller, $ok_if_empty);
+       if (!empty ($_REQUEST[$argname]) and long2ip (ip2long ($_REQUEST[$argname])) !== $_REQUEST[$argname])
+       {
+               showError ("IPv4 address validation failed for value '" . $_REQUEST[$argname] . "' (calling function is [${caller}]).", __FUNCTION__);
+               die();
+       }
+}
+
+// This function renders rack as HTML table.
+function renderRack ($rack_id, $hl_obj_id = 0)
+{
+       $rackData = spotEntity ('rack', $rack_id);
+       amplifyCell ($rackData);
        markAllSpans ($rackData);
        if ($hl_obj_id > 0)
                highlightObject ($rackData, $hl_obj_id);
@@ -430,7 +553,7 @@ function renderRack ($rack_id = 0, $hl_obj_id = 0)
                        switch ($state)
                        {
                                case 'T':
-                                       $objectData = getObjectInfo ($rackData[$i][$locidx]['object_id']);
+                                       $objectData = spotEntity ('object', $rackData[$i][$locidx]['object_id']);
                                        if (!empty ($objectData['asset_no']))
                                                $prefix = "<div title='${objectData['asset_no']}";
                                        else
@@ -465,8 +588,6 @@ function renderRack ($rack_id = 0, $hl_obj_id = 0)
 
 function renderNewRackForm ($row_id)
 {
-       showMessageOrError();
-
        startPortlet ('Add one');
        printOpFormIntro ('addRack', array ('got_data' => 'TRUE'));
        echo '<table border=0 align=center>';
@@ -475,7 +596,7 @@ function renderNewRackForm ($row_id)
                $defh = '';
        echo "<tr><th class=tdright>Rack name (*):</th><td class=tdleft><input type=text name=rack_name tabindex=1></td>";
        echo "<td rowspan=4>Assign tags:<br>";
-       renderTagSelect();
+       renderNewEntityTags();
        echo "</td></tr>\n";
        echo "<tr><th class=tdright>Height in units (*):</th><td class=tdleft><input type=text name=rack_height1 tabindex=2 value='${defh}'></td></tr>\n";
        echo "<tr><th class=tdright>Comment:</th><td class=tdleft><input type=text name=rack_comment tabindex=3></td></tr>\n";
@@ -492,7 +613,7 @@ function renderNewRackForm ($row_id)
                $defh = '';
        echo "<tr><th class=tdright>Height in units (*):</th><td class=tdleft><input type=text name=rack_height2 value='${defh}'></td>";
        echo "<td rowspan=3 valign=top>Assign tags:<br>";
-       renderTagSelect();
+       renderNewEntityTags();
        echo "</td></tr>\n";
        echo "<tr><th class=tdright>Rack names (*):</th><td class=tdleft><textarea name=rack_names cols=40 rows=25></textarea></td></tr>\n";
        echo "<tr><td class=submit colspan=2>";
@@ -503,50 +624,32 @@ function renderNewRackForm ($row_id)
 
 function renderEditObjectForm ($object_id)
 {
-       showMessageOrError();
-
-       global $pageno, $tabno;
-       $object = getObjectInfo ($object_id);
+       global $pageno;
+       $object = spotEntity ('object', $object_id);
        if ($object == NULL)
        {
-               showError ('getObjectInfo() failed', __FUNCTION__);
+               showError ('Error retrieving data', __FUNCTION__);
                return;
        }
        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>";
+       echo '<table border=0 cellspacing=0 cellpadding=3 align=center>';
+       echo "<tr><td>&nbsp;</td><th colspan=2><h2>Attributes</h2></th></tr>";
+       echo "<tr><td>&nbsp;</td><th class=tdright>Type:</th><td class=tdleft>";
        printSelect (getObjectTypeList(), 'object_type_id', $object['objtype_id']);
        echo "</td></tr>\n";
        // baseline info
-       echo "<tr><th class=tdright>Common name:</th><td class=tdleft><input type=text name=object_name value='${object['name']}'></td></tr>\n";
-       echo "<tr><th class=tdright>Visible label:</th><td class=tdleft><input type=text name=object_label value='${object['label']}'></td></tr>\n";
-       echo "<tr><th class=tdright>Asset tag:</th><td class=tdleft><input type=text name=object_asset_no value='${object['asset_no']}'></td></tr>\n";
-       echo "<tr><th class=tdright>Barcode:</th><td class=tdleft><input type=text name=object_barcode value='${object['barcode']}'></td></tr>\n";
-       echo "<tr><th class=tdright>Has problems:</th><td class=tdleft><input type=checkbox name=object_has_problems";
-       if ($object['has_problems'] == 'yes')
-               echo ' checked';
-       echo "></td></tr>\n";
-       echo "<tr><th class=tdright>Actions:</th><td class=tdleft><a href='".
-               makeHrefProcess(array('op'=>'deleteObject', 'page'=>'objects', 'tab'=>'default', 'object_id'=>$object_id, 'name'=>$object['name'])).
-               "' onclick=\"javascript:return confirm('Are you sure you want to delete the object?')\">Delete object</a></td></tr>\n";
-       echo "<tr><td colspan=2><b>Comment:</b><br><textarea name=object_comment rows=10 cols=80>${object['comment']}</textarea></td></tr>";
-       echo '</table>';
-
-       echo '</td><td class=pcright>';
-       
+       echo "<tr><td>&nbsp;</td><th class=tdright>Common name:</th><td class=tdleft><input type=text name=object_name value='${object['name']}'></td></tr>\n";
+       echo "<tr><td>&nbsp;</td><th class=tdright>Visible label:</th><td class=tdleft><input type=text name=object_label value='${object['label']}'></td></tr>\n";
+       echo "<tr><td>&nbsp;</td><th class=tdright>Asset tag:</th><td class=tdleft><input type=text name=object_asset_no value='${object['asset_no']}'></td></tr>\n";
+       echo "<tr><td>&nbsp;</td><th class=tdright>Barcode:</th><td class=tdleft><input type=text name=object_barcode value='${object['barcode']}'></td></tr>\n";
        // optional attributes
        $values = getAttrValues ($object_id);
+       echo '<input type=hidden name=num_attrs value=' . count($values) . ">\n";
        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)
                {
@@ -561,7 +664,7 @@ function renderEditObjectForm ($object_id)
                        else
                                echo '&nbsp;';
                        echo '</td>';
-                       echo "<td class=tdright>${record['name']}:</td><td class=tdleft>";
+                       echo "<th class=sticker>${record['name']}:</th><td class=tdleft>";
                        switch ($record['type'])
                        {
                                case 'uint':
@@ -572,17 +675,24 @@ function renderEditObjectForm ($object_id)
                                case 'dict':
                                        $chapter = readChapter ($record['chapter_name']);
                                        $chapter[0] = '-- NOT SET --';
-                                       printSelect ($chapter, "${i}_value", $record['key']);
+                                       $chapter = cookOptgroups ($chapter, $object['objtype_id'], $record['key']);
+                                       printNiftySelect ($chapter, "${i}_value", $record['key']);
                                        break;
                        }
                        echo "</td></tr>\n";
                        $i++;
                }
-               echo "</table>";
        }
-       echo "</td></tr>\n";
+       echo "<tr><td>&nbsp;</td><th class=tdright>Has problems:</th><td class=tdleft><input type=checkbox name=object_has_problems";
+       if ($object['has_problems'] == 'yes')
+               echo ' checked';
+       echo "></td></tr>\n";
+       echo "<tr><td>&nbsp;</td><th class=tdright>Actions:</th><td class=tdleft><a href='".
+               makeHrefProcess(array('op'=>'deleteObject', 'page'=>'depot', 'tab'=>'default', 'object_id'=>$object_id)).
+               "' onclick=\"javascript:return confirm('Are you sure you want to delete the object?')\">Delete object</a></td></tr>\n";
+       echo "<tr><td colspan=3><b>Comment:</b><br><textarea name=object_comment rows=10 cols=80>${object['comment']}</textarea></td></tr>";
 
-       echo "<tr><th class=submit colspan=2>";
+       echo "<tr><th class=submit colspan=3>";
        printImageHREF ('SAVE', 'Save changes', TRUE);
        echo "</form></th></tr></table>\n";
        finishPortlet();
@@ -597,14 +707,9 @@ function renderEditObjectForm ($object_id)
 // This is a clone of renderEditObjectForm().
 function renderEditRackForm ($rack_id)
 {
-       showMessageOrError();
        global $pageno;
-       $rack = getRackData ($rack_id);
-       if ($rack == NULL)
-       {
-               showError ('getRackData() failed', __FUNCTION__);
-               return;
-       }
+       $rack = spotEntity ('rack', $rack_id);
+       amplifyCell ($rack);
 
        startPortlet ('Rack attributes');
        printOpFormIntro ('updateRack');
@@ -633,64 +738,32 @@ function renderEditRackForm ($rack_id)
 }
 
 // This is a helper for creators and editors.
-function printSelect ($rowList, $select_name, $selected_id = NULL, $tabindex = NULL)
+// Input array keys are OPTION VALUEs and input array values are OPTION text.
+function printSelect ($optionList, $select_name, $selected_id = NULL, $tabindex = NULL)
 {
-       // First collect all data for OPTGROUPs, then ouput it and dump
-       // the rest of records as is.
-       $optgroup = array();
-       $other = array();
-       foreach ($rowList as $dict_key => $dict_value)
-       {
-               if (strpos ($dict_value, '%GSKIP%') !== FALSE)
-               {
-                       $tmp = explode ('%GSKIP%', $dict_value, 2);
-                       $optgroup[$tmp[0]][$dict_key] = $tmp[1];
-               }
-               elseif (strpos ($dict_value, '%GPASS%') !== FALSE)
-               {
-                       $tmp = explode ('%GPASS%', $dict_value, 2);
-                       $optgroup[$tmp[0]][$dict_key] = $tmp[1];
-               }
-               else
-                       $other[$dict_key] = $dict_value;
-       }
        echo "<select name=${select_name}" . ($tabindex ? " tabindex=${tabindex}" : '') . '>';
-       if (!count ($optgroup))
+       foreach ($optionList as $dict_key => $dict_value)
+               echo "<option value='${dict_key}'" . ($dict_key == $selected_id ? ' selected' : '') . ">${dict_value}</option>";
+       echo "</select>";
+}
+
+// Input is a cooked list of OPTGROUPs, each with own sub-list of OPTIONs in the same
+// format as printSelect() expects.
+function printNiftySelect ($groupList, $select_name, $selected_id = NULL, $tabindex = NULL)
+{
+       // special treatment for ungrouped data
+       if (count ($groupList) == 1 and isset ($groupList['other']))
        {
-               foreach ($other as $dict_key => $dict_value)
-               {
-                       echo "<option value=${dict_key}";
-                       if ($dict_key == $selected_id)
-                               echo ' selected';
-                       echo ">${dict_value}</option>";
-               }
+               printSelect ($groupList['other'], $select_name, $selected_id, $tabindex);
+               return;
        }
-       else
+       echo "<select name=${select_name}" . ($tabindex ? " tabindex=${tabindex}" : '') . '>';
+       foreach ($groupList as $groupname => $groupdata)
        {
-               foreach ($optgroup as $groupname => $groupdata)
-               {
-                       echo "<optgroup label='${groupname}'>";
-                       foreach ($groupdata as $dict_key => $dict_value)
-                       {
-                               echo "<option value=${dict_key}";
-                               if ($dict_key == $selected_id)
-                                       echo ' selected';
-                               echo ">${dict_value}</option>";
-                       }
-                       echo "</optgroup>\n";
-               }
-               if (count ($other))
-               {
-                       echo "<optgroup label='other'>\n";
-                       foreach ($other as $dict_key => $dict_value)
-                       {
-                               echo "<option value=${dict_key}";
-                               if ($dict_key == $selected_id)
-                                       echo ' selected';
-                               echo ">${dict_value}</option>";
-                       }
-                       echo "</optgroup>\n";
-               }
+               echo "<optgroup label='${groupname}'>";
+               foreach ($groupdata as $dict_key => $dict_value)
+                       echo "<option value='${dict_key}'" . ($dict_key == $selected_id ? ' selected' : '') . ">${dict_value}</option>";
+               echo "</optgroup>\n";
        }
        echo "</select>";
 }
@@ -707,9 +780,9 @@ function renderRackInfoPortlet ($rackData)
        renderProgressBar (getRSUforRack ($rackData));
        echo "</td></tr>\n";
        echo "<tr><th width='50%' class=tdright>Objects:</th><td class=tdleft>";
-       echo count (stuffInRackspace ($rackData));
+       echo count ($rackData['mountedObjects']);
        echo "</td></tr>\n";
-       printTagTRs (makeHref(array('page'=>'rackspace', 'tab'=>'default'))."&");
+       printTagTRs ($rackData, makeHref(array('page'=>'rackspace', 'tab'=>'default'))."&");
        if (!empty ($rackData['comment']))
                echo "<tr><th width='50%' class=tdright>Comment:</th><td class=tdleft>${rackData['comment']}</td></tr>\n";
        echo '</table>';
@@ -718,20 +791,10 @@ function renderRackInfoPortlet ($rackData)
 
 // This is a universal editor of rack design/waste.
 // FIXME: switch to using printOpFormIntro()
-function renderGridForm ($rack_id = 0, $filter, $header, $submit, $state1, $state2)
+function renderGridForm ($rack_id, $filter, $header, $submit, $state1, $state2)
 {
-       if ($rack_id == 0)
-       {
-               showError ('Invalid rack_id', __FUNCTION__);
-               return;
-       }
-       if (($rackData = getRackData ($rack_id)) == NULL)
-       {
-               showError ('getRackData() failed', __FUNCTION__);
-               return;
-       }
-       showMessageOrError();
-       global $pageno, $tabno;
+       $rackData = spotEntity ('rack', $rack_id);
+       amplifyCell ($rackData);
        $filter ($rackData);
        markupObjectProblems ($rackData);
 
@@ -750,8 +813,10 @@ function renderGridForm ($rack_id = 0, $filter, $header, $submit, $state1, $stat
        startPortlet ($header);
        echo "<center>\n";
        echo "<table class=rack border=0 cellspacing=0 cellpadding=1>\n";
-       echo "<tr><th width='10%'>&nbsp;</th><th width='20%'>Front</th>";
-       echo "<th width='50%'>Interior</th><th width='20%'>Back</th></tr>\n";
+       echo "<tr><th width='10%'>&nbsp;</th>";
+       echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '0', ${rackData['height']})\">Front</a></th>";
+       echo "<th width='50%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '1', ${rackData['height']})\">Interior</a></th>";
+       echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '2', ${rackData['height']})\">Back</a></th></tr>\n";
        printOpFormIntro ('updateRack');
        markupAtomGrid ($rackData, $state2);
        renderAtomGrid ($rackData);
@@ -766,7 +831,7 @@ function renderRackDesign ($rack_id)
        renderGridForm ($rack_id, 'applyRackDesignMask', 'Rack design', 'Set rack design', 'A', 'F');
 }
 
-function renderRackProblems ($rack_id = 0)
+function renderRackProblems ($rack_id)
 {
        renderGridForm ($rack_id, 'applyRackProblemMask', 'Rack problems', 'Mark unusable atoms', 'F', 'U');
 }
@@ -781,35 +846,15 @@ function finishPortlet ()
        echo "</div>\n";
 }
 
-function printRefsOfType ($refs, $type, $eq)
-{
-       $gotone=0;
-       foreach ($refs as $ref)
-       {
-               if ($eq($ref['type'], $type))
-               {
-                       if ($gotone) echo ', ';
-                       echo "<a href='".makeHref(array('page'=>'object', 'object_id'=>$ref['object_id']))."'>";
-                       if (!empty ($ref['name']))
-                               echo $ref['name'] . '@';
-                       echo "${ref['object_name']}</a>";
-                       $gotone=1;
-               }
-       }
-}
-
-function renderRackObject ($object_id = 0)
+function renderRackObject ($object_id)
 {
        global $nextorder, $aac;
-       if ($object_id <= 0)
-       {
-               showError ('Invalid object_id', __FUNCTION__);
-               return;
-       }
-       $info = getObjectInfo ($object_id);
+       $info = spotEntity ('object', $object_id);
+       // FIXME: employ amplifyCell() instead of calling loader functions directly
+       #amplifyCell ($info);
        if ($info == NULL)
        {
-               showError ('getObjectInfo() failed', __FUNCTION__);
+               showError ('Error loading data', __FUNCTION__);
                return;
        }
        // Main layout starts.
@@ -821,13 +866,20 @@ function renderRackObject ($object_id = 0)
        echo "<table border=0 cellspacing=0 cellpadding=3 width='100%'>\n";
        if (!empty ($info['name']))
                echo "<tr><th width='50%' class=tdright>Common name:</th><td class=tdleft>${info['name']}</td></tr>\n";
-       elseif (in_array ($info['objtype_id'], explode (',', getConfigVar ('NAMEFUL_OBJTYPES'))))
+       // FIXME: don't call spotEntity() each time, do it once in the beginning.
+       elseif (considerConfiguredConstraint (spotEntity ('object', $object_id), 'NAMEWARN_LISTSRC'))
                echo "<tr><td colspan=2 class=msg_error>Common name is missing.</td></tr>\n";
-       echo "<tr><th width='50%' class=tdright>Object type:</th>";
-       echo "<td class=tdleft><a href='".makeHref(array('page'=>'objgroup', 'group_id'=>$info['objtype_id'], 'hl_object_id'=>$object_id))."'>${info['objtype_name']}</a></td></tr>\n";
+       echo "<tr><th width='50%' class=tdright>Object type:</th><td class=tdleft><a href='";
+       echo makeHref (array (
+               'page' => 'depot',
+               'tab' => 'default',
+               'cfe' => '{$typeid_' . $info['objtype_id'] . '}'
+       ));
+       echo "'>${info['objtype_name']}</a></td></tr>\n";
        if (!empty ($info['asset_no']))
                echo "<tr><th width='50%' class=tdright>Asset tag:</th><td class=tdleft>${info['asset_no']}</td></tr>\n";
-       elseif (in_array ($info['objtype_id'], explode (',', getConfigVar ('REQUIRE_ASSET_TAG_FOR'))))
+       // FIXME: ditto
+       elseif (considerConfiguredConstraint (spotEntity ('object', $object_id), 'ASSETWARN_LISTSRC'))
                echo "<tr><td colspan=2 class=msg_error>Asset tag is missing.</td></tr>\n";
        if (!empty ($info['label']))
                echo "<tr><th width='50%' class=tdright>Visible label:</th><td class=tdleft>${info['label']}</td></tr>\n";
@@ -837,8 +889,21 @@ 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=tdright><span class=sticker>${record['name']}</span>:</th><td class=tdleft>${record['a_value']}</td></tr>\n";
-       printTagTRs (makeHref(array('page'=>'objgroup', 'tab'=>'default', 'group_id'=>$info['objtype_id']))."&");
+                       echo "<tr><th width='50%' class=sticker>${record['name']}:</th><td class=sticker>${record['a_value']}</td></tr>\n";
+       printTagTRs
+       (
+               $info,
+               makeHref
+               (
+                       array
+                       (
+                               'page'=>'depot',
+                               'tab'=>'default',
+                               'andor' => 'and',
+                               'cfe' => '{$typeid_' . $info['objtype_id'] . '}',
+                       )
+               )."&"
+       );
        echo "</table><br>\n";
        finishPortlet();
 
@@ -899,7 +964,7 @@ function renderRackObject ($object_id = 0)
                startPortlet ('IPv4 addresses');
                echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
                if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
-                       echo "<tr><th>OS interface</th><th>IP address</th><th colspan=2>network</th><th>routed by</th><th>peers</th></tr>\n";
+                       echo "<tr><th>OS interface</th><th>IP address</th><th>network</th><th>routed by</th><th>peers</th></tr>\n";
                else
                        echo "<tr><th>OS interface</th><th>IP address</th><th>peers</th></tr>\n";
                $hl_ipv4_addr = '';
@@ -916,7 +981,7 @@ function renderRackObject ($object_id = 0)
                        $netid = getIPv4AddressNetworkId ($dottedquad);
                        if (NULL !== $netid)
                        {
-                               $netinfo = getIPv4NetworkInfo ($netid);
+                               $netinfo = spotEntity ('ipv4net', $netid);
                                loadIPv4AddrList ($netinfo);
                        }
                        echo "<tr class='${class}' valign=top><td class=tdleft>${alloc['osif']}</td><td class='${secondclass}'>";
@@ -933,16 +998,21 @@ function renderRackObject ($object_id = 0)
                        if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
                        {
                                if (NULL === $netid)
-                                       echo '<td colspan=2>N/A</td><td>&nbsp;</td>';
+                                       echo '<td class=sparenetwork>N/A</td><td class=sparenetwork>N/A</td>';
                                else
                                {
-                                       printIPv4NetInfoTDs ($netinfo, $secondclass);
+                                       echo "<td class='${secondclass}'>";
+                                       renderCell ($netinfo);
+                                       echo "</td>";
                                        // filter out self-allocation
                                        $other_routers = array();
                                        foreach (findRouters ($netinfo['addrlist']) as $router)
                                                if ($router['id'] != $object_id)
                                                        $other_routers[] = $router;
-                                       printRoutersTD ($other_routers);
+                                       if (count ($other_routers))
+                                               printRoutersTD ($other_routers);
+                                       else
+                                               echo "<td class='${secondclass}'>&nbsp;</td>";
                                }
                        }
                        // peers
@@ -1027,15 +1097,15 @@ function renderRackObject ($object_id = 0)
        if (count ($pools))
        {
                $order = 'odd';
-               startPortlet ('Real server pools');
+               startPortlet ('Real server pools (' . count ($pools) . ')');
                echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
                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>";
-                       renderVSCell ($vs_id);
+                       renderCell (spotEntity ('ipv4vs', $vs_id));
                        echo "</td><td class=tdleft>";
-                       renderRSPoolCell ($info['pool_id'], $info['pool_name']);
+                       renderCell (spotEntity ('ipv4rspool', $info['pool_id']));
                        echo '</td><td class=tdleft>' . $info['rscount'] . '</td>';
                        echo "<td class=slbconf>${info['vsconfig']}</td>";
                        echo "<td class=slbconf>${info['rsconfig']}</td>";
@@ -1051,10 +1121,8 @@ function renderRackObject ($object_id = 0)
        echo "<td class=pcright>";
        // rackspace portlet
        startPortlet ('rackspace allocation');
-       // FIXME: now we call getRackData() twice
-       $racks = getResidentRacksData ($object_id);
-       foreach ($racks as $rackData)
-               renderRack ($rackData['id'], $object_id);
+       foreach (getResidentRacksData ($object_id, FALSE) as $rack_id)
+               renderRack ($rack_id, $object_id);
        echo '<br>';
        finishPortlet();
        echo "</td></tr>";
@@ -1095,7 +1163,7 @@ function showMessageOrError ()
 }
 
 // This function renders a form for port edition.
-function renderPortsForObject ($object_id = 0)
+function renderPortsForObject ($object_id)
 {
        function printNewItemTR ()
        {
@@ -1110,13 +1178,6 @@ function renderPortsForObject ($object_id = 0)
                printImageHREF ('add', 'add a port', TRUE, 104);
                echo "</td></tr></form>";
        }
-       global $pageno, $tabno;
-       if ($object_id <= 0)
-       {
-               showError ('Invalid object_id', __FUNCTION__);
-               return;
-       }
-       showMessageOrError();
        startPortlet ('Ports and interfaces');
        $ports = getObjectPortsAndLinks ($object_id);
        usort($ports, 'sortByName');
@@ -1128,7 +1189,7 @@ function renderPortsForObject ($object_id = 0)
        foreach ($ports as $port)
        {
                printOpFormIntro ('editPort', array ('port_id' => $port['id']));
-               echo "<tr><td><a href='".makeHrefProcess(array('op'=>'delport', 'port_id'=>$port['id'], 'object_id'=>$object_id, 'port_name'=>$port['name']))."'>";
+               echo "<tr><td><a href='".makeHrefProcess(array('op'=>'delPort', 'port_id'=>$port['id'], 'object_id'=>$object_id, 'port_name'=>$port['name']))."'>";
                printImageHREF ('delete', 'Unlink and Delete this port');
                echo "</a></td>\n";
                echo "<td><input type=text name=name value='${port['name']}' size=8></td>";
@@ -1142,7 +1203,7 @@ function renderPortsForObject ($object_id = 0)
                else
                {
                        echo "<input type=hidden name=port_type_id value='${port['type_id']}'>";
-                       echo "<td>${port['type']}</td>\n";
+                       echo "<td class=tdleft>${port['type']}</td>\n";
                }
                echo "<td><input type=text name=l2address value='${port['l2address']}'></td>\n";
                if ($port['remote_object_id'])
@@ -1158,7 +1219,7 @@ function renderPortsForObject ($object_id = 0)
                                        'remote_port_name'=>$port['remote_name'], 
                                        'remote_object_name'=>$port['remote_object_name'])).
                        "'>";
-                       printImageHREF ('clear', 'Unlink this port');
+                       printImageHREF ('cut', 'Unlink this port');
                        echo "</a></td>";
                }
                elseif (!empty ($port['reservation_comment']))
@@ -1179,7 +1240,7 @@ function renderPortsForObject ($object_id = 0)
                        echo "<td>&nbsp;</td><td>&nbsp;</td>";
                        echo "<td>";
                        echo "<a href='javascript:;' onclick='window.open(\"".makeHrefForHelper('portlist', array('port'=>$port['id'], 'type'=>$port['type_id'], 'object_id'=>$object_id, 'port_name'=>$port['name']))."\",\"findlink\",\"height=700, width=400, location=no, menubar=no, resizable=yes, scrollbars=no, status=no, titlebar=no, toolbar=no\");'>";
-                       printImageHREF ('link', 'Link this port');
+                       printImageHREF ('plug', 'Link this port');
                        echo "</a> <input type=text name=reservation_comment>";
                        echo "</td>\n";
                }
@@ -1194,21 +1255,21 @@ function renderPortsForObject ($object_id = 0)
 
        startPortlet ('Add/update multiple ports');
        printOpFormIntro ('addMultiPorts');
-       echo 'Format: <select name=format>';
+       echo 'Format: <select name=format tabindex=201>';
        echo '<option value=c3600asy>Cisco 3600 async: sh line | inc TTY</option>';
        echo '<option value=fiwg selected>Foundry ServerIron/FastIron WorkGroup/Edge: sh int br</option>';
        echo '<option value=fisxii>Foundry FastIron SuperX/II4000: sh int br</option>';
        echo '<option value=ssv1>SSV:&lt;interface name&gt; &lt;MAC address&gt;</option>';
        echo "</select>";
        echo 'Default port type: ';
-       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";
+       printSelect (getPortTypes(), 'port_type', getConfigVar ('default_port_type'), 202);
+       echo "<input type=submit value='Parse output' tabindex=204><br>\n";
+       echo "<textarea name=input cols=100 rows=50 tabindex=203></textarea><br>\n";
        echo '</form>';
        finishPortlet();
 }
 
-function renderIPv4ForObject ($object_id = 0)
+function renderIPv4ForObject ($object_id)
 {
        function printNewItemTR ()
        {
@@ -1219,25 +1280,19 @@ function renderIPv4ForObject ($object_id = 0)
                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>";
+               echo "<td colspan=2>&nbsp;</td><td>";
                printSelect ($aat, 'bond_type', NULL, 102);
                echo "</td><td>&nbsp;</td><td>";
                printImageHREF ('add', 'allocate', TRUE, 103);
                echo "</td></tr></form>";
        }
-       global $pageno, $tabno, $aat;
-       if ($object_id <= 0)
-       {
-               showError ('Invalid object_id', __FUNCTION__);
-               return;
-       }
-       showMessageOrError();
+       global $aat;
        startPortlet ('Allocations');
        $alloclist = getObjectIPv4Allocations ($object_id);
        echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
        echo '<tr><th>&nbsp;</th><th>OS interface</th><th>IP address</th>';
        if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
-               echo '<th colspan=2>network</th><th>routed by</th>';
+               echo '<th>network</th><th>routed by</th>';
        echo '<th>type</th><th>misc</th><th>&nbsp</th></tr>';
 
        if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
@@ -1248,7 +1303,7 @@ function renderIPv4ForObject ($object_id = 0)
                $netid = getIPv4AddressNetworkId ($dottedquad);
                if (NULL !== $netid)
                {
-                       $netinfo = getIPv4NetworkInfo ($netid);
+                       $netinfo = spotEntity ('ipv4net', $netid);
                        loadIPv4AddrList ($netinfo);
                }
                printOpFormIntro ('updIPv4Allocation', array ('ip' => $dottedquad));
@@ -1269,25 +1324,21 @@ function renderIPv4ForObject ($object_id = 0)
                if (getConfigVar ('EXT_IPV4_VIEW') == 'yes')
                {
                        if (NULL === $netid)
-                               echo '<td colspan=2>N/A</td><td>&nbsp;</td>';
+                               echo '<td class=sparenetwork>N/A</td><td class=sparenetwork>N/A</td>';
                        else
                        {
-                               printIPv4NetInfoTDs ($netinfo);
-                               echo "<td class=tdleft>";
-                               // FIXME: These calls are really heavy, replace them with a more appropriate dedicated function.
-                               $newline = '';
-                               foreach (findRouters ($netinfo['addrlist']) as $router)
-                               {
-                                       if ($router['id'] == $object_id)
-                                               continue;
-                                       echo $newline . $router['addr'] . ", <a href='".makeHref(array('page'=>'object', 'object_id'=>$router['id'], 'hl_ipv4_addr'=>$router['addr']))."'>";
-                                       echo (empty ($router['iface']) ? '' : $router['iface'] . '@') . $router['dname'] . '</a>';
-                                       $routertags = loadRackObjectTags ($router['id']);
-                                       if (count ($routertags))
-                                               echo '<br><small>' . serializeTags ($routertags, makeHref(array('page'=>'objects', 'tab'=>'default'))."&") . '</small>';
-                                       $newline = '<br>';
-                               }
+                               echo '<td>';
+                               renderCell ($netinfo);
                                echo '</td>';
+                               // filter out self-allocation
+                               $other_routers = array();
+                               foreach (findRouters ($netinfo['addrlist']) as $router)
+                                       if ($router['id'] != $object_id)
+                                               $other_routers[] = $router;
+                               if (count ($other_routers))
+                                       printRoutersTD ($other_routers);
+                               else
+                                       echo "<td>&nbsp;</td>";
                        }
                }
                echo '<td>';
@@ -1339,7 +1390,7 @@ function printLog ($log)
                        $msginfo = array
                        (
 // records 0~99 with success messages
-                               0 => array ('code' => 'success', 'format' => 'Success: %s'),
+                               0 => array ('code' => 'success', 'format' => '%s'),
                                1 => array ('code' => 'success', 'format' => '%u new records done, %u already existed'),
                                2 => array ('code' => 'success', 'format' => 'NATv4 rule was successfully added.'),
                                3 => array ('code' => 'success', 'format' => 'NATv4 rule was successfully deleted.'),
@@ -1397,7 +1448,7 @@ function printLog ($log)
                                55 => array ('code' => 'success', 'format' => 'Chapter was added.'),
                                56 => array ('code' => 'success', 'format' => 'Update succeeded.'),
                                57 => array ('code' => 'success', 'format' => 'Reset complete'),
-                               58 => array ('code' => 'success', 'format' => "Successfully deleted tag ."),
+                               58 => array ('code' => 'success', 'format' => "Deleted tag '%s'."),
                                59 => array ('code' => 'success', 'format' => "Created tag '%s'."),
                                60 => array ('code' => 'success', 'format' => "Updated tag '%s'."),
                                61 => array ('code' => 'success', 'format' => 'Password changed successfully.'),
@@ -1408,23 +1459,24 @@ 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'),
-                               77 => array ('code' => 'success', 'format' => 'Row %s was deleted successfully'),
-                               78 => array ('code' => 'success', 'format' => 'File %s saved Ok'),
-                               79 => array ('code' => 'success', 'format' => 'Rack %s was deleted successfully'),
+                               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 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'),
+                               77 => array ('code' => 'success', 'format' => 'Row "%s" was deleted successfully'),
+                               78 => array ('code' => 'success', 'format' => 'File "%s" saved Ok'),
+                               79 => array ('code' => 'success', 'format' => 'Rack "%s" was deleted successfully'),
+                               80 => array ('code' => 'success', 'format' => "Added new object '%s'"),
 
 // records 100~199 with fatal error messages
-                               100 => array ('code' => 'error', 'format' => 'Generic error: %s'),
+                               100 => array ('code' => 'error', 'format' => '%s'),
                                101 => array ('code' => 'error', 'format' => 'Port name cannot be empty'),
                                102 => array ('code' => 'error', 'format' => "Error creating user account '%s'"),
-                               103 => array ('code' => 'error', 'format' => 'getHashByID() failed'),
+                               103 => array ('code' => 'error', 'format' => 'User not found!'),
                                104 => array ('code' => 'error', 'format' => "Error updating user account '%s'"),
 // ...
 // ...
@@ -1467,9 +1519,9 @@ function printLog ($log)
                                143 => array ('code' => 'error', 'format' => 'Tried chaining %u tags, but experienced %u errors.'),
                                144 => array ('code' => 'error', 'format' => "Error deleting tag: '%s'"),
                                145 => array ('code' => 'error', 'format' => "Invalid tag name '%s'"),
-                               146 => array ('code' => 'error', 'format' => "Tag '%s' (or similar name) already exists"),
-                               147 => array ('code' => 'error', 'format' => "Could not create tag '%s' because of error '%s'"),
-                               148 => array ('code' => 'error', 'format' => "Could not update tag '%s' because of error '%s'"),
+// ...
+                               147 => array ('code' => 'error', 'format' => "Could not create tag '%s': %s"),
+                               148 => array ('code' => 'error', 'format' => "Could not update tag '%s': %s"),
                                149 => array ('code' => 'error', 'format' => 'Turing test failed'),
                                150 => array ('code' => 'error', 'format' => 'Can only change password under DB authentication.'),
                                151 => array ('code' => 'error', 'format' => 'Old password doesn\'t match.'),
@@ -1500,16 +1552,26 @@ function printLog ($log)
                                176 => array ('code' => 'error', 'format' => 'This network already exists'),
                                177 => array ('code' => 'error', 'format' => 'commitUpdateRack() failed'),
                                178 => array ('code' => 'error', 'format' => 'file not found'),
-                               179 => array ('code' => 'error', 'format' => 'Error saving file, all changes lost!'),
+                               179 => array ('code' => 'error', 'format' => 'Declining outdated text. Re-edit the file for consistency.'),
+                               180 => array ('code' => 'error', 'format' => 'Error saving file, all changes lost!'),
+                               181 => array ('code' => 'error', 'format' => "file uploads not allowed, change 'file_uploads' parameter in php.ini"),
+                               182 => array ('code' => 'error', 'format' => 'SQL query failed: %s'),
+                               183 => array ('code' => 'error', 'format' => "Tag id '%s' does not exist."),
+                               184 => array ('code' => 'error', 'format' => 'Submitted form is invalid at line %u'),
+                               185 => array ('code' => 'error', 'format' => "Failed to add object '%s'"),
+                               186 => array ('code' => 'error', 'format' => 'Incomplete form has been ignored. Cheers.'),
+                               187 => array ('code' => 'error', 'format' => "Internal error in function '%s'"),
 
 // records 200~299 with warnings
-                               200 => array ('code' => 'warning', 'format' => 'generic warning: %s'),
+                               200 => array ('code' => 'warning', 'format' => '%s'),
                                201 => array ('code' => 'warning', 'format' => 'nothing happened...'),
                                202 => array ('code' => 'warning', 'format' => 'gw: %s'),
                                203 => array ('code' => 'warning', 'format' => 'Port %s seems to be the first in VLAN %u at this switch.'),
                                204 => array ('code' => 'warning', 'format' => 'Check uplink/downlink configuration for proper operation.'),
                                205 => array ('code' => 'warning', 'format' => '%u change request(s) have been ignored'),
                                206 => array ('code' => 'warning', 'format' => 'Rack is not empty'),
+                               207 => array ('code' => 'warning', 'format' => 'Ignored empty request'),
+
                        );
                        // Handle the arguments. Is there any better way to do it?
                        foreach ($log['m'] as $record)
@@ -1578,60 +1640,48 @@ and either delete them before unmounting or refuse to unmount the object.
 */
 
 // We extensively use $_REQUEST in the function.
-// FIXME: move related code into ophandler
-function renderRackSpaceForObject ($object_id = 0)
+function renderRackSpaceForObject ($object_id)
 {
-       if ($object_id <= 0)
-       {
-               showError ('Invalid object_id', __FUNCTION__);
-               return;
-       }
-       $is_submit = isset ($_REQUEST['got_atoms']);
        $is_update = isset ($_REQUEST['rackmulti'][0]);
-       $info = getObjectInfo ($object_id);
+       $info = spotEntity ('object', $object_id);
        if ($info == NULL)
        {
-               showError ('getObjectInfo() failed', __FUNCTION__);
+               showError ('Error loading data', __FUNCTION__);
                return;
        }
        // Always process occupied racks plus racks chosen by user. First get racks with
        // already allocated rackspace...
-       $workingRacksData = getResidentRacksData ($object_id);
-       if ($workingRacksData === NULL)
-       {
-               print_r ($workingRacksData);
-               showError ('getResidentRacksData() failed', __FUNCTION__);
-               return;
-       }
+       if (NULL === ($workingRacksData = getResidentRacksData ($object_id)))
+               die; // some error already shown
 
        // ...and then add those chosen by user (if any).
-       if ($is_update)
+       if (isset($_REQUEST['rackmulti']))
                foreach ($_REQUEST['rackmulti'] as $cand_id)
                {
                        if (!isset ($workingRacksData[$cand_id]))
                        {
-                               $rackData = getRackData ($cand_id);
-                               if ($rackData == NULL)
+                               if (NULL == ($rackData = spotEntity ('rack', $cand_id)))
                                {
-                                       showError ('getRackData() failed', __FUNCTION__);
+                                       showError ('Rack not found', __FUNCTION__);
                                        return NULL;
                                }
+                               amplifyCell ($rackData);
                                $workingRacksData[$cand_id] = $rackData;
                        }
                }
 
+       printOpFormIntro ('updateObjectAllocation');
+
        // Do it only once...
-       foreach ($workingRacksData as &$rackData)
+       foreach ($workingRacksData as $rackId => &$rackData)
+       {
                applyObjectMountMask ($rackData, $object_id);
+               echo "<input type=\"hidden\" name=\"rackmulti[]\" value=\"$rackId\">";
+       }
        // Now we workaround an old caveat: http://bugs.php.net/bug.php?id=37410
        unset ($rackData);
 
        // This is the time for rendering.
-       global $pageno, $tabno;
-
-       showMessageOrError();
-
-       printOpFormIntro ('updateObjectAllocation');
 
        // Main layout starts.
        echo "<table border=0 class=objectview cellspacing=0 cellpadding=0><tr>";
@@ -1639,18 +1689,13 @@ function renderRackSpaceForObject ($object_id = 0)
        // Left portlet with rack list.
        echo "<td class=pcleft height='1%'>";
        startPortlet ('Racks');
-       $allRacksData = getRacksForRow();
+       $allRacksData = listCells ('rack');
        if (count ($allRacksData) <= getConfigVar ('RACK_PRESELECT_THRESHOLD'))
        {
-               foreach (array_keys ($allRacksData) as $rack_id)
+               foreach ($allRacksData as $rack)
                {
-                       $rackData = getRackData ($rack_id);
-                       if ($rackData == NULL)
-                       {
-                               showError ('getRackData() failed', __FUNCTION__);
-                               return NULL;
-                       }
-                       $workingRacksData[$rack_id] = $rackData;
+                       amplifyCell ($rack);
+                       $workingRacksData[$rack_id] = $rack;
                }
                foreach ($workingRacksData as &$rackData)
                        applyObjectMountMask ($rackData, $object_id);
@@ -1684,16 +1729,20 @@ function renderRackSpaceForObject ($object_id = 0)
                markupAtomGrid ($rackData, 'T');
                // If we have a form processed, discard user input and show new database
                // contents.
-               if (!$is_submit and $is_update)
+               if ($is_update)
                        mergeGridFormToRack ($rackData);
                echo "<td valign=top>";
                echo "<center>\n<h2>${rackData['name']}</h2>\n";
                echo "<table class=rack border=0 cellspacing=0 cellpadding=1>\n";
-               echo "<tr><th width='10%'>&nbsp;</th><th width='20%'>Front</th>";
-               echo "<th width='50%'>Interior</th><th width='20%'>Back</th></tr>\n";
+               echo "<tr><th width='10%'>&nbsp;</th>";
+               echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '0', ${rackData['height']})\">Front</a></th>";
+               echo "<th width='50%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '1', ${rackData['height']})\">Interior</a></th>";
+               echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '2', ${rackData['height']})\">Back</a></th></tr>\n";
                renderAtomGrid ($rackData);
-               echo "<tr><th width='10%'>&nbsp;</th><th width='20%'>Front</th>";
-               echo "<th width='50%'>Interior</th><th width='20%'>Back</th></tr>\n";
+               echo "<tr><th width='10%'>&nbsp;</th>";
+               echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '0', ${rackData['height']})\">Front</a></th>";
+               echo "<th width='50%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '1', ${rackData['height']})\">Interior</a></th>";
+               echo "<th width='20%'><a href='javascript:;' onclick=\"toggleColumnOfAtoms('${rack_id}', '2', ${rackData['height']})\">Back</a></th></tr>\n";
                echo "</table></center>\n";
                echo '</td>';
        }
@@ -1701,6 +1750,7 @@ function renderRackSpaceForObject ($object_id = 0)
        finishPortlet();
        echo "</td>\n";
 
+
        echo "</form>\n";
        echo "</tr></table>\n";
 }
@@ -1717,7 +1767,8 @@ function renderMolecule ($mdata, $object_id)
                $atom = $rua['atom'];
                if (!isset ($rackpack[$rack_id]))
                {
-                       $rackData = getRackData ($rack_id);
+                       $rackData = spotEntity ('rack', $rack_id);
+                       amplifyCell ($rackData);
                        for ($i = $rackData['height']; $i > 0; $i--)
                                for ($locidx = 0; $locidx < 3; $locidx++)
                                        $rackData[$i][$locidx]['state'] = 'F';
@@ -1747,140 +1798,35 @@ function renderMolecule ($mdata, $object_id)
        }
 }
 
-function renderUnmountedObjectsPortlet ()
-{
-       startPortlet ('Unmounted objects');
-       $objs = getUnmountedObjects();
-       if ($objs === NULL)
-       {
-               showError ('getUnmountedObjects() failed', __FUNCTION__);
-               return;
-       }
-       global $nextorder;
-       $order = 'odd';
-       echo '<br><br><table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
-       echo '<tr><th>Common name</th><th>Visible label</th><th>Asset number</th><th>Barcode</th></tr>';
-       foreach ($objs as $obj)
-       {
-               echo "<tr class=row_${order}><td><a href='".makeHref(array('page'=>'object', 'object_id'=>$obj['id']))."'>${obj['dname']}</a></td>";
-               echo "<td>${obj['label']}</td>";
-               echo "<td>${obj['asset_no']}</td>";
-               echo "<td>${obj['barcode']}</td>";
-               echo "</tr>";
-               $order = $nextorder[$order];
-       }
-       echo "</table><br>\n";
-       finishPortlet();
-}
-
-function renderProblematicObjectsPortlet ()
+function renderEmptyPortsSelect ($port_id, $type_id)
 {
-       startPortlet ('Problematic objects');
-       $objs = getProblematicObjects();
-       if ($objs === NULL)
-       {
-               showError ('getProblematicObjects() failed', __FUNCTION__);
-               return;
-       }
-       global $nextorder;
-       $order = 'odd';
-       echo '<br><br><table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
-       echo '<tr><th>Type</th><th>Common name</th></tr>';
-       foreach ($objs as $obj)
+       $ports = getEmptyPortsOfType($type_id);
+       usort($ports, 'sortEmptyPorts');
+       foreach ($ports as $port)
        {
-               echo "<tr class=row_${order}><td>${obj['objtype_name']}</td>";
-               echo "<td><a href='".makeHref(array('page'=>'object', 'object_id'=>$obj['id']))."'>${obj['dname']}</a></tr>";
-               $order = $nextorder[$order];
+               if ($port_id == $port['Port_id'])
+                       continue;
+               echo "<option value='${port['Port_id']}' onclick='getElementById(\"remote_port_name\").value=\"${port['Port_name']}\"; getElementById(\"remote_object_name\").value=\"${port['Object_name']}\";'>${port['Object_name']} ${port['Port_name']}</option>\n";
        }
-       echo "</table><br>\n";
-       finishPortlet();
 }
 
-function renderObjectSpace ()
+function renderDepot ()
 {
-       global $taglist, $tagtree;
-       showMessageOrError();
-       echo "<table border=0 class=objectview>\n";
-       echo "<tr><td class=pcleft width='50%'>";
-       startPortlet ('View all by type');
-       $groupInfo = getObjectGroupInfo();
-       if ($groupInfo === NULL)
-       {
-               showError ('getObjectGroupInfo() failed', __FUNCTION__);
-               return;
-       }
-       if (count ($groupInfo) == 0)
-               echo "No objects exist in DB";
-       else
-       {
-               echo '<div align=left><ul>';
-               foreach ($groupInfo as $gi)
-                       echo "<li><a href='".makeHref(array('page'=>'objgroup', 'group_id'=>$gi['id']))."'>${gi['name']}</a> (${gi['count']})</li>";
-               echo '</ul></div>';
-       }
-       finishPortlet();
-
-       echo '</td><td class=pcright>';
-
-       startPortlet ('View all by tag');
-       if (count ($taglist) == 0)
-               echo "No tags exist in DB";
-       else
-               renderTagCloud ('object');
-       finishPortlet();
-       echo "</td></tr></table>\n";
-}
+       global $pageno, $nextorder;
+       $cellfilter = getCellFilter();
+       $objects = filterCellList (listCells ('object'), $cellfilter['expression']);
 
-function renderObjectGroup ()
-{
-       global $pageno, $tabno, $nextorder, $taglist, $tagtree;
-       showMessageOrError();
-       assertUIntArg ('group_id', __FUNCTION__, TRUE);
-       $group_id = $_REQUEST['group_id'];
-       $tagfilter = getTagFilter();
-       $tagfilter_str = getTagFilterStr ($tagfilter);
        echo "<table border=0 class=objectview>\n";
-       echo "<tr><td class=pcleft width='25%'>";
-       startPortlet ('change type');
-       $groupInfo = getObjectGroupInfo();
-       if ($groupInfo === NULL)
-       {
-               showError ('getObjectGroupInfo() failed', __FUNCTION__);
-               return;
-       }
-       if (count ($groupInfo) == 0)
-               echo "No objects exist in DB";
-       else
-       {
-               echo '<div align=left><ul>';
-               foreach ($groupInfo as $gi)
-               {
-                       echo "<li><a href='".makeHref(array('page'=>$pageno, 'group_id'=>$gi['id']))."${tagfilter_str}'>";
-                       if ($gi['id'] == $group_id)
-                               echo '<strong>';
-                       echo "${gi['name']}</a>";
-                       if ($gi['id'] == $group_id)
-                               echo '</strong>';
-                       echo " (${gi['count']})";
-                       if ($gi['id'] == $group_id)
-                               echo ' &larr;';
-                       echo "</li>";
-               }
-               echo '</ul></div>';
-       }
-       finishPortlet();
-
-       echo '</td><td class=pcleft>';
+       echo "<tr><td class=pcleft>";
 
-       $objects = getObjectList ($group_id, $tagfilter, getTFMode());
        startPortlet ('Objects (' . count ($objects) . ')');
        if ($objects === NULL)
        {
-               showError ('getObjectList() failed', __FUNCTION__);
+               showError ('Fatal error retrieving object list', __FUNCTION__);
                return;
        }
        echo '<br><br><table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
-       echo '<tr><th>Common name</th><th>Visible label</th><th>Asset tag</th><th>Barcode</th><th>Row/Rack</th><th></th></tr>';
+       echo '<tr><th>Common name</th><th>Visible label</th><th>Asset tag</th><th>Barcode</th><th>Row/Rack</th></tr>';
        $order = 'odd';
        foreach ($objects as $obj)
        {
@@ -1888,10 +1834,10 @@ function renderObjectGroup ()
                        $secondclass = 'tdleft port_highlight';
                else
                        $secondclass = 'tdleft';
-               $tags = loadRackObjectTags ($obj['id']);
+               $tags = loadEntityTags ('object', $obj['id']);
                echo "<tr class=row_${order} valign=top><td class='${secondclass}'><a href='".makeHref(array('page'=>'object', 'object_id'=>$obj['id']))."'><strong>${obj['dname']}</strong></a>";
                if (count ($tags))
-                       echo '<br><small>' . serializeTags ($tags, makeHref(array('page'=>$pageno, 'tab'=>'default', 'group_id'=>$group_id))."&") . '</small>';
+                       echo '<br><small>' . serializeTags ($tags, makeHref(array('page'=>$pageno, 'tab'=>'default')) . '&') . '</small>';
                echo "</td><td class='${secondclass}'>${obj['label']}</td>";
                echo "<td class='${secondclass}'>${obj['asset_no']}</td>";
                echo "<td class='${secondclass}'>${obj['barcode']}</td>";
@@ -1907,57 +1853,35 @@ function renderObjectGroup ()
 
        echo "</td><td class=pcright width='25%'>";
 
-       renderTagFilterPortlet ($tagfilter, 'object', 'group_id', $group_id);
+       renderCellFilterPortlet ($cellfilter, 'object');
        echo "</td></tr></table>\n";
 }
 
-function renderEmptyPortsSelect ($port_id, $type_id)
+// History viewer for history-enabled simple dictionaries.
+function renderHistory ($object_type, $object_id)
 {
-       $ports = getEmptyPortsOfType($type_id);
-       usort($ports, 'sortEmptyPorts');
-       foreach ($ports as $port)
-       {
-               if ($port_id == $port['Port_id'])
-                       continue;
-               echo "<option value='${port['Port_id']}' onclick='getElementById(\"remote_port_name\").value=\"${port['Port_name']}\"; getElementById(\"remote_object_name\").value=\"${port['Object_name']}\";'>${port['Object_name']} ${port['Port_name']}</option>\n";
-       }
-}
-
-function renderAllIPv4Allocations ()
-{
-       $addresses = getAllIPv4Allocations();
-       usort($addresses, 'sortObjectAddressesAndNames');
-       foreach ($addresses as $address)
-       {
-               echo "<option value='${address['ip']}' onclick='getElementById(\"ip\").value=\"${address['ip']}\";'>${address['object_name']} ${address['name']} ${address['ip']}</option>\n";
-       }
-}
-
-// History viewer for history-enabled simple dictionaries.
-function renderHistory ($object_type, $object_id)
-{
-       switch ($object_type)
+       switch ($object_type)
        {
                case 'row':
-                       $query = "select ctime, user_name, name, deleted, comment from RackRowHistory where id = ${object_id} order by ctime";
-                       $header = '<tr><th>change time</th><th>author</th><th>rack row name</th><th>is deleted?</th><th>rack row comment</th></tr>';
-                       $extra = 4;
+                       $query = "select ctime, user_name, name, comment from RackRowHistory where id = ${object_id} order by ctime";
+                       $header = '<tr><th>change time</th><th>author</th><th>rack row name</th><th>rack row comment</th></tr>';
+                       $extra = 3;
                        break;
                case 'rack':
                        $query =
-                               "select ctime, user_name, rh.name, rh.deleted, rr.name as name, rh.height, rh.comment " .
+                               "select ctime, user_name, rh.name, rr.name as name, rh.height, rh.comment " .
                                "from RackHistory as rh left join RackRow as rr on rh.row_id = rr.id " .
                                "where rh.id = ${object_id} order by ctime";
-                       $header = '<tr><th>change time</th><th>author</th><th>rack name</th><th>is deleted?</th><th>rack row name</th><th>rack height</th><th>rack comment</th></tr>';
-                       $extra = 6;
+                       $header = '<tr><th>change time</th><th>author</th><th>rack name</th><th>rack row name</th><th>rack height</th><th>rack comment</th></tr>';
+                       $extra = 5;
                        break;
                case 'object':
                        $query =
-                               "select ctime, user_name, RackObjectHistory.name as name, label, barcode, asset_no, deleted, has_problems, dict_value, comment " .
+                               "select ctime, user_name, RackObjectHistory.name as name, label, barcode, asset_no, has_problems, dict_value, comment " .
                                "from RackObjectHistory inner join Dictionary on objtype_id = dict_key join Chapter on Dictionary.chapter_id = Chapter.id " .
                                "where Chapter.name = 'RackObjectType' and RackObjectHistory.id=${object_id} order by ctime";
-                       $header = '<tr><th>change time</th><th>author</th><th>common name</th><th>visible label</th><th>barcode</th><th>asset no</th><th>is deleted?</th><th>has problems?</th><th>object type</th><th>comment</th></tr>';
-                       $extra = 9;
+                       $header = '<tr><th>change time</th><th>author</th><th>common name</th><th>visible label</th><th>barcode</th><th>asset no</th><th>has problems?</th><th>object type</th><th>comment</th></tr>';
+                       $extra = 8;
                        break;
                default:
                        showError ("Uknown object type '${object_type}'", __FUNCTION__);
@@ -2119,9 +2043,9 @@ function renderIPv4SpaceRecords ($tree, &$tagcache, $baseurl, $target = 0, $leve
 function renderIPv4Space ()
 {
        global $pageno, $tabno;
-       $tagfilter = getTagFilter();
-       $netlist = getIPv4NetworkList ($tagfilter, getTFMode());
-
+       $cellfilter = getCellFilter();
+       $netlist = filterCellList (listCells ('ipv4net'), $cellfilter['expression']);
+       array_walk ($netlist, 'amplifyCell');
 
        $netcount = count ($netlist);
        // expand request can take either natural values or "ALL". Zero means no expanding.
@@ -2133,12 +2057,15 @@ function renderIPv4Space ()
        startPortlet ("networks (${netcount})");
        echo '<h4>';
        if ($eid === 0)
-               echo 'auto-collapsing at threshold ' . getConfigVar ('TREE_THRESHOLD') . " (<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'eid'=>'ALL'))."'>expand all</a>)";
+               echo 'auto-collapsing at threshold ' . getConfigVar ('TREE_THRESHOLD') .
+                       " (<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'eid'=>'ALL')) .
+                       $cellfilter['urlextra'] . "'>expand all</a>)";
        elseif ($eid === 'ALL')
-               echo "expanding all (<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno))."'>auto-collapse</a>)";
+               echo "expanding all (<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno)) .
+                       $cellfilter['urlextra'] . "'>auto-collapse</a>)";
        else
        {
-               $netinfo = getIPv4NetworkInfo ($eid);
+               $netinfo = spotEntity ('ipv4net', $eid);
                echo "expanding ${netinfo['ip']}/${netinfo['mask']} (<a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno))."'>auto-collapse</a> / <a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'eid'=>'ALL'))."'>expand&nbsp;all</a>)"; 
        }
        echo "</h4><table class='widetable' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
@@ -2147,12 +2074,12 @@ function renderIPv4Space ()
                echo "<th>routed by</th>";
        echo "</tr>\n";
        $tagcache = array();
-       $baseurl = makeHref(array('page'=>$pageno, 'tab'=>$tabno)) . getTagFilterStr ($tagfilter);
+       $baseurl = makeHref(array('page'=>$pageno, 'tab'=>$tabno)) . $cellfilter['urlextra'];
        renderIPv4SpaceRecords ($tree, $tagcache, $baseurl, $eid);
        echo "</table>\n";
        finishPortlet();
        echo '</td><td class=pcright>';
-       renderTagFilterPortlet ($tagfilter, 'ipv4net');
+       renderCellFilterPortlet ($cellfilter, 'ipv4net');
        echo "</td></tr></table>\n";
 }
 
@@ -2177,7 +2104,7 @@ function renderIPv4SLB ()
                foreach (array_keys ($vipdata['lblist']) as $lb_object_id)
                        if (!in_array ($lb_object_id, $lblist))
                        {
-                               $oi = getObjectInfo ($lb_object_id);
+                               $oi = spotEntity ('object', $lb_object_id);
                                $lbdname[$lb_object_id] = $oi['dname'];
                                $lblist[] = $lb_object_id;
                        }
@@ -2199,7 +2126,7 @@ function renderIPv4SLB ()
                foreach ($summary as $vsid => $vsdata)
                {
                        echo "<tr class=row_${order}><td class=tdleft>";
-                       renderVSCell ($vsid);
+                       renderCell (spotEntity ('ipv4vs', $vsid));
                        echo "</td>";
                        foreach ($lblist as $lb_object_id)
                        {
@@ -2224,9 +2151,6 @@ function renderIPv4SLB ()
 
 function renderIPv4SpaceEditor ()
 {
-       global $pageno, $tabno;
-       showMessageOrError();
-
        // IPv4 validator
 ?>
        <script type="text/javascript">
@@ -2247,7 +2171,7 @@ function renderIPv4SpaceEditor ()
        echo "<input type=hidden name=op value=addIPv4Prefix>\n";
        // tags column
        echo '<tr><td rowspan=4><h3>assign tags</h3>';
-       renderTagSelect();
+       renderNewEntityTags();
        echo '</td>';
        // inputs column
        echo "<th class=tdright>prefix</th><td class=tdleft><input type=text name='range' size=18 class='live-validate' tabindex=1></td>";
@@ -2259,19 +2183,38 @@ function renderIPv4SpaceEditor ()
        echo "</form></table><br><br>\n";
        finishPortlet();
 
-       $addrspaceList = getIPv4NetworkList();
-       $netcount = count ($addrspaceList);
-       if ($netcount)
+       $addrspaceList = listCells ('ipv4net');
+       array_walk ($addrspaceList, 'amplifyCell');
+       if (count ($addrspaceList))
        {
-               startPortlet ("Manage existing (${netcount})");
+               startPortlet ('Manage existing (' . count ($addrspaceList) . ')');
                echo "<table class='widetable' border=0 cellpadding=5 cellspacing=0 align='center'>\n";
                echo "<tr><th>&nbsp;</th><th>prefix</th><th>name</th><th>&nbsp;</th></tr>";
                foreach ($addrspaceList as $netinfo)
                {
-                       echo "<form method=post action='".makeHrefProcess(array($op=>'updIPv4Prefix', 'id'=>$netinfo['id']))."'>";
-                       echo "<tr valign=top><td><a href='".makeHrefProcess(array('op'=>'delIPv4Prefix', 'id'=>$netinfo['id']))."'>";
-                       printImageHREF ('delete', 'Delete this IP range');
-                       echo "</a></td>\n<td class=tdleft>${netinfo['ip']}/${netinfo['mask']}</td>";
+                       echo "<form method=post action='".makeHrefProcess(array('op'=>'updIPv4Prefix', 'id'=>$netinfo['id']))."'>";
+                       echo "<tr valign=top><td>";
+                       if (getConfigVar ('IPV4_JAYWALK') == 'yes')
+                       {
+                               echo "<a href='".makeHrefProcess(array('op'=>'delIPv4Prefix', 'id'=>$netinfo['id']))."'>";
+                               printImageHREF ('destroy', 'Delete this prefix');
+                               echo "</a>";
+                       }
+                       else // only render clickable image for empty networks
+                       {
+                               $netdata = spotEntity ('ipv4net', $netinfo['id']);
+                               loadIPv4AddrList ($netdata);
+                               if (count ($netdata['addrlist']))
+                                       printImageHREF ('nodestroy', 'There are ' . count ($netdata['addrlist']) . ' allocations inside');
+                               else
+                               {
+                                       echo "<a href='".makeHrefProcess(array('op'=>'delIPv4Prefix', 'id'=>$netinfo['id']))."'>";
+                                       printImageHREF ('destroy', 'Delete this prefix');
+                                       echo "</a>";
+                               }
+
+                       }
+                       echo "</td>\n<td class=tdleft>${netinfo['ip']}/${netinfo['mask']}</td>";
                        echo "<td><input type=text name=name size=40 value='${netinfo['name']}'>";
                        echo "</td><td>";
                        printImageHREF ('save', 'Save changes', TRUE);
@@ -2361,7 +2304,7 @@ function renderIPv4Network ($id)
        else
                $page=0;
 
-       $range = getIPv4NetworkInfo ($id);
+       $range = spotEntity ('ipv4net', $id);
        loadIPv4AddrList ($range);
        echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
        echo "<tr><td colspan=2 align=center><h1>${range['ip']}/${range['mask']}</h1><h2>${range['name']}</h2></td></tr>\n";
@@ -2383,22 +2326,25 @@ function renderIPv4Network ($id)
                $backtrace = array();
                while (NULL !== ($upperid = getIPv4AddressNetworkId ($range['ip'], $clen)))
                {
-                       $upperinfo = getIPv4NetworkInfo ($upperid);
+                       $upperinfo = spotEntity ('ipv4net', $upperid);
                        $clen = $upperinfo['mask'];
-                       $backtrace[] = $upperid;
+                       $backtrace[] = $upperinfo;
                }
                $arrows = count ($backtrace);
-               foreach (array_reverse ($backtrace) as $ancestorid)
+               foreach (array_reverse ($backtrace) as $ainfo)
                {
-                       $ainfo = getIPv4NetworkInfo ($ancestorid);
                        echo "<tr><th width='50%' class=tdright>";
                        for ($i = 0; $i < $arrows; $i++)
                                echo '&uarr;';
                        $arrows--;
-                       echo "</th><td class=tdleft><a href='".makeHref(array('page'=>$pageno, 'tab'=>$tabno, 'id'=>$ainfo['id']))."'>${ainfo['ip']}/${ainfo['mask']}</a></td></tr>";
+                       echo "</th><td class=tdleft>";
+                       renderCell ($ainfo);
+                       echo "</td></tr>";
                }
                echo "<tr><th width='50%' class=tdright>&rarr;</th>";
-               echo "<td class=tdleft>${range['ip']}/${range['mask']}</td></tr>";
+               echo "<td class=tdleft>";
+               renderCell ($range);
+               echo "</td></tr>";
                // FIXME: get and display nested networks
                // $theitem = pickLeaf ($ipv4tree, $id);
        }
@@ -2423,7 +2369,7 @@ function renderIPv4Network ($id)
                echo "</tr>\n";
        }
 
-       printTagTRs (makeHref(array('page'=>'ipv4space', 'tab'=>'default'))."&");
+       printTagTRs ($range, makeHref(array('page'=>'ipv4space', 'tab'=>'default'))."&");
        echo "</table><br>\n";
        finishPortlet();
 
@@ -2528,8 +2474,7 @@ function renderIPv4Network ($id)
 
 function renderIPv4NetworkProperties ($id)
 {
-       showMessageOrError();
-       $netdata = getIPv4NetworkInfo ($id);
+       $netdata = spotEntity ('ipv4net', $id);
        echo "<center><h1>${netdata['ip']}/${netdata['mask']}</h1></center>\n";
        echo "<table border=0 cellpadding=10 cellpadding=1 align='center'>\n";
        printOpFormIntro ('editRange');
@@ -2556,7 +2501,6 @@ function renderIPv4Address ($dottedquad)
        echo "<tr><th width='50%' class=tdright>Arriving NAT connections:</th><td class=tdleft>" . count ($address['inpf']) . "</td></tr>\n";
        echo "<tr><th width='50%' class=tdright>SLB virtual services:</th><td class=tdleft>" . count ($address['lblist']) . "</td></tr>\n";
        echo "<tr><th width='50%' class=tdright>SLB real servers:</th><td class=tdleft>" . count ($address['rslist']) . "</td></tr>\n";
-       printTagTRs();
        echo "</table><br>\n";
        finishPortlet();
        echo "</td>\n";
@@ -2650,7 +2594,6 @@ function renderIPv4Address ($dottedquad)
 
 function renderIPv4AddressProperties ($dottedquad)
 {
-       showMessageOrError();
        $address = getIPv4Address ($dottedquad);
        echo "<center><h1>$dottedquad</h1></center>\n";
 
@@ -2660,41 +2603,38 @@ function renderIPv4AddressProperties ($dottedquad)
        echo "<tr><td class='tdright'>Name:</td><td class='tdleft'><input type=text name=name size=20 value='${address['name']}'></tr>";
        echo "<td class='tdright'>Reserved:</td><td class='tdleft'><input type=checkbox name=reserved size=20 ";
        echo ($address['reserved']=='yes') ? 'checked' : '';
-       echo "></tr><tr><td colspan=2 class='tdcenter'>";
+       echo "></tr><tr><td class=tdleft>";
        printImageHREF ('SAVE', 'Save changes', TRUE);
-       echo "</td></form></tr></table>\n";
-       finishPortlet();
+       echo "</td></form><td class=tdright>";
        if (empty ($address['name']) and $address['reserved'] == 'no')
-               return;
-
-       startPortlet ('release');
-       printOpFormIntro ('editAddress', array ('name' => '', 'reserved' => ''));
-       echo "<input type=submit value='release'></form>";
+               printImageHREF ('CLEAR gray');
+       else
+       {
+               printOpFormIntro ('editAddress', array ('name' => '', 'reserved' => ''));
+               printImageHREF ('CLEAR', 'Release', TRUE);
+               echo "</form>";
+       }
+       echo "</td></tr></table>\n";
        finishPortlet();
 }
 
 function renderIPv4AddressAllocations ($dottedquad)
 {
-       showMessageOrError();
        function printNewItemTR ()
        {
                global $aat;
                printOpFormIntro ('addIPv4Allocation');
                echo "<tr><td>";
                printImageHREF ('add', 'allocate', TRUE);
-               echo "</td><td><select name='object_id' tabindex=100>";
-
-               foreach (explode (',', getConfigVar ('IPV4_PERFORMERS')) as $type) 
-                       foreach (getNarrowObjectList ($type) as $object)
-                               echo "<option value='${object['id']}'>${object['dname']}</option>";
-
-               echo "</select></td><td><input type=text tabindex=101 name=bond_name size=10></td><td>";
+               echo "</td><td>";
+               printSelect (getNarrowObjectList ('IPV4OBJ_LISTSRC'), 'object_id', NULL, 100);
+               echo "</td><td><input type=text tabindex=101 name=bond_name size=10></td><td>";
                printSelect ($aat, 'bond_type', NULL, 102);
                echo "</td><td>";
                printImageHREF ('add', 'allocate', TRUE, 103);
                echo "</td></form></tr>";
        }
-       global $pageno, $tabno, $aat;
+       global $aat;
 
        $address = getIPv4Address ($dottedquad);
 
@@ -2729,12 +2669,10 @@ function renderIPv4AddressAllocations ($dottedquad)
        echo "</table><br><br>";
 }
 
-function renderNATv4ForObject ($object_id = 0)
+function renderNATv4ForObject ($object_id)
 {
-       global $root;
        function printNewItemTR ($alloclist)
        {
-               global $root;
                printOpFormIntro ('addNATv4Rule');
                echo "<tr align='center'><td>";
                printImageHREF ('add', 'Add new NAT rule', TRUE);
@@ -2760,12 +2698,9 @@ function renderNATv4ForObject ($object_id = 0)
                printImageHREF ('add', 'Add new NAT rule', TRUE, 6);
                echo "</td></tr></form>";
        }
-       global $pageno, $tabno;
        
-       $info = getObjectInfo ($object_id);
        $forwards = getNATv4ForObject ($object_id);
        $alloclist = getObjectIPv4Allocations ($object_id);
-       showMessageOrError();
        echo "<center><h2>locally performed NAT</h2></center>";
 
        echo "<table class='widetable' cellpadding=5 cellspacing=0 border=0 align='center'>\n";
@@ -2860,59 +2795,36 @@ function renderNATv4ForObject ($object_id = 0)
        echo "</table><br><br>";
 }
 
-// FIXME: move related code away into ophandler(s)
 function renderAddMultipleObjectsForm ()
 {
-       global $root, $pageno, $tabno, $nextorder;
-
-       $type_id = array();
-       $global_type_id = 0;
-       $name = array();
-       $asset_no = array();
-       $keepvalues1 = $keepvalues2 = FALSE;
-       showMessageOrError();
-
-       // Render a form for the next.
        $typelist = getObjectTypeList();
        $typelist[0] = 'select type...';
+       $max = getConfigVar ('MASSCOUNT');
+       $tabindex = 100;
 
        startPortlet ('Distinct types, same tags');
-       $max = getConfigVar ('MASSCOUNT');
        printOpFormIntro ('addObjects');
        echo '<table border=0 align=center>';
        echo "<tr><th>Object type</th><th>Common name</th><th>Visible label</th>";
        echo "<th>Asset tag</th><th>Barcode</th><th>Tags</th></tr>\n";
-       // If a user forgot to select object type on input, we keep his
-       // previous input in the form.
        for ($i = 0; $i < $max; $i++)
        {
                echo '<tr><td>';
                // Don't employ DEFAULT_OBJECT_TYPE to avoid creating ghost records for pre-selected empty rows.
-               printSelect ($typelist, "${i}_object_type_id", 0);
+               printSelect ($typelist, "${i}_object_type_id", 0, $tabindex);
                echo '</td>';
-               echo "<td><input type=text size=30 name=${i}_object_name";
-               if ($keepvalues1 and isset ($name[$i]) and (!isset ($type_id[$i]) or $type_id[$i] == 0))
-                       echo " value='${name[$i]}'";
-               echo "></td>";
-               echo "<td><input type=text size=30 name=${i}_object_label";
-               if ($keepvalues1 and isset ($label[$i]) and (!isset ($type_id[$i]) or $type_id[$i] == 0))
-                       echo " value='${label[$i]}'";
-               echo "></td>";
-               echo "<td><input type=text size=20 name=${i}_object_asset_no";
-               if ($keepvalues1 and isset ($asset_no[$i]) and (!isset ($type_id[$i]) or $type_id[$i] == 0))
-                       echo " value='${asset_no[$i]}'";
-               echo "></td>";
-               echo "<td><input type=text size=10 name=${i}_object_barcode";
-               if ($keepvalues1 and isset ($barcode[$i]) and (!isset ($type_id[$i]) or $type_id[$i] == 0))
-                       echo " value='${barcode[$i]}'";
-               echo "></td>";
+               echo "<td><input type=text size=30 name=${i}_object_name tabindex=${tabindex}></td>";
+               echo "<td><input type=text size=30 name=${i}_object_label tabindex=${tabindex}></td>";
+               echo "<td><input type=text size=20 name=${i}_object_asset_no tabindex=${tabindex}></td>";
+               echo "<td><input type=text size=10 name=${i}_object_barcode tabindex=${tabindex}></td>";
                if ($i == 0)
                {
                        echo "<td valign=top rowspan=${max}>";
-                       renderTagSelect();
+                       renderNewEntityTags();
                        echo "</td>\n";
                }
                echo "</tr>\n";
+               $tabindex++;
        }
        echo "<tr><td class=submit colspan=5><input type=submit name=got_fast_data value='Go!'></td></tr>\n";
        echo "</form></table>\n";
@@ -2922,14 +2834,12 @@ function renderAddMultipleObjectsForm ()
        printOpFormIntro ('addLotOfObjects');
        echo "<table border=0 align=center><tr><th>names</th><th>type</th></tr>";
        echo "<tr><td rowspan=3><textarea name=namelist cols=40 rows=25>\n";
-       if ($keepvalues2 and $global_type_id == 0)
-               echo $_REQUEST['namelist'];
        echo "</textarea></td><td valign=top>";
        printSelect ($typelist, "global_type_id", getConfigVar ('DEFAULT_OBJECT_TYPE'));
        echo "</td></tr>";
        echo "<tr><th>Tags</th></tr>";
        echo "<tr><td valign=top>";
-       renderTagSelect();
+       renderNewEntityTags();
        echo "</td></tr>";
        echo "<tr><td colspan=2><input type=submit name=got_very_fast_data value='Go!'></td></tr></table>\n";
        echo "</form>\n";
@@ -2946,14 +2856,14 @@ function printGreeting ()
 
 function renderSearchResults ()
 {
-       global $remote_username, $root;
+       global $root;
        $terms = trim ($_REQUEST['q']);
        if (empty ($terms))
        {
                showError ('Search string cannot be empty.', __FUNCTION__);
                return;
        }
-       if (!permitted ('objects', 'default'))
+       if (!permitted ('depot', 'default'))
        {
                showError ('You are not authorized for viewing information about objects.', __FUNCTION__);
                return;
@@ -3001,7 +2911,7 @@ function renderSearchResults ()
                {
                        $nhits++;
                        $lasthit = 'ipv4network';
-                       $summary['ipv4network'][] = getIPv4NetworkInfo ($tmp);
+                       $summary['ipv4network'][] = spotEntity ('ipv4net', $tmp);
                }
        }
        else
@@ -3056,6 +2966,13 @@ function renderSearchResults ()
                        $lasthit = 'file';
                        $summary['file'] = $tmp;
                }
+               $tmp = getRackSearchResult ($terms);
+               if (count ($tmp))
+               {
+                       $nhits += count ($tmp);
+                       $lasthit = 'rack';
+                       $summary['rack'] = $tmp;
+               }
        }
        if ($nhits == 0)
                echo "<center><h2>Nothing found for '${terms}'</h2></center>";
@@ -3103,6 +3020,9 @@ function renderSearchResults ()
                        case 'file':
                                echo "<script language='Javascript'>document.location='${root}?page=file&file_id=${record['id']}';//</script>";
                                break;
+                       case 'rack':
+                               echo "<script language='Javascript'>document.location='${root}?page=rack&rack_id=${record['id']}';//</script>";
+                               break;
                }
                return;
        }
@@ -3115,12 +3035,12 @@ function renderSearchResults ()
                        switch ($where)
                        {
                                case 'object':
-                                       startPortlet ("<a href='${root}?page=objects'>Objects</a>");
+                                       startPortlet ("<a href='${root}?page=depot'>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>';
                                        foreach ($what as $obj)
                                        {
-                                               $tags = loadRackObjectTags ($obj['id']);
+                                               $tags = loadEntityTags ('object', $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>';
@@ -3135,12 +3055,11 @@ 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>';
-                                       foreach ($what as $netinfo)
+                                       foreach ($what as $cell)
                                        {
-                                               echo "<tr class=row_${order} valign=top>";
-                                               printIPv4NetInfoTDs ($netinfo);
-                                               echo "</tr>\n";
+                                               echo "<tr class=row_${order} valign=top><td>";
+                                               renderCell ($cell);
+                                               echo "</td></tr>\n";
                                                $order = $nextorder[$order];
                                        }
                                        echo '</table>';
@@ -3168,11 +3087,11 @@ function renderSearchResults ()
                                case 'ipv4rspool':
                                        startPortlet ("<a href='${root}?page=ipv4rsplist'>RS pools</a>");
                                        echo '<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
-                                       foreach ($what as $rspool)
+                                       foreach ($what as $cell)
                                        {
-                                               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>";
+                                               echo "<tr class=row_${order}><td class=tdleft>";
+                                               renderCell ($cell);
+                                               echo "</td></tr>";
                                                $order = $nextorder[$order];
                                        }
                                        echo '</table>';
@@ -3181,12 +3100,11 @@ 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>Description</th></tr>';
-                                       foreach ($what as $vs)
+                                       foreach ($what as $cell)
                                        {
-                                               echo "<tr class=row_${order}><td class=tdleft><a href='${root}?page=ipv4vs&vs_id=${vs['id']}'>";
-                                               echo buildVServiceName ($vs);
-                                               echo "</a></td><td class=tdleft>${vs['name']}</td></tr>";
+                                               echo "<tr class=row_${order}><td class=tdleft>";
+                                               renderCell ($cell);
+                                               echo "</td></tr>";
                                                $order = $nextorder[$order];
                                        }
                                        echo '</table>';
@@ -3195,12 +3113,11 @@ 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>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']}'>";
-                                               echo $item['user_name'];
-                                               echo "</a></td><td class=tdleft>${item['user_realname']}</td></tr>";
+                                               echo "<tr class=row_${order}><td class=tdleft>";
+                                               renderCell ($item);
+                                               echo "</td></tr>";
                                                $order = $nextorder[$order];
                                        }
                                        echo '</table>';
@@ -3209,12 +3126,24 @@ function renderSearchResults ()
                                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)
+                                       foreach ($what as $cell)
                                        {
-                                               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>";
+                                               echo "<tr class=row_${order}><td class=tdleft>";
+                                               renderCell ($cell);
+                                               echo "</td></tr>";
+                                               $order = $nextorder[$order];
+                                       }
+                                       echo '</table>';
+                                       finishPortlet();
+                                       break;
+                               case 'rack':
+                                       startPortlet ("<a href='${root}?page=rackspace'>Racks</a>");
+                                       echo '<table border=0 cellpadding=5 cellspacing=0 align=center class=cooltable>';
+                                       foreach ($what as $cell)
+                                       {
+                                               echo "<tr class=row_${order}><td class=tdleft>";
+                                               renderCell ($cell);
+                                               echo "</td></tr>";
                                                $order = $nextorder[$order];
                                        }
                                        echo '</table>';
@@ -3226,7 +3155,7 @@ function renderSearchResults ()
 
 // This function prints a table of checkboxes to aid the user in toggling mount atoms
 // from one state to another. The first argument is rack data as
-// produced by getRackData(), the second is the value used for the 'unckecked' state
+// produced by amplifyCell(), the second is the value used for the 'unckecked' state
 // and the third is the value used for 'checked' state.
 // Usage contexts:
 // for mounting an object:             printAtomGrid ($data, 'F', 'T')
@@ -3239,51 +3168,61 @@ function renderAtomGrid ($data)
        $rack_id = $data['id'];
        for ($unit_no = $data['height']; $unit_no > 0; $unit_no--)
        {
-               echo "<tr><th>${unit_no}</th>";
+               echo "<tr><th><a href='javascript:;' onclick=\"toggleRowOfAtoms('${rack_id}','${unit_no}')\">${unit_no}</a></th>";
                for ($locidx = 0; $locidx < 3; $locidx++)
                {
+                       $name = "atom_${rack_id}_${unit_no}_${locidx}";
                        $state = $data[$unit_no][$locidx]['state'];
                        echo "<td class=state_${state}";
                        if (isset ($data[$unit_no][$locidx]['hl']))
                                echo $data[$unit_no][$locidx]['hl'];
                        echo ">";
                        if (!($data[$unit_no][$locidx]['enabled'] === TRUE))
-                               echo '<input type=checkbox disabled>';
+                               echo "<input type=checkbox id=${name} disabled>";
                        else
-                               echo "<input type=checkbox" . $data[$unit_no][$locidx]['checked'] . " name=atom_${rack_id}_${unit_no}_${locidx}>";
+                               echo "<input type=checkbox" . $data[$unit_no][$locidx]['checked'] . " name=${name} id=${name}>";
                        echo '</td>';
                }
                echo "</tr>\n";
        }
 }
 
-function renderUserList ()
+function renderCellList ($realm = NULL, $title = 'items', $do_amplify = FALSE)
 {
-       global $nextorder, $accounts, $root, $pageno;
+       if ($realm === NULL)
+       {
+               global $pageno;
+               $realm = $pageno;
+       }
+       global $nextorder;
+       $order = 'odd';
+       $cellfilter = getCellFilter();
+       $celllist = filterCellList (listCells ($realm), $cellfilter['expression']);
+       if ($do_amplify)
+               array_walk ($celllist, 'amplifyCell');
        echo "<table border=0 class=objectview>\n";
        echo "<tr><td class=pcleft>";
-       startPortlet ('User accounts');
+       startPortlet ($title . ' (' . count ($celllist) . ')');
        echo "<table class=cooltable border=0 cellpadding=5 cellspacing=0 align=center>\n";
-       echo "<tr><th class=tdleft>Username</th><th class=tdleft>Real name</th></tr>";
-       $order = 'odd';
-       $tagfilter = getTagFilter();
-       foreach (getUserAccounts ($tagfilter, getTFMode()) as $user)
-       {
-               echo "<tr class=row_${order} valign=top><td class=tdleft><a href='${root}?page=user&user_id=${user['user_id']}'>";
-               echo "${user['user_name']}</a>";
-               $usertags = loadUserTags ($user['user_id']);
-               if (count ($usertags))
-                       echo '<br><small>' . serializeTags ($usertags, "${root}?page=${pageno}&tab=default&") . '</small>';
-               echo "</td><td class=tdleft>${user['user_realname']}</td></li>";
+       foreach ($celllist as $cell)
+       {
+               echo "<tr class=row_${order}><td>";
+               renderCell ($cell);
+               echo "</td></tr>\n";
                $order = $nextorder[$order];
        }
        echo '</table>';
        finishPortlet();
        echo '</td><td class=pcright>';
-       renderTagFilterPortlet ($tagfilter, 'user');
+       renderCellFilterPortlet ($cellfilter, $realm);
        echo "</td></tr></table>\n";
 }
 
+function renderUserList ()
+{
+       renderCellList ('user', 'User accounts');
+}
+
 function renderUserListEditor ()
 {
        function printNewItemTR ()
@@ -3295,9 +3234,8 @@ function renderUserListEditor ()
                printImageHREF ('create', 'Add new account', TRUE, 103);
                echo "</td></tr></form>";
        }
-       global $root, $pageno, $tabno, $accounts;
-       startPortlet ('User accounts');
-       showMessageOrError();
+       $accounts = listCells ('user');
+       startPortlet ('User accounts (' . count ($accounts) . ')');
        echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
        echo "<tr><th>Username</th><th>Real name</th><th>Password</th><th>&nbsp;</th></tr>\n";
        if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
@@ -3329,8 +3267,7 @@ function renderPortMapEditor ()
 
 function renderPortMap ($editable = FALSE)
 {
-       global $nextorder, $pageno, $tabno;
-       showMessageOrError();
+       global $nextorder;
        startPortlet ("Port compatibility map");
        $ptlist = getPortTypes();
        $pclist = getPortCompat();
@@ -3366,31 +3303,27 @@ function renderPortMap ($editable = FALSE)
        finishPortlet();
 }
 
+// Find direct sub-pages and dump as a list.
+// FIXME: assume all config kids to have static titles at the moment,
+// but use some proper abstract function later.
 function renderConfigMainpage ()
 {
-       global $pageno, $root;
-       $children = getDirectChildPages ($pageno);
+       global $pageno, $page, $root;
        echo '<ul>';
-       // FIXME: assume all config kids to have static titles at the moment,
-       // but use some proper abstract function later.
-       foreach ($children as $cpageno => $child)
-               echo "<li><a href='${root}?page=${cpageno}'>" . $child['title'] . "</li>\n";
-       echo '';
+       foreach ($page as $cpageno => $cpage)
+               if (isset ($cpage['parent']) and $cpage['parent'] == $pageno)
+                       echo "<li><a href='${root}?page=${cpageno}'>" . $cpage['title'] . "</li>\n";
        echo '</ul>';
 }
 
 function renderRackPage ($rack_id)
 {
-       if ($rack_id == 0)
-       {
-               showError ('Invalid argument', __FUNCTION__);
-               return;
-       }
-       if (($rackData = getRackData ($rack_id)) == NULL)
+       if (NULL == ($rackData = spotEntity ('rack', $rack_id)))
        {
-               showError ('getRackData() failed', __FUNCTION__);
+               showError ('Rack not found', __FUNCTION__);
                return;
        }
+       amplifyCell ($rackData);
        echo "<table border=0 class=objectview cellspacing=0 cellpadding=0><tr>";
 
        // Left column with information.
@@ -3412,11 +3345,10 @@ function renderRackPage ($rack_id)
 function renderDictionary ()
 {
        global $nextorder;
-       $dict = getDict (TRUE);
        echo '<ul>';
-       foreach ($dict as $chapter_no => $chapter)
+       foreach (getChapterList() as $chapter_no => $chapter)
        {
-               $wc = count ($chapter['word']);
+               $wc = $chapter['wordc'];
                echo "<li><a href='".makeHref(array('page'=>'chapter', 'chapter_no'=>$chapter_no))."'>${chapter['name']}</a>";
                echo " (${wc} records)</li>";
        }
@@ -3457,19 +3389,18 @@ function renderChapter ($tgt_chapter_no)
 
 function renderChapterEditor ($tgt_chapter_no)
 {
-       global $pageno, $tabno, $nextorder;
+       global $nextorder;
        function printNewItemTR ()
        {
                printOpFormIntro ('add');
                echo '<tr><td>&nbsp;</td><td>';
                printImageHREF ('add', 'Add new', TRUE);
                echo "</td>";
-               echo "<td class=tdleft><input type=text name=dict_value size=32></td><td>";
-               printImageHREF ('add', 'Add new', TRUE);
+               echo "<td class=tdleft><input type=text name=dict_value size=32 tabindex=100></td><td>";
+               printImageHREF ('add', 'Add new', TRUE, 101);
                echo '</td></tr></form>';
        }
        $dict = getDict();
-       showMessageOrError();
        echo "<br><table class=cooltable border=0 cellpadding=5 cellspacing=0 align=center>\n";
        foreach ($dict as $chapter_no => $chapter)
        {
@@ -3524,33 +3455,39 @@ function renderChaptersEditor ()
        {
                printOpFormIntro ('add');
                echo '<tr><td>';
-               printImageHREF ('add', 'Add new', TRUE);
+               printImageHREF ('create', 'Add new', TRUE);
                echo "</td><td><input type=text name=chapter_name tabindex=100></td><td>&nbsp;</td><td>";
-               printImageHREF ('add', 'Add new', TRUE, 101);
+               printImageHREF ('create', 'Add new', TRUE, 101);
                echo '</td></tr></form>';
        }
-       global $pageno, $tabno;
-       showMessageOrError();
-       $dict = getDict();
+       $dict = getChapterList();
+       foreach (array_keys ($dict) as $chapter_no)
+               $dict[$chapter_no]['mapped'] = FALSE;
+       foreach (getAttrMap() as $attrinfo)
+               if ($attrinfo['type'] == 'dict')
+                       foreach ($attrinfo['application'] as $app)
+                               $dict[$app['chapter_no']]['mapped'] = TRUE;
        echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
        echo '<tr><th>&nbsp;</th><th>Chapter name</th><th>Words</th><th>&nbsp;</th></tr>';
        if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
                printNewItemTR();
-       foreach ($dict as $chapter)
+       foreach ($dict as $chapter_id => $chapter)
        {
-               $wordcount = count ($chapter['word']);
-               $sticky = $chapter['sticky'];
-               printOpFormIntro ('upd', array ('chapter_no' => $chapter['no']));
+               $wordcount = $chapter['wordc'];
+               $sticky = $chapter['sticky'] == 'yes';
+               printOpFormIntro ('upd', array ('chapter_no' => $chapter_id));
                echo '<tr>';
                echo '<td>';
                if ($sticky)
-                       printImageHREF ('nodelete', 'system chapter');
+                       printImageHREF ('nodestroy', 'system chapter');
                elseif ($wordcount > 0)
-                       printImageHREF ('nodelete', 'contains ' . $wordcount . ' word(s)');
+                       printImageHREF ('nodestroy', 'contains ' . $wordcount . ' word(s)');
+               elseif ($chapter['mapped'])
+                       printImageHREF ('nodestroy', 'used in attribute map');
                else
                {
-                       echo "<a href='".makeHrefProcess(array('op'=>'del', 'chapter_no'=>$chapter['no']))."'>";
-                       printImageHREF ('delete', 'Remove chapter');
+                       echo "<a href='".makeHrefProcess(array('op'=>'del', 'chapter_no'=>$chapter_id))."'>";
+                       printImageHREF ('destroy', 'Remove chapter');
                        echo "</a>";
                }
                echo '</td>';
@@ -3570,7 +3507,7 @@ function renderChaptersEditor ()
 
 function renderAttributes ()
 {
-       global $nextorder;
+       global $nextorder, $attrtypes;
        $attrMap = getAttrMap();
        startPortlet ('Optional attributes');
        echo "<table class=cooltable border=0 cellpadding=5 cellspacing=0 align=center>\n";
@@ -3580,7 +3517,7 @@ function renderAttributes ()
        {
                echo "<tr class=row_${order}>";
                echo "<td class=tdleft>${attr['name']}</td>";
-               echo "<td class=tdleft>${attr['type']}</td>";
+               echo "<td class=tdleft>" . $attrtypes[$attr['type']] . "</td>";
                echo '<td class=tdleft>';
                if (count ($attr['application']) == 0)
                        echo '&nbsp;';
@@ -3605,19 +3542,14 @@ function renderEditAttributesForm ()
                printOpFormIntro ('add');
                echo '<tr><td>';
                printImageHREF ('add', 'Create attribute', TRUE);
-               echo "</td><td><input type=text tabindex=100 name=attr_name></td>";
-               echo '<td><select name=attr_type tabindex=101>';
-               echo '<option value=uint>uint</option>';
-               echo '<option value=float>float</option>';
-               echo '<option value=string>string</option>';
-               echo '<option value=dict>dict</option>';
-               echo '</select></td><td>';
+               echo "</td><td><input type=text tabindex=100 name=attr_name></td><td>";
+               global $attrtypes;
+               printSelect ($attrtypes, 'attr_type', NULL, 101);
+               echo '</td><td>';
                printImageHREF ('add', 'Create attribute', TRUE, 102);
                echo '</td></tr></form>';
        }
-       global $pageno, $tabno;
        $attrMap = getAttrMap();
-       showMessageOrError();
        startPortlet ('Optional attributes');
        echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
        echo '<tr><th>&nbsp;</th><th>Name</th><th>Type</th><th>&nbsp;</th></tr>';
@@ -3660,19 +3592,16 @@ function renderEditAttrMapForm ()
                echo '<td>';
                printSelect (getObjectTypeList(), 'objtype_id', NULL, 101);
                echo '</td>';
-               $dict = getDict();
                echo '<td><select name=chapter_no tabindex=102>';
-               foreach ($dict as $chapter)
-                       if (!$chapter['sticky'])
-                               echo "<option value='${chapter['no']}'>${chapter['name']}</option>";
+               foreach (getChapterList() as $chapter)
+                       if ($chapter['sticky'] != 'yes')
+                               echo "<option value='${chapter['id']}'>${chapter['name']}</option>";
                echo '</select></td><td>';
                printImageHREF ('add', '', TRUE, 103);
                echo '</td></tr>';
                echo '</form>';
        }
-       global $pageno, $tabno;
        $attrMap = getAttrMap();
-       showMessageOrError();
        startPortlet ('Attribute map');
        echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
        echo '<tr><th>&nbsp;</th><th>Attribute name</th><th>Object type</th><th>Dictionary chapter</th><th>&nbsp;</th></tr>';
@@ -3707,13 +3636,18 @@ function renderEditAttrMapForm ()
 }
 
 function printImageHREF ($tag, $title = '', $do_input = FALSE, $tabindex = 0)
+{
+       echo getImageHREF ($tag, $title, $do_input, $tabindex);
+}
+
+function getImageHREF ($tag, $title = '', $do_input = FALSE, $tabindex = 0)
 {
        global $root, $image;
        if (!isset ($image[$tag]))
                $tag = 'error';
        $img = $image[$tag];
        if ($do_input == TRUE)
-               echo
+               return
                        "<input type=image name=submit class=icon " .
                        "src='${root}${img['path']}' " .
                        "border=0 " .
@@ -3721,7 +3655,7 @@ function printImageHREF ($tag, $title = '', $do_input = FALSE, $tabindex = 0)
                        (empty ($title) ? '' : " title='${title}'") . // JT: Add title to input hrefs too
                        ">";
        else
-               echo
+               return
                        "<img " .
                        "src='${root}${img['path']}' " .
                        "width=${img['width']} " .
@@ -3866,7 +3800,6 @@ function dragon ()
 function renderUIConfig ()
 {
        global $configCache, $nextorder;
-       showMessageOrError();
        startPortlet ('Current configuration');
        echo '<table class=cooltable border=0 cellpadding=5 cellspacing=0 align=center width="50%">';
        echo '<tr><th class=tdleft>Option</th><th class=tdleft>Value</th></tr>';
@@ -3887,7 +3820,6 @@ function renderUIConfig ()
 function renderUIConfigEditForm ()
 {
        global $configCache;
-       showMessageOrError();
        startPortlet ('Current configuration');
        echo "<table cellspacing=0 cellpadding=5 align=center class=widetable width='50%'>\n";
        echo "<tr><th class=tdleft>Option</th>";
@@ -3915,9 +3847,8 @@ function renderUIConfigEditForm ()
 
 // This function queries the gateway about current VLAN configuration and
 // renders a form suitable for submit. Ah, and it does submit processing as well.
-function renderVLANMembership ($object_id = 0)
+function renderVLANMembership ($object_id)
 {
-       showMessageOrError();
        $data = getSwitchVLANs ($object_id);
        if ($data === NULL)
        {
@@ -4053,14 +3984,8 @@ function renderVLANMembership ($object_id = 0)
        echo '</td></tr></table>';
 }
 
-function renderSNMPPortFinder ($object_id = 0)
+function renderSNMPPortFinder ($object_id)
 {
-       showMessageOrError();
-       if ($object_id <= 0)
-       {
-               showError ('Invalid argument', __FUNCTION__);
-               return;
-       }
        printOpFormIntro ('querySNMPData');
        if (!extension_loaded ('snmp'))
        {
@@ -4081,19 +4006,13 @@ this tab will not be seen any more. Good luck.<br>\n";
 function renderUIResetForm()
 {
        printOpFormIntro ('go');
-       echo "This button will reset user interface configuration to its defaults (except organization name and auth source): ";
+       echo "This button will reset user interface configuration to its defaults (except organization name): ";
        echo "<input type=submit value='proceed'>";
        echo "</form>";
 }
 
-function renderLVSConfig ($object_id = 0)
+function renderLVSConfig ($object_id)
 {
-       showMessageOrError();
-       if ($object_id <= 0)
-       {
-               showError ('Invalid argument', __FUNCTION__);
-               return;
-       }
        echo '<br>';
        printOpFormIntro ('submitSLBConfig');
        echo "<center><input type=submit value='Submit for activation'></center>";
@@ -4106,12 +4025,8 @@ function renderLVSConfig ($object_id = 0)
 function renderVirtualService ($vsid)
 {
        global $nextorder;
-       if ($vsid <= 0)
-       {
-               showError ('Invalid argument', __FUNCTION__);
-               return;
-       }
-       $vsinfo = getVServiceInfo ($vsid);
+       $vsinfo = spotEntity ('ipv4vs', $vsid);
+       amplifyCell ($vsinfo);
        echo '<table border=0 class=objectview cellspacing=0 cellpadding=0>';
        if (!empty ($vsinfo['name']))
                echo "<tr><td colspan=2 align=center><h1>${vsinfo['name']}</h1></td></tr>\n";
@@ -4123,9 +4038,9 @@ function renderVirtualService ($vsid)
        if (!empty ($vsinfo['name']))
                echo "<tr><th width='50%' class=tdright>Name:</th><td class=tdleft>${vsinfo['name']}</td></tr>\n";
        echo "<tr><th width='50%' class=tdright>Protocol:</th><td class=tdleft>${vsinfo['proto']}</td></tr>\n";
-       echo "<tr><th width='50%' class=tdright>Virtual IP address:</th><td class=tdleft><a href='".makeHref(array('page'=>'ipaddress', 'tab'=>'default', 'ip'=>$vsinfo['vid']))."'>${vsinfo['vip']}</a></td></tr>\n";
+       echo "<tr><th width='50%' class=tdright>Virtual IP address:</th><td class=tdleft><a href='".makeHref(array('page'=>'ipaddress', 'tab'=>'default', 'ip'=>$vsinfo['vip']))."'>${vsinfo['vip']}</a></td></tr>\n";
        echo "<tr><th width='50%' class=tdright>Virtual port:</th><td class=tdleft>${vsinfo['vport']}</td></tr>\n";
-       printTagTRs (makeHref(array('page'=>'ipv4vslist', 'tab'=>'default'))."&");
+       printTagTRs ($vsinfo, makeHref(array('page'=>'ipv4vslist', 'tab'=>'default'))."&");
        if (!empty ($vsinfo['vsconfig']))
        {
                echo "<tr><th class=slbconf>VS configuration:</th><td>&nbsp;</td></tr>";
@@ -4151,7 +4066,7 @@ function renderVirtualService ($vsid)
                // Pool info
                echo '<table width=100%>';
                echo "<tr><td colspan=2>";
-               renderRSPoolCell ($pool_id, $poolInfo['name']);
+               renderCell (spotEntity ('ipv4rspool', $pool_id));
                echo "</td></tr>";
                if (!empty ($poolInfo['vsconfig']))
                        echo "<tr><th>VS config</th><td class='dashed slbconf'>${poolInfo['vsconfig']}</td></tr>";
@@ -4182,8 +4097,8 @@ function renderVirtualService ($vsid)
        }
        echo "</table>\n";
        finishPortlet ();
-       echo '</td>';
-
+       echo '</td></tr><tr><td colspan=2>';
+       renderFilesPortlet ('ipv4vs', $vsid);
        echo '</tr><table>';
 }
 
@@ -4195,16 +4110,11 @@ function renderProgressBar ($percentage = 0, $theme = '')
        echo (empty ($theme) ? '' : "&theme=${theme}") . "'>";
 }
 
-function renderRSPoolServerForm ($pool_id = 0)
+function renderRSPoolServerForm ($pool_id)
 {
-       global $pageno, $tabno, $nextorder;
-       if ($pool_id <= 0)
-       {
-               showError ('Invalid pool_id', __FUNCTION__);
-               return;
-       }
-       showMessageOrError();
-       $poolInfo = getRSPoolInfo ($pool_id);
+       global $nextorder;
+       $poolInfo = spotEntity ('ipv4rspool', $pool_id);
+       amplifyCell ($poolInfo);
 
        if (($rsc = count ($poolInfo['rslist'])))
        {
@@ -4274,15 +4184,11 @@ function renderRSPoolServerForm ($pool_id = 0)
        finishPortlet();
 }
 
-function renderRSPoolLBForm ($pool_id = 0)
+function renderRSPoolLBForm ($pool_id)
 {
-       global $pageno, $tabno, $nextorder;
-       showMessageOrError();
-
-       $poolInfo = getRSPoolInfo ($pool_id);
-       $vs_list = array ();
-       foreach (getVSList() as $vsid => $vsinfo)
-               $vs_list[$vsid] = buildVServiceName ($vsinfo) . (empty ($vsinfo['name']) ? '' : " (${vsinfo['name']})");
+       global $nextorder;
+       $poolInfo = spotEntity ('ipv4rspool', $pool_id);
+       amplifyCell ($poolInfo);
 
        if (count ($poolInfo['lblist']))
        {
@@ -4300,7 +4206,7 @@ function renderRSPoolLBForm ($pool_id = 0)
                                echo "<td class=tdleft>";
                                renderLBCell ($object_id);
                                echo "</td><td class=tdleft>";
-                               renderVSCell ($vs_id);
+                               renderCell (spotEntity ('ipv4vs', $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);
@@ -4314,12 +4220,9 @@ function renderRSPoolLBForm ($pool_id = 0)
        startPortlet ('Add new');
        echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
        printOpFormIntro ('addLB');
-       echo "<tr valign=top><th>LB / VS</th><td class=tdleft><select name='object_id' tabindex=1>";
-       foreach (explode (',', getConfigVar ('NATV4_PERFORMERS')) as $type)
-               foreach (getNarrowObjectList ($type) as $object)
-                       echo "<option value='${object['id']}'>${object['dname']}</option>";
-       echo "</select> ";
-       printSelect ($vs_list, 'vs_id', NULL, 2);
+       echo "<tr valign=top><th>LB / VS</th><td class=tdleft>";
+       printSelect (getNarrowObjectList ('IPV4LB_LISTSRC'), 'object_id', NULL, 1);
+       printSelect (getIPv4VSOptions(), 'vs_id', NULL, 2);
        echo "</td><td>";
        printImageHREF ('add', 'Configure LB', TRUE, 5);
        echo "</td></tr>\n";
@@ -4329,11 +4232,11 @@ function renderRSPoolLBForm ($pool_id = 0)
        finishPortlet();
 }
 
-function renderVServiceLBForm ($vs_id = 0)
+function renderVServiceLBForm ($vs_id)
 {
-       global $pageno, $tabno, $nextorder;
-       showMessageOrError();
-       $vsinfo = getVServiceInfo ($vs_id);
+       global $nextorder;
+       $vsinfo = spotEntity ('ipv4vs', $vs_id);
+       amplifyCell ($vsinfo);
 
        if (count ($vsinfo['rspool']))
        {
@@ -4351,7 +4254,7 @@ function renderVServiceLBForm ($vs_id = 0)
                                echo "<td class=tdleft>";
                                renderLBCell ($object_id);
                                echo "</td><td class=tdleft>";
-                               renderRSPoolCell ($pool_id, $rspinfo['name']);
+                               renderCell (spotEntity ('ipv4rspool', $pool_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);
@@ -4362,18 +4265,12 @@ function renderVServiceLBForm ($vs_id = 0)
                finishPortlet();
        }
 
-       $rsplist = array();
-       foreach (getRSPoolList() as $pool_id => $poolInfo)
-               $rsplist[$pool_id] = $poolInfo['name'];
        startPortlet ('Add new');
        echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
        printOpFormIntro ('addLB');
-       echo "<tr valign=top><th>LB / RS pool</th><td class=tdleft><select name='object_id' tabindex=1>";
-       foreach (explode (',', getConfigVar ('NATV4_PERFORMERS')) as $type)
-               foreach (getNarrowObjectList ($type) as $object)
-                       echo "<option value='${object['id']}'>${object['dname']}</option>";
-       echo "</select> ";
-       printSelect ($rsplist, 'pool_id', NULL, 2);
+       echo "<tr valign=top><th>LB / RS pool</th><td class=tdleft>";
+       printSelect (getNarrowObjectList ('IPV4LB_LISTSRC'), 'object_id', NULL, 1);
+       printSelect (getIPv4RSPoolOptions(), 'pool_id', NULL, 2);
        echo "</td><td>";
        printImageHREF ('add', 'Configure LB', TRUE, 5);
        echo "</td></tr>\n";
@@ -4383,20 +4280,16 @@ function renderVServiceLBForm ($vs_id = 0)
        finishPortlet();
 }
 
-function renderRSPool ($pool_id = 0)
+function renderRSPool ($pool_id)
 {
        global $nextorder;
-       if ($pool_id <= 0)
-       {
-               showError ('Invalid pool_id', __FUNCTION__);
-               return;
-       }
-       $poolInfo = getRSPoolInfo ($pool_id);
+       $poolInfo = spotEntity ('ipv4rspool', $pool_id);
        if ($poolInfo == NULL)
        {
-               showError ('getRSPoolInfo() returned NULL', __FUNCTION__);
+               showError ('Could not load data!', __FUNCTION__);
                return;
        }
+       amplifyCell ($poolInfo);
 
        echo "<table border=0 class=objectview cellspacing=0 cellpadding=0>";
        if (!empty ($poolInfo['name']))
@@ -4409,7 +4302,7 @@ function renderRSPool ($pool_id = 0)
                echo "<tr><th width='50%' class=tdright>Pool name:</th><td class=tdleft>${poolInfo['name']}</td></tr>\n";
        echo "<tr><th width='50%' class=tdright>Real servers:</th><td class=tdleft>" . count ($poolInfo['rslist']) . "</td></tr>\n";
        echo "<tr><th width='50%' class=tdright>Load balancers:</th><td class=tdleft>" . count ($poolInfo['lblist']) . "</td></tr>\n";
-       printTagTRs (makeHref(array('page'=>'ipv4rsplist', 'tab'=>'default'))."&");
+       printTagTRs ($poolInfo, makeHref(array('page'=>'ipv4rsplist', 'tab'=>'default'))."&");
        if (!empty ($poolInfo['vsconfig']))
        {
                echo "<tr><th width='50%' class=tdright>VS configuration:</th><td>&nbsp;</td></tr>\n";
@@ -4431,7 +4324,7 @@ function renderRSPool ($pool_id = 0)
                foreach ($vslist as $vs_id => $configs)
        {
                echo "<tr valign=top class=row_${order}><td class=tdleft><a href='".makeHref(array('page'=>'ipv4vs', 'vs_id'=>$vs_id))."'>";
-               renderVSCell ($vs_id);
+               renderCell (spotEntity ('ipv4vs', $vs_id));
                echo "</td><td>";
                renderLBCell ($object_id);
                echo "</td><td class=slbconf>${configs['vsconfig']}</td>";
@@ -4458,42 +4351,19 @@ function renderRSPool ($pool_id = 0)
        }
        echo "</table>\n";
        finishPortlet();
-
+       echo "</td></tr><tr><td colspan=2>\n";
+       renderFilesPortlet ('ipv4rspool', $pool_id);
        echo "</td></tr></table>\n";
 }
 
 function renderVSList ()
 {
-       global $pageno, $nextorder;
-       $tagfilter = getTagFilter();
-       $vslist = getVSList ($tagfilter, getTFMode());
-       echo "<table border=0 class=objectview>\n";
-       echo "<tr><td class=pcleft>";
-
-       startPortlet ('Virtual services (' . count ($vslist) . ')');
-       echo "<table class=widetable border=0 cellpadding=10 cellspacing=0 align=center>\n";
-       echo "<tr><th>endpoint, name, tags</th><th>VS configuration</th><th>RS configuration</th></tr>";
-       $order = 'odd';
-       foreach ($vslist as $vsid => $vsinfo)
-       {
-               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];
-       }
-       echo "</table>";
-       finishPortlet();
-       echo '</td><td class=pcright>';
-       renderTagFilterPortlet ($tagfilter, 'ipv4vs');
-       echo '</td></tr></table>';
+       renderCellList ('ipv4vs', 'Virtual services');
 }
 
 function renderVSListEditForm ()
 {
-       global $pageno, $tabno, $nextorder;
-       showMessageOrError();
+       global $nextorder;
        $protocols = array ('TCP' => 'TCP', 'UDP' => 'UDP');
 
        startPortlet ('Add new');
@@ -4512,14 +4382,14 @@ function renderVSListEditForm ()
        printImageHREF ('CREATE', 'create virtual service', TRUE);
        echo "</td></tr><tr><th>VS configuration</th><td colspan=4 class=tdleft><textarea name=vsconfig rows=10 cols=80></textarea></td>\n";
        echo "<td rowspan=2><h3>assign tags</h3>";
-       renderTagSelect();
+       renderNewEntityTags();
        echo "</td></tr>";
        echo "<tr><th>RS configuration</th><td colspan=4 class=tdleft><textarea name=rsconfig rows=10 cols=80></textarea></td></tr>\n";
        echo "</table>";
        echo "</form>\n";
        finishPortlet();
 
-       $vslist = getVSList();
+       $vslist = listCells ('ipv4vs');
        if (!count ($vslist))
                return;
        startPortlet ('Manage existing (' . count ($vslist) . ')');
@@ -4557,48 +4427,12 @@ function renderVSListEditForm ()
 
 function renderRSPoolList ()
 {
-       global $pageno, $nextorder;
-       $tagfilter = getTagFilter();
-       $pool_list = getRSPoolList ($tagfilter, getTFMode());
-       if ($pool_list === NULL)
-       {
-               showError ('getRSPoolList() failed', __FUNCTION__);
-               return;
-       }
-       echo "<table border=0 class=objectview>\n";
-       echo "<tr><td class=pcleft>";
-       startPortlet ('RS pools (' . count ($pool_list) . ')');
-       echo "<table class=widetable border=0 cellpadding=10 cellspacing=0 align=center>\n";
-       echo "<tr><th>name, refcnt, tags</th><th>VS configuration</th><th>RS configuration</th></tr>";
-       $order = 'odd';
-       foreach ($pool_list as $pool_id => $pool_info)
-       {
-               $pooltags = loadIPv4RSPoolTags ($pool_id);
-               echo "<tr valign=top class=row_${order}><td class=tdleft>";
-               echo "<a href='".makeHref(array('page'=>'ipv4rspool', 'pool_id'=>$pool_id))."'>" . (empty ($pool_info['name']) ? 'ANONYMOUS' : $pool_info['name']) . '</a>';
-               echo ($pool_info['refcnt'] ? ", ${pool_info['refcnt']}" : '');
-               if (count ($pooltags))
-               {
-                       echo '<br>';
-                       echo serializeTags ($pooltags, makeHref(array('page'=>$pageno, 'tab'=>'default'))."&");
-               }
-               echo "</td><td class=tdleft><pre>${pool_info['vsconfig']}</pre></td>";
-               echo "<td class=tdleft><pre>${pool_info['rsconfig']}</pre></td>";
-               echo "</tr>\n";
-               $order = $nextorder[$order];
-       }
-       echo "</table>";
-       finishPortlet ();
-       echo '</td><td class=pcright>';
-       renderTagFilterPortlet ($tagfilter, 'ipv4rspool');
-       echo '</td></tr></table>';
+       renderCellList ('ipv4rspool', 'RS pools');
 }
 
 function editRSPools ()
 {
-       global $pageno, $tabno, $nextorder;
-       showMessageOrError();
-
+       global $nextorder;
        startPortlet ('Add new');
        printOpFormIntro ('add');
        echo "<table class=widetable border=0 cellpadding=10 cellspacing=0 align=center>\n";
@@ -4607,13 +4441,13 @@ function editRSPools ()
        printImageHREF ('CREATE', 'create real server pool', TRUE);
        echo "</td></tr><tr><th>VS configuration</th><td><textarea name=vsconfig rows=10 cols=80></textarea></td>";
        echo "<td rowspan=2><h3>assign tags</h3>";
-       renderTagSelect();
+       renderNewEntityTags();
        echo "</td></tr>";
        echo "<tr><th>RS configuration</th><td><textarea name=rsconfig rows=10 cols=80></textarea></td></tr>";
        echo "</table></form>";
        finishPortlet();
 
-       $pool_list = getRSPoolList();
+       $pool_list = listCells ('ipv4rspool');
        if (!count ($pool_list))
                return;
        startPortlet ('Manage existing (' . count ($pool_list) . ')');
@@ -4624,7 +4458,7 @@ function editRSPools ()
        {
                printOpFormIntro ('upd', array ('pool_id' => $pool_id));
                echo "<tr valign=top class=row_${order}><td>";
-               if ($pool_info['refcnt'])
+               if ($pool_info['refcnt'] or $pool_info['rscount'])
                        printImageHREF ('nodelete', 'RS pool is used ' . $pool_info['refcnt'] . ' time(s)');
                else
                {
@@ -4648,7 +4482,7 @@ function renderRealServerList ()
 {
        global $nextorder;
        $rslist = getRSList ();
-       $pool_list = getRSPoolList ();
+       $pool_list = listCells ('ipv4rspool');
        echo "<table class=widetable border=0 cellpadding=10 cellspacing=0 align=center>\n";
        echo "<tr><th>RS pool</th><th>in service</th><th>real IP address</th><th>real port</th><th>RS configuration</th></tr>";
        $order = 'even';
@@ -4680,29 +4514,22 @@ function renderLBList ()
        global $nextorder;
        echo "<table class=widetable border=0 cellpadding=10 cellspacing=0 align=center>\n";
        echo "<tr><th>Object</th><th>RS pools configured</th></tr>";
-       $oicache = array();
        $order = 'odd';
        foreach (getLBList() as $object_id => $poolcount)
        {
-               if (!isset ($oicache[$object_id]))
-                       $oicache[$object_id] = getObjectInfo ($object_id);
+               $oi = spotEntity ('object', $object_id);
                echo "<tr valign=top class=row_${order}><td><a href='".makeHref(array('page'=>'object', 'object_id'=>$object_id))."'>";
-               echo $oicache[$object_id]['dname'] . '</a></td>';
+               echo $oi['dname'] . '</a></td>';
                echo "<td>${poolcount}</td></tr>";
                $order = $nextorder[$order];
        }
        echo "</table>";
 }
 
-function renderRSPoolRSInServiceForm ($pool_id = 0)
+function renderRSPoolRSInServiceForm ($pool_id)
 {
-       if ($pool_id <= 0)
-       {
-               showError ('Invalid pool_id', __FUNCTION__);
-               return;
-       }
-       showMessageOrError();
-       $poolInfo = getRSPoolInfo ($pool_id);
+       $poolInfo = spotEntity ('ipv4rspool', $pool_id);
+       amplifyCell ($poolInfo);
        printOpFormIntro ('upd', array ('rscount' => count ($poolInfo['rslist'])));
        echo "<table class=widetable border=0 cellpadding=10 cellspacing=0 align=center>\n";
        echo "<tr><th>RS address</th><th>RS port</th><th>RS configuration</th><th>in service</th></tr>\n";
@@ -4720,21 +4547,15 @@ function renderRSPoolRSInServiceForm ($pool_id = 0)
        echo "</td></tr></table>\n</form>";
 }
 
-function renderLivePTR ($id = 0)
+function renderLivePTR ($id)
 {
-       if ($id == 0)
-       {
-               showError ("Invalid argument", __FUNCTION__);
-               return;
-       }
-       showMessageOrError();
        if (isset($_REQUEST['pg']))
                $page = $_REQUEST['pg'];
        else
                $page=0;
        global $pageno, $tabno;
        $maxperpage = getConfigVar ('IPV4_ADDRS_PER_PAGE');
-       $range = getIPv4NetworkInfo ($id);
+       $range = spotEntity ('ipv4net', $id);
        loadIPv4AddrList ($range);
        echo "<center><h1>${range['ip']}/${range['mask']}</h1><h2>${range['name']}</h2></center>\n";
 
@@ -4767,6 +4588,7 @@ function renderLivePTR ($id = 0)
        echo "<table class='widetable' border=0 cellspacing=0 cellpadding=5 align='center'>\n";
        echo "<tr><th>address</th><th>current name</th><th>DNS data</th><th>import</th></tr>\n";
        $idx = 1;
+       $box_counter = 1;
        $cnt_match = $cnt_mismatch = $cnt_missing = 0;
        for ($ip = $startip; $ip <= $endip; $ip++)
        {
@@ -4808,13 +4630,15 @@ function renderLivePTR ($id = 0)
                echo "'><a href='".makeHref(array('page'=>'ipaddress', 'ip'=>$straddr))."'>${straddr}</a></td>";
                echo "<td class=tdleft>${addr['name']}</td><td class=tdleft>${ptrname}</td><td>";
                if ($print_cbox)
-                       echo "<input type=checkbox name=import_${idx} tabindex=${idx}>";
+                       echo "<input type=checkbox name=import_${idx} tabindex=${idx} id=atom_1_" . $box_counter++ . "_1>";
                else
                        echo '&nbsp;';
                echo "</td></tr>\n";
                $idx++;
        }
-       echo "<tr><td colspan=4 align=center><input type=submit value='Import selected records'></td></tr>";
+       echo "<tr><td colspan=3 align=center><input type=submit value='Import selected records'></td><td>";
+       echo --$box_counter ? "<a href='javascript:;' onclick=\"toggleColumnOfAtoms(1, 1, ${box_counter})\">(toggle selection)</a>" : '&nbsp;';
+       echo "</td></tr>";
        echo "</table>";
        echo "</form>";
        finishPortlet();
@@ -4833,17 +4657,9 @@ function renderLivePTR ($id = 0)
        echo "</td></tr></table>\n";
 }
 
-function renderAutoPortsForm ($object_id = 0)
+function renderAutoPortsForm ($object_id)
 {
-       if ($object_id <= 0)
-       {
-               showError ('Invalid object_id', __FUNCTION__);
-               return;
-       }
-       // If the below call has any data to display, the non-default redirection from the generator
-       // has failed. Don't ignore the message log anyway.
-       showMessageOrError();
-       $info = getObjectInfo ($object_id);
+       $info = spotEntity ('object', $object_id);
        $ptlist = readChapter ('PortType');
        echo "<table class='widetable' border=0 cellspacing=0 cellpadding=5 align='center'>\n";
        echo "<caption>The following ports can be quickly added:</caption>";
@@ -4859,6 +4675,7 @@ function renderAutoPortsForm ($object_id = 0)
 
 function renderTagRowForViewer ($taginfo, $level = 0)
 {
+       $self = __FUNCTION__;
        if (!count ($taginfo['kids']))
                $level++; // Shift instead of placing a spacer. This won't impact any nested nodes.
        echo "<tr><td align=left style='padding-left: " . ($level * 16) . "px;'>";
@@ -4868,25 +4685,13 @@ function renderTagRowForViewer ($taginfo, $level = 0)
        echo $taginfo['tag'] . '</span>';
        echo "</td></tr>\n";
        foreach ($taginfo['kids'] as $kid)
-               renderTagRowForViewer ($kid, $level + 1);
-}
-
-// FIXME: generated hyperlink must depend on the realm given
-function renderTagRowForCloud ($taginfo, $realm, $level = 0)
-{
-       echo "<tr><td align=left style='padding-left: " . ($level * 16) . "px;'>";
-       echo "<a href='".makeHref(array('page'=>'objgroup', 'group_id'=>0, 'tagfilter[]'=>$taginfo['id']))."'>";
-       echo $taginfo['tag'] . '</a>';
-       if (isset ($taginfo['refcnt'][$realm]))
-               echo ' (' . $taginfo['refcnt'][$realm] . ')';
-       echo "</td></tr>\n";
-       foreach ($taginfo['kids'] as $kid)
-               renderTagRowForCloud ($kid, $realm, $level + 1);
+               $self ($kid, $level + 1);
 }
 
 function renderTagRowForEditor ($taginfo, $level = 0)
 {
-       global $pageno, $tabno, $taglist;
+       $self = __FUNCTION__;
+       global $taglist;
        if (!count ($taginfo['kids']))
                $level++; // Idem
        echo "<tr><td align=left style='padding-left: " . ($level * 16) . "px;'>";
@@ -4917,7 +4722,7 @@ function renderTagRowForEditor ($taginfo, $level = 0)
        printImageHREF ('save', 'Save changes', TRUE);
        echo "</form></td></tr>\n";
        foreach ($taginfo['kids'] as $kid)
-               renderTagRowForEditor ($kid, $level + 1);
+               $self ($kid, $level + 1);
 }
 
 function renderTagTree ()
@@ -4933,19 +4738,6 @@ function renderTagTree ()
        echo '</table></center>';
 }
 
-function renderTagCloud ($realm = '')
-{
-       global $taglist, $tagtree;
-       echo '<table>';
-       foreach (getObjectiveTagTree ($tagtree, $realm) as $taginfo)
-       {
-               echo '<tr>';
-               renderTagRowForCloud ($taginfo, $realm);
-               echo "</tr>\n";
-       }
-       echo '</table>';
-}
-
 function renderTagTreeEditor ()
 {
        function printNewItemTR ()
@@ -4963,7 +4755,6 @@ function renderTagTreeEditor ()
                echo "</td></tr></form>\n";
        }
        global $taglist, $tagtree;
-       showMessageOrError();
 
        $otags = getOrphanedTags();
        if (count ($otags))
@@ -5002,144 +4793,168 @@ function renderTagTreeEditor ()
        finishPortlet();
 }
 
-// Output a sequence of OPTION elements, selecting those, which are present on the
-// explicit tags list.
-function renderTagOption ($taginfo, $level = 0)
+function renderTagCheckbox ($inputname, $preselect, $taginfo, $level = 0)
 {
-       global $expl_tags;
        $self = __FUNCTION__;
-       $selected = tagOnChain ($taginfo, $expl_tags) ? ' selected' : '';
-       echo '<option value=' . $taginfo['id'] . "${selected}>";
-       for ($i = 0; $i < $level; $i++)
-               echo '-- ';
-       echo $taginfo['tag'] . "</option>\n";
-       foreach ($taginfo['kids'] as $kid)
-               $self ($kid, $level + 1);
-}
-
-function renderTagCheckbox ($taginfo, $level = 0)
-{
-       global $expl_tags;
-       $self = __FUNCTION__;
-       $selected = tagOnChain ($taginfo, $expl_tags) ? ' checked' : '';
-       echo "<tr><td colspan=2 align=left style='padding-left: " . ($level * 16) . "px;'>";
-       echo '<input type=checkbox name=taglist[] value=' . $taginfo['id'] . "${selected}> ";
+       if (tagOnChain ($taginfo, $preselect))
+       {
+               $selected = ' checked';
+               $class = 'seltagbox';
+       }
+       else
+       {
+               $selected = '';
+               $class = 'tagbox';
+       }
+       echo "<tr><td colspan=2 class=${class} style='padding-left: " . ($level * 16) . "px;'>";
+       echo "<input type=checkbox name='${inputname}[]' value='${taginfo['id']}'${selected}> ";
        echo $taginfo['tag'] . "</td></tr>\n";
        foreach ($taginfo['kids'] as $kid)
-               $self ($kid, $level + 1);
-}
-
-// Idem, but select those, which are shown on the $_REQUEST['tagfiler'] array.
-// Ignore tag ids, which can't be found on the tree.
-function renderTagOptionForFilter ($taginfo, $tagfilter, $realm, $level = 0)
-{
-       $selected = tagOnIdList ($taginfo, $tagfilter) ?' selected' : '';
-       echo '<option value=' . $taginfo['id'] . "${selected}>";
-       for ($i = 0; $i < $level; $i++)
-               echo '-- ';
-       echo $taginfo['tag'] . (isset ($taginfo['refcnt'][$realm]) ? ' (' . $taginfo['refcnt'][$realm] . ')' : '') . "</option>\n";
-       foreach ($taginfo['kids'] as $kid)
-               renderTagOptionForFilter ($kid, $tagfilter, $realm, $level + 1);
+               $self ($inputname, $preselect, $kid, $level + 1);
 }
 
-function renderEntityTags ($entity_id = 0)
+function renderEntityTags ($entity_id)
 {
-       global $tagtree, $expl_tags, $pageno, $page, $etype_by_pageno;
-       if ($entity_id <= 0)
-       {
-               showError ('Invalid or missing arguments', __FUNCTION__);
-               return;
-       }
-       showMessageOrError();
-       $entity_realm = $etype_by_pageno[$pageno];
+       global $tagtree, $target_given_tags, $pageno, $page, $target_given_tags;
        $bypass_name = $page[$pageno]['bypass'];
        startPortlet ('Tag list');
-       if (count ($expl_tags))
-               echo '<h3>(' . serializeTags ($expl_tags) . ')</h3>';
-       echo '<table border=0 align=center>';
-       printOpFormIntro ('saveTags', array ($bypass_name => $entity_id));
+       echo '<table border=0 cellspacing=0 cellpadding=3 align=center>';
+       printOpFormIntro ('saveTags');
+       // Show a tree of tags with preselection, which matches current chain.
        foreach ($tagtree as $taginfo)
-               renderTagCheckbox ($taginfo);
+               renderTagCheckbox ('taglist', $target_given_tags, $taginfo);
        echo '<tr><td class=tdleft>';
        printImageHREF ('SAVE', 'Save changes', TRUE);
        echo "</form></td><td class=tdright>";
-       printOpFormIntro ('saveTags', array ($bypass_name => $entity_id, 'taglist[]' => ''));
-       printImageHREF ('CLEAR', 'Reset all tags', TRUE);
-       echo '</form></td></tr></table>';
+       if (!count ($target_given_tags))
+               printImageHREF ('CLEAR gray');
+       else
+       {
+               printOpFormIntro ('saveTags', array ('taglist[]' => ''));
+               printImageHREF ('CLEAR', 'Reset all tags', TRUE);
+               echo '</form>';
+       }
+       echo '</td></tr></table>';
        finishPortlet();
 }
 
-function printTagTRs ($baseurl = '')
+function printTagTRs ($cell, $baseurl = '')
 {
-       global $expl_tags, $impl_tags, $auto_tags;
-       if (getConfigVar ('SHOW_EXPLICIT_TAGS') == 'yes' and count ($expl_tags))
+       if (getConfigVar ('SHOW_EXPLICIT_TAGS') == 'yes' and count ($cell['etags']))
        {
-               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";
+               echo "<tr><th width='50%' class=tagchain>Explicit tags:</th><td class=tagchain>";
+               echo serializeTags ($cell['etags'], $baseurl) . "</td></tr>\n";
        }
-       if (getConfigVar ('SHOW_IMPLICIT_TAGS') == 'yes' and count ($impl_tags))
+       if (getConfigVar ('SHOW_IMPLICIT_TAGS') == 'yes' and count ($cell['itags']))
        {
-               echo "<tr><th width='50%' class=tdright>Implicit tags:</th><td class=tdleft>";
-               echo serializeTags ($impl_tags, $baseurl) . "</td></tr>\n";
+               echo "<tr><th width='50%' class=tagchain>Implicit tags:</th><td class=tagchain>";
+               echo serializeTags ($cell['itags'], $baseurl) . "</td></tr>\n";
        }
-       if (getConfigVar ('SHOW_AUTOMATIC_TAGS') == 'yes' and count ($auto_tags))
+       if (getConfigVar ('SHOW_AUTOMATIC_TAGS') == 'yes' and count ($cell['atags']))
        {
-               echo "<tr><th width='50%' class=tdright>Automatic tags:</th><td class=tdleft>";
-               echo serializeTags ($auto_tags) . "</td></tr>\n";
+               echo "<tr><th width='50%' class=tagchain>Automatic tags:</th><td class=tagchain>";
+               echo serializeTags ($cell['atags']) . "</td></tr>\n";
        }
 }
 
-// Detect, filter and return requested tag filter mode: either 'and' or 'or'.
-function getTFMode ()
-{
-       if (isset ($_REQUEST['tfmode']) and $_REQUEST['tfmode'] == 'all')
-               return 'all';
-       return 'any';
-}
-
-// Output a portlet with currently selected tags and prepare a form for update.
-function renderTagFilterPortlet ($tagfilter, $realm, $bypass_name = '', $bypass_value = '')
+// This one is going to replace the tag filter.
+function renderCellFilterPortlet ($preselect, $realm, $bypass_name = '', $bypass_value = '')
 {
        global $pageno, $tabno, $taglist, $tagtree;
-       $objectivetags = getObjectiveTagTree ($tagtree, $realm);
-       startPortlet ('Tag filter');
-       if (!count ($objectivetags))
+       startPortlet ('filter');
+       echo "<form method=get>\n";
+       echo '<table border=0 align=center>';
+       $ruler = "<tr><td colspan=2 class=tagbox><hr></td></tr>\n";
+       $hr = '';
+       // and/or block
+       if (getConfigVar ('FILTER_SUGGEST_ANDOR') == 'yes' or strlen ($preselect['andor']))
+       {
+               echo $hr;
+               $hr = $ruler;
+               $andor = strlen ($preselect['andor']) ? $preselect['andor'] : getConfigVar ('FILTER_DEFAULT_ANDOR');
+               echo '<tr>';
+               foreach (array ('and', 'or') as $boolop)
+               {
+                       $class = $andor == $boolop ? 'seltagbox' : 'tagbox';
+                       $checked = $andor == $boolop ? ' checked' : '';
+                       echo "<td class=${class}><input type=radio name=andor value=${boolop}";
+                       echo $checked . ">${boolop}</input></td>";
+               }
+       }
+       // tags block
+       if (getConfigVar ('FILTER_SUGGEST_TAGS') == 'yes' or count ($preselect['tagidlist']))
        {
-               echo "None defined for current realm.<br>";
-               return;
+               echo $hr;
+               $hr = $ruler;
+               // Show a tree of tags, pre-select according to currently requested list filter.
+               global $tagtree;
+               $objectivetags = getObjectiveTagTree ($tagtree, $realm);
+               if (!count ($objectivetags))
+                       echo "<tr><td colspan=2 class='tagbox sparenetwork'>(nothing is tagged yet)</td></tr>";
+               else
+                       foreach ($objectivetags as $taginfo)
+                               renderTagCheckbox ('cft', buildTagChainFromIds ($preselect['tagidlist']), $taginfo);
+       }
+       // predicates block
+       if (getConfigVar ('FILTER_SUGGEST_PREDICATES') == 'yes' or count ($preselect['pnamelist']))
+       {
+               echo $hr;
+               $hr = $ruler;
+               global $pTable;
+               $myPredicates = array();
+               $psieve = getConfigVar ('FILTER_PREDICATE_SIEVE');
+               // Repack matching predicates in a way, which tagOnChain() understands.
+               foreach (array_keys ($pTable) as $pname)
+                       if (mb_ereg_match ($psieve, $pname))
+                               $myPredicates[] = array ('id' => $pname, 'tag' => $pname, 'kids' => array());
+               if (!count ($myPredicates))
+                       echo "<tr><td colspan=2 class='tagbox sparenetwork'>(no predicates to show)</td></tr>";
+               else
+               {
+                       // Repack preselect likewise.
+                       $myPreselect = array();
+                       foreach ($preselect['pnamelist'] as $pname)
+                               $myPreselect[] = array ('id' => $pname);
+                       foreach ($myPredicates as $pinfo)
+                               renderTagCheckbox ('cfp', $myPreselect, $pinfo);
+               }
        }
-       echo '<table border=0 align=center>';
-
-       if (count ($tagfilter))
-               echo '<h3>(' . serializeTags (buildTagChainFromIds ($tagfilter)) . ')</h3>';
-       echo "<form method=get>\n";
-       echo "<input type=hidden name=page value=${pageno}>\n";
-       echo "<input type=hidden name=tab value=${tabno}>\n";
-       if ($bypass_name != '')
-               echo "<input type=hidden name=${bypass_name} value='${bypass_value}'>\n";
-       echo '<tr><td colspan=2><select name=tagfilter[] multiple size=' . getConfigVar ('MAXSELSIZE') . '>';
-       foreach ($objectivetags as $taginfo)
-               renderTagOptionForFilter ($taginfo, complementByKids ($tagfilter), $realm);
-       echo '</select></td></tr><tr><td>';
-//     $tfmode = getTFMode();
-//     echo '<input type=radio name=tfmode value=all' . ($tfmode == 'all' ? ' checked' : '') . '>all ';
-//     echo '<input type=radio name=tfmode value=any' . ($tfmode == 'any' ? ' checked' : '') . '>any ';
-       printImageHREF ('apply', 'Apply filter', TRUE);
-       echo "</form></td><td>";
-
-       // "reset"
-       echo "<form method=get>\n";
-       echo "<input type=hidden name=page value=${pageno}>\n";
-       echo "<input type=hidden name=tab value=${tabno}>\n";
-       if ($bypass_name != '')
-               echo "<input type=hidden name=${bypass_name} value='${bypass_value}'>\n";
-       printImageHREF ('clear', 'reset', TRUE);
-       echo '</form></td></tr></table>';
+       // extra code
+       if (getConfigVar ('FILTER_SUGGEST_EXTRA') == 'yes' or strlen ($preselect['extratext']))
+       {
+               echo $hr;
+               $hr = $ruler;
+               $class = isset ($preselect['extraclass']) ? 'class=' . $preselect['extraclass'] : '';
+               echo "<tr><td colspan=2><textarea name=cfe ${class}>\n" . $preselect['extratext'];
+               echo "</textarea></td></tr>\n";
+       }
+       // submit block
+       {
+               echo $hr;
+               $hr = $ruler;
+               // "apply"
+               echo '<tr><td>';
+               echo "<input type=hidden name=page value=${pageno}>\n";
+               echo "<input type=hidden name=tab value=${tabno}>\n";
+               if ($bypass_name != '')
+                       echo "<input type=hidden name=${bypass_name} value='${bypass_value}'>\n";
+               printImageHREF ('apply', 'Apply filter', TRUE);
+               echo "</form></td><td>";
+               // "reset"
+               echo "<form method=get>\n";
+               echo "<input type=hidden name=page value=${pageno}>\n";
+               echo "<input type=hidden name=tab value=${tabno}>\n";
+               if ($bypass_name != '')
+                       echo "<input type=hidden name=${bypass_name} value='${bypass_value}'>\n";
+               printImageHREF ('clear', 'reset', TRUE);
+               echo '</form></td></tr>';
+       }
+       echo '</table>';
        finishPortlet();
 }
 
 // Dump all tags in a single SELECT element.
-function renderTagSelect ()
+function renderNewEntityTags ()
 {
        global $taglist, $tagtree;
        if (!count ($taglist))
@@ -5149,7 +4964,7 @@ function renderTagSelect ()
        }
        echo '<div class=tagselector><table border=0 align=center>';
        foreach ($tagtree as $taginfo)
-               renderTagCheckbox ($taginfo);
+               renderTagCheckbox ('taglist', array(), $taginfo);
        echo '</table></div>';
 }
 
@@ -5158,14 +4973,13 @@ function renderTagRollerForRow ($row_id)
        $a = rand (1, 20);
        $b = rand (1, 20);
        $sum = $a + $b;
-       showMessageOrError();
        printOpFormIntro ('rollTags', array ('realsum' => $sum));
        echo "<table border=1 align=center>";
        echo "<tr><td colspan=2>This special tool allows assigning tags to physical contents (racks <strong>and all contained objects</strong>) of the current ";
        echo "rack row.<br>The tag(s) selected below will be ";
        echo "appended to already assigned tag(s) of each particular entity. </td></tr>";
        echo "<tr><th>Tags</th><td>";
-       renderTagSelect();
+       renderNewEntityTags();
        echo "</td></tr>";
        echo "<tr><th>Control question: the sum of ${a} and ${b}</th><td><input type=text name=sum></td></tr>";
        echo "<tr><td colspan=2 align=center><input type=submit value='Go!'></td></tr>";
@@ -5174,21 +4988,16 @@ function renderTagRollerForRow ($row_id)
 
 function renderObjectSLB ($object_id)
 {
-       global $pageno, $tabno, $nextorder;
-       showMessageOrError();
-       $vs_list = $rsplist = array();
-       foreach (getVSList() as $vsid => $vsinfo)
-               $vs_list[$vsid] = buildVServiceName ($vsinfo) . (empty ($vsinfo['name']) ? '' : " (${vsinfo['name']})");
-       foreach (getRSPoolList() as $pool_id => $poolInfo)
-               $rsplist[$pool_id] = $poolInfo['name'];
+       global $nextorder;
+       // Keep the list in a variable to assist in decoding pool name below.
 
        startPortlet ('Add new');
        echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
        printOpFormIntro ('addLB');
        echo "<tr valign=top><th>VS / RS pool</th><td class=tdleft>";
-       printSelect ($vs_list, 'vs_id', NULL, 1);
+       printSelect (getIPv4VSOptions(), 'vs_id', NULL, 1);
        echo "</td><td>";
-       printSelect ($rsplist, 'pool_id', NULL, 2);
+       printSelect (getIPv4RSPoolOptions(), 'pool_id', NULL, 2);
        echo "</td><td>";
        printImageHREF ('add', 'Configure LB', TRUE, 5);
        echo "</td></tr>\n";
@@ -5211,9 +5020,9 @@ function renderObjectSLB ($object_id)
                        printImageHREF ('delete', 'Unconfigure');
                        echo "</a></td>";
                        echo "</td><td class=tdleft>";
-                       renderVSCell ($vs_id);
+                       renderCell (spotEntity ('ipv4vs', $vs_id));
                        echo "</td><td class=tdleft>";
-                       renderRSPoolCell ($vsinfo['pool_id'], $rsplist[$vsinfo['pool_id']]);
+                       renderCell (spotEntity ('ipv4rspool', $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);
@@ -5227,8 +5036,7 @@ function renderObjectSLB ($object_id)
 
 function renderEditRSPool ($pool_id)
 {
-       showMessageOrError();
-       $poolinfo = getRSPoolInfo ($pool_id);
+       $poolinfo = spotEntity ('ipv4rspool', $pool_id);
        printOpFormIntro ('updIPv4RSP');
        echo '<table border=0 align=center>';
        echo "<tr><th class=tdright>name:</th><td class=tdleft><input type=text name=name value='${poolinfo['name']}'></td></tr>\n";
@@ -5242,8 +5050,8 @@ function renderEditRSPool ($pool_id)
 
 function renderEditVService ($vsid)
 {
-       showMessageOrError();
-       $vsinfo = getVServiceInfo ($vsid);
+       $vsinfo = spotEntity ('ipv4vs', $vsid);
+       amplifyCell ($vsinfo);
        printOpFormIntro ('updIPv4VS');
        echo '<table border=0 align=center>';
        echo "<tr><th class=tdright>VIP:</th><td class=tdleft><input tabindex=1 type=text name=vip value='${vsinfo['vip']}'></td></tr>\n";
@@ -5283,13 +5091,59 @@ function renderRackCodeViewer ()
 function renderRackCodeEditor ()
 {
        $text = loadScript ('RackCode');
-       showMessageOrError();
        printOpFormIntro ('saveRackCode');
+       echo <<<ENDJAVASCRIPT
+<script type="text/javascript">
+var prevCode = '';
+function verify()
+{
+       $.ajax({
+               type: "POST",
+               url: "ajax.php",
+               data: "ac=verifyCode&code="+RCTA.getCode(),
+               success: function (data)
+               {
+                       arr = data.split("\\n");
+                       if (arr[0] == "ACK")
+                       {
+                               $("#SaveChanges")[0].disabled = "";
+                               $("#ShowMessage")[0].innerHTML = "Code verification OK, don't forget to save the code";
+                               $("#ShowMessage")[0].className = "msg_success";
+                       }
+                       else
+                       {
+                               $("#SaveChanges")[0].disabled = "disabled";
+                               $("#ShowMessage")[0].innerHTML = arr[1];
+                               $("#ShowMessage")[0].className = "msg_warning";
+                       }
+                       prevCode = RCTA.getCode();
+               }
+       });
+}
+
+
+function invalidate()
+{
+       if (prevCode != RCTA.getCode())
+       {
+               prevCode = RCTA.getCode();
+               $("#SaveChanges")[0].disabled = "disabled";
+               $("#ShowMessage")[0].innerHTML = "";
+               $("#ShowMessage")[0].className = "";
+       }
+}
+
+setInterval(invalidate, 1000);
+</script>
+ENDJAVASCRIPT;
+
        echo '<table border=0 align=center>';
        echo "<tr><td><textarea rows=40 cols=100 name=rackcode id=RCTA class='codepress rackcode'>";
        echo $text . "</textarea></td></tr>\n";
        echo "<tr><td align=center>";
-       echo "<input type='submit' value='Save' onclick='RCTA.toggleEditor();'>";
+       echo '<div id="ShowMessage"></div>';
+       echo "<input type='button' value='Verify' onclick='verify();'>";
+       echo "<input type='submit' value='Save' disabled='disabled' id='SaveChanges' onclick='RCTA.toggleEditor();'>";
 //     printImageHREF ('SAVE', 'Save changes', TRUE);
        echo "</td></tr>";
        echo '</table>';
@@ -5298,32 +5152,13 @@ function renderRackCodeEditor ()
 
 function renderUser ($user_id)
 {
-       global $accounts, $expl_tags, $impl_tags;
-       $username = getUsernameByID ($user_id);
+       $userinfo = spotEntity ('user', $user_id);
 
        startPortlet ('summary');
        echo '<table border=0 align=center>';
-       echo "<tr><th class=tdright>Account name:</th><td class=tdleft>${username}</td></tr>";
-       echo '<tr><th class=tdright>Real name:</th><td class=tdleft>' . $accounts[$username]['user_realname'] . '</td></tr>';
-       // Using printTagTRs() is inappropriate here, because autotags will be filled with current user's
-       // data, not the viewed one.
-       $baseurl = makeHref(array('page'=>'userlist', 'tab'=>'default'))."&";
-       if (getConfigVar ('SHOW_EXPLICIT_TAGS') == 'yes' and count ($expl_tags))
-       {
-               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=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=tdright><span class=tagheader>Automatic tags</span>:</th><td class=tdleft>";
-               echo serializeTags ($target_auto_tags) . "</td></tr>\n";
-       }
+       echo "<tr><th class=tdright>Account name:</th><td class=tdleft>${userinfo['user_name']}</td></tr>";
+       echo '<tr><th class=tdright>Real name:</th><td class=tdleft>' . $userinfo['user_realname'] . '</td></tr>';
+       printTagTRs ($userinfo, makeHref(array('page'=>'userlist', 'tab'=>'default'))."&");
        echo '</table>';
        finishPortlet();
 
@@ -5332,7 +5167,6 @@ function renderUser ($user_id)
 
 function renderMyPasswordEditor ()
 {
-       showMessageOrError();
        printOpFormIntro ('changeMyPassword');
        echo '<table border=0 align=center>';
        echo "<tr><th class=tdright>Current password (*):</th><td><input type=password name=oldpassword tabindex=1></td></tr>";
@@ -5350,20 +5184,28 @@ function renderAccessDenied ()
        echo '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';
        echo "<link rel=stylesheet type='text/css' href=pi.css />\n";
        echo "<link rel=icon href='" . getFaviconURL() . "' type='image/x-icon' />";
-       echo "<link rel=icon href='" . getFaviconURL() . "' type='image/x-icon' />";
        echo "</head><body>";
-       global $root, $auto_tags, $expl_tags, $impl_tags, $pageno, $tabno;
+       global $root, $pageno, $tabno,
+               $user_given_tags,
+               $target_given_tags,
+               $auto_tags,
+               $expl_tags,
+               $impl_tags;
        echo "<table border=1 cellspacing=0 cellpadding=3 width='50%' align=center>\n";
        echo '<tr><th colspan=2><h3>';
        printImageHREF ('DENIED');
        echo ' access denied ';
        printImageHREF ('DENIED');
        echo '</h3></th></tr>';
-       echo "<tr><th width='50%' class=tdright><span class=tagheader>Explicit tags</span>:</th><td class=tdleft>";
+       echo "<tr><th width='50%' class=tagchain>User given tags:</th><td class=tagchain>";
+       echo serializeTags ($user_given_tags) . "&nbsp;</td></tr>\n";
+       echo "<tr><th width='50%' class=tagchain>Target given tags:</th><td class=tagchain>";
+       echo serializeTags ($target_given_tags) . "&nbsp;</td></tr>\n";
+       echo "<tr><th width='50%' class=tagchain>Effective explicit tags:</th><td class=tagchain>";
        echo serializeTags ($expl_tags) . "&nbsp;</td></tr>\n";
-       echo "<tr><th width='50%' class=tdright><span class=tagheader>Implicit tags</span>:</th><td class=tdleft>";
+       echo "<tr><th width='50%' class=tagchain>Effective implicit tags:</th><td class=tagchain>";
        echo serializeTags ($impl_tags) . "&nbsp;</td></tr>\n";
-       echo "<tr><th width='50%' class=tdright><span class=tagheader>Automatic tags</span>:</th><td class=tdleft>";
+       echo "<tr><th width='50%' class=tagchain>Automatic tags:</th><td class=tagchain>";
        echo serializeTags ($auto_tags) . "&nbsp;</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";
@@ -5374,64 +5216,34 @@ function renderAccessDenied ()
 
 function renderMyAccount ()
 {
-       global $remote_username, $accounts;
+       global $remote_username, $remote_displayname;
        echo "<table border=0 class=objectview cellspacing=0 cellpadding=0 width='50%'>";
        echo "<tr><td colspan=2 align=center><h1>${remote_username}</h1></td></tr>\n";
-       echo "<tr><td colspan=2 align=center><h2>" . $accounts[$remote_username]['user_realname'] . "</h2></td></tr>\n";
+       echo "<tr><td colspan=2 align=center><h2>${remote_displayname}</h2></td></tr>\n";
        echo "</table>";
 }
 
 // File-related functions
-function renderFileSpace ()
-{
-       global $taglist, $tagtree;
-       echo "<table border=0 class=objectview>\n";
-       echo "<tr><td class=pcleft width='50%'>";
-       startPortlet ('View all by link');
-       $linkInfo = getFileLinkInfo();
-       if ($linkInfo === NULL)
-       {
-               showError ('getFileLinkInfo() failed', __FUNCTION__);
-               return;
-       }
-       if (count ($linkInfo) == 0)
-               echo "No files exist in DB";
-       else
-       {
-               echo '<div align=left><ul>';
-               foreach ($linkInfo as $li)
-                       echo "<li><a href='".makeHref(array('page'=>'filesbylink', 'entity_type'=>$li['entity_type']))."'>${li['name']}</a> (${li['count']})</li>";
-               echo '</ul></div>';
-       }
-       finishPortlet();
-       echo '</td><td class=pcright>';
-       renderTagFilterPortlet (getTagFilter(), 'file');
-       echo "</td></tr></table>\n";
-}
-
-function renderFile ($file_id = 0)
+function renderFile ($file_id)
 {
        global $nextorder, $aac, $root;
-       if ($file_id <= 0)
-       {
-               showError ('Invalid file_id', __FUNCTION__);
-               return;
-       }
-       $file = getFileInfo ($file_id);
+       $file = spotEntity ('file', $file_id);
        if ($file == NULL)
        {
-               showError ('getFileInfo() failed', __FUNCTION__);
+               showError ('Error loading data', __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 colspan=2 align=center><h1>" . htmlspecialchars ($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']);
+       printf("<td class=tdleft>%s</td></tr>", htmlspecialchars ($file['type']));
        echo "<tr><th width='50%' class=tdright>Size:</th>";
-       printf("<td class=tdleft>%s</td></tr>", formatFileSize($file['size']));
+       echo "<td class=tdleft><a href='${root}download.php?file_id=${file_id}'>";
+       printImageHREF ('download', 'Download file');
+       printf("</a>&nbsp;%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>";
@@ -5439,22 +5251,11 @@ function renderFile ($file_id = 0)
        echo "<tr><th width='50%' class=tdright>Accessed:</th>";
        printf("<td class=tdleft>%s</td></tr>", formatTimestamp($file['atime']));
 
-       echo "<tr><th width='50%' class=tdright>Download file:</th>";
-       echo "<td class=tdleft><a href='${root}download.php?file_id=${file_id}'>";
-       printImageHREF ('download', 'Download file');
-       echo "</a></td></tr>\n";
-
-       echo "<tr><th width='50%' class=tdright>Upload replacement:</th>";
-       printOpFormIntro ('replaceFile', array ('MAX_FILE_SIZE' => convertToBytes(get_cfg_var('upload_max_filesize'))), TRUE);
-       echo "<td class=tdleft><input type='file' size='10' name='file' tabindex=100>&nbsp;\n";
-       printImageHREF ('save', 'Save changes', TRUE, 101);
-       echo "</form></td></tr>\n";
-
        printTagTRs (makeHref(array('page'=>'files', 'tab'=>'default'))."&");
        if (!empty ($file['comment']))
        {
                echo '<tr><th class=slbconf>Comment:</th><td>&nbsp;</td></tr>';
-               echo '<tr><td colspan=2 class="dashed slbconf">' . string_insert_hrefs ($file['comment']) . '</td></tr>';
+               echo '<tr><td colspan=2 class="dashed slbconf">' . string_insert_hrefs (htmlspecialchars ($file['comment'])) . '</td></tr>';
        }
        echo "</table><br>\n";
        finishPortlet();
@@ -5462,15 +5263,27 @@ function renderFile ($file_id = 0)
        $links = getFileLinks ($file_id);
        if (count ($links))
        {
-               startPortlet ('Links');
-               ///usort($ports, 'sortByName');
+               startPortlet ('Links (' . count ($links) . ')');
                echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
-               echo "<tr><th>Linked to</th><th>Name</th></tr>\n";
                foreach ($links as $link)
                {
-                       echo "<tr><td>${link['entity_type']}</td>";
-                       echo "<td><a href='".makeHref(array('page'=>$link['page'], $link['id_name']=>$link['entity_id']))."'>${link['name']}</a></td>";
-                       echo "</tr>\n";
+                       echo '<tr><td class=tdleft>';
+                       switch ($link['entity_type'])
+                       {
+                               case 'user':
+                               case 'ipv4net':
+                               case 'rack':
+                               case 'ipv4vs':
+                               case 'ipv4rspool':
+                                       renderCell (spotEntity ($link['entity_type'], $link['entity_id']));
+                                       break;
+                               default:
+                                       echo formatEntityName ($link['entity_type']) . ': ';
+                                       echo "<a href='" . makeHref(array('page'=>$link['page'], $link['id_name']=>$link['entity_id']));
+                                       echo "'>${link['name']}</a>";
+                                       break;
+                       }
+                       echo '</td></tr>';
                }
                echo "</table><br>\n";
                finishPortlet();
@@ -5488,37 +5301,51 @@ function renderFile ($file_id = 0)
        echo "</table>\n";
 }
 
-function renderFileProperties ($file_id = 0)
+function renderFileReuploader ()
 {
-       global $root;
-       $file = getFileInfo ($file_id);
+       startPortlet ('Replace existing contents');
+       printOpFormIntro ('replaceFile', array (), TRUE);
+       echo "<input type=file size=10 name=file tabindex=100>&nbsp;\n";
+       printImageHREF ('save', 'Save changes', TRUE, 101);
+       echo "</form>\n";
+       finishPortlet();
+}
+
+function renderFileProperties ($file_id)
+{
+       $file = spotEntity ('file', $file_id);
        if ($file === NULL)
        {
-               showError ('getFileInfo() failed', __FUNCTION__);
+               showError ('Error loading data', __FUNCTION__);
                return;
        }
-       showMessageOrError();
        echo '<table border=0 align=center>';
        printOpFormIntro ('updateFile');
-       echo "<tr><th class=tdright>MIME-type:</th><td class=tdleft><input tabindex=101 type=text name=file_type value='${file['type']}'></td></tr>";
-       echo "<tr><th class=tdright>Filename:</th><td class=tdleft><input tabindex=102 type=text name=file_name value='${file['name']}'></td></tr>\n";
-       echo "<tr><th class=tdright>Comment:</th><td class=tdleft><textarea tabindex=103 name=file_comment rows=10 cols=80>${file['comment']}</textarea></td></tr>\n";
+       echo "<tr><th class=tdright>MIME-type:</th><td class=tdleft><input tabindex=101 type=text name=file_type value='";
+       echo htmlspecialchars ($file['type']) . "'></td></tr>";
+       echo "<tr><th class=tdright>Filename:</th><td class=tdleft><input tabindex=102 type=text name=file_name value='";
+       echo htmlspecialchars ($file['name']) . "'></td></tr>\n";
+       echo "<tr><th class=tdright>Comment:</th><td class=tdleft><textarea tabindex=103 name=file_comment rows=10 cols=80>\n";
+       echo htmlspecialchars ($file['comment']) . "</textarea></td></tr>\n";
        echo "<tr><th class=submit colspan=2>";
        printImageHREF ('SAVE', 'Save changes', TRUE, 102);
        echo '</th></tr></form></table>';
 }
 
-// Used for uploading a parentless file
-function renderFileUploadForm ()
+function renderFileBrowser ()
 {
-       global $aat, $pageno, $tabno, $nextorder;
+       renderCellList ('file', 'Files', TRUE);
+}
 
-       showMessageOrError();
+// Like renderFileBrowser(), but with the option to delete files
+function renderFileManager ()
+{
+       global $nextorder;
+       // Used for uploading a parentless file
        startPortlet ('Upload new');
        echo "<table border=0 cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
        echo "<tr><th>File</th><th>Comment</th><th></th></tr>\n";
-
-       printOpFormIntro ('addFile', array ('MAX_FILE_SIZE' => convertToBytes(get_cfg_var('upload_max_filesize'))), TRUE);
+       printOpFormIntro ('addFile', array (), TRUE);
        echo "<tr>";
        echo "<td class=tdleft><input type='file' size='10' name='file' tabindex=100></td>\n";
        echo "<td class=tdleft><textarea tabindex=101 name=comment rows=10 cols=80></textarea></td>\n";
@@ -5527,94 +5354,42 @@ function renderFileUploadForm ()
        echo '</td></tr></form>';
        echo "</table><br>\n";
        finishPortlet();
-}
-
-function renderFilesByLink ()
-{
-       global $pageno, $tabno, $nextorder, $taglist, $tagtree, $root;
-       assertStringArg ('entity_type', __FUNCTION__, TRUE);
-       $entity_type = $_REQUEST['entity_type'];
-       $tagfilter = getTagFilter();
-       $tagfilter_str = getTagFilterStr ($tagfilter);
-       echo "<table border=0 class=objectview>\n";
-       echo "<tr><td class=pcleft width='25%'>";
 
-       startPortlet ('change type');
-       $linkInfo = getFileLinkInfo();
-       if ($linkInfo === NULL)
-       {
-               showError ('getFileLinkInfo() failed', __FUNCTION__);
+       $files = listCells ('file');
+       if (!count ($files))
                return;
-       }
-       if (count ($linkInfo) == 0)
-               echo 'No files exist in DB';
-       else
-       {
-               echo '<div align=left><ul>';
-               foreach ($linkInfo as $li)
-               {
-                       echo "<li><a href='".makeHref(array('page'=>$pageno, 'entity_type'=>$li['entity_type']))."${tagfilter_str}'>";
-                       if ($li['entity_type'] == $entity_type)
-                               echo '<strong>';
-                       echo "${li['name']}</a>";
-                       if ($li['entity_type'] == $entity_type)
-                               echo '</strong>';
-                       echo " (${li['count']})";
-                       if ($li['entity_type'] == $entity_type)
-                               echo ' &larr;';
-                       echo "</li>";
-               }
-               echo '</ul></div>';
-       }
-       finishPortlet();
-
-       echo '</td><td class=pcleft>';
 
-       $files = getFileList ($entity_type, $tagfilter, getTFMode());
-       startPortlet ('Files (' . count ($files) . ')');
-       if ($files === NULL)
-       {
-               showError ('getFileList() failed', __FUNCTION__);
-               return;
-       }
-       echo '<br><br><table cellpadding=5 cellspacing=0 align=center class=cooltable>';
-       echo '<tr><th>Name</th><th>Comment</th><th>Size</th><th>Created</th><th>Linked to</th><th>Actions</th></tr>';
+       startPortlet ('Manage existing (' . count ($files) . ')');
        $order = 'odd';
+       echo '<table cellpadding=5 cellspacing=0 align=center class=cooltable>';
+       echo '<tr><th>File</th><th>Unlink</th><th>Destroy</th></tr>';
        foreach ($files as $file)
        {
-               printf("<tr class=row_%s valign=top>", $order);
-               echo "<td class='tdleft'><a href='".makeHref(array('page'=>'file', 'file_id'=>$file['id']))."'><strong>${file['name']}</strong></a>";
-               $tags = loadEntityTags ('file', $file['id']);
-               if (count ($tags))
-                       echo '<br><small>' . serializeTags ($tags, makeHref(array('page'=>$pageno, 'tab'=>'default', 'entity_type'=>$entity_type))."&") . '</small>';
-               printf("</td><td class='tdleft'>%s</td>", $file['comment']);
-               printf("<td class='tdleft'>%s</td>", formatFileSize($file['size']));
-               echo "<td class='tdleft'>";
-               $links = getFileLinks($file['id']);
-               if (count ($links))
-                       printf("<small>%s</small>", serializeFileLinks($links));
-               echo '</td>';
-               echo "<td><a href='${root}download.php?file_id=${file['id']}'>";
-               printImageHREF ('download', 'Download file');
-               echo '</a> ';
-               echo "<a href='".makeHrefProcess(array('op'=>'deleteFile', 'file_id'=>$file['id'], 'entity_type'=>$entity_type))."'>";
-               printImageHREF ('delete', 'Delete file');
-               echo '</a></td>';
-               echo "</tr>\n";
+               printf("<tr class=row_%s valign=top><td class=tdleft>", $order);
+               renderCell ($file);
+               // Don't load links data earlier to enable special processing.
+               amplifyCell ($file);
+               echo '</td><td class=tdleft>';
+               echo serializeFileLinks ($file['links'], TRUE);
+               echo '</td><td class=tdcenter>';
+               if (count ($file['links']))
+                       printImageHREF ('NODESTROY', 'References (' . count ($file['links']) . ')');
+               else
+               {
+                       echo "<a href='".makeHrefProcess(array('op'=>'deleteFile', 'file_id'=>$file['id'])).
+                               "' onclick=\"javascript:return confirm('Are you sure you want to delete the file?')\">";
+                       printImageHREF ('DESTROY', 'Delete file');
+                       echo "</a>";
+               }
+               echo "</td></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__);
@@ -5626,42 +5401,33 @@ function renderFilesPortlet ($entity_type = NULL, $entity_id = 0)
        {
                startPortlet ('files (' . count ($files) . ')');
                echo "<table cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
-               echo "<tr><th>Name</th><th>Size</th><th>Comment</th><th>Actions</th></tr>\n";
+               echo "<tr><th>File</th><th>Comment</th></tr>\n";
                foreach ($files as $file)
                {
-                       echo '<td class=tdleft>';
-                       renderFileCell ($file);
-                       printf("</td><td class=tdleft>%s</td>", formatFileSize($file['size']));
-                       echo "<td class=tdleft>${file['comment']}</td>";
-                       echo "<td><a href='${root}download.php?file_id=${file['id']}'>";
-                       printImageHREF ('download', 'Download file');
-                       echo '</a></td></tr>';
+                       echo "<tr valign=top><td class=tdleft>";
+                       // That's a bit of overkill and ought to be justified after
+                       // getFilesOfEntity() returns a standard cell list.
+                       renderCell (spotEntity ('file', $file['id']));
+                       echo "</td><td class=tdleft>${file['comment']}</td></tr>";
                        if ('' != ($pcode = getFilePreviewCode ($file)))
-                               echo "<tr><td colspan=4>${pcode}</td></tr>\n";
+                               echo "<tr><td colspan=2>${pcode}</td></tr>\n";
                }
                echo "</table><br>\n";
                finishPortlet();
        }
 }
 
-function renderFilesForEntity ($entity_id = 0)
+function renderFilesForEntity ($entity_id)
 {
-       global $root, $page, $pageno, $tabno, $etype_by_pageno;
-       if ($entity_id <= 0)
-       {
-               showError ('Invalid entity info', __FUNCTION__);
-               return;
-       }
-
-       showMessageOrError();
+       global $page, $pageno, $etype_by_pageno;
        // Now derive entity_type and bypass_name from pageno.
        $entity_type = $etype_by_pageno[$pageno];
        $id_name = $page[$pageno]['bypass'];
        
-       startPortlet ('Upload new');
+       startPortlet ('Upload and link new');
        echo "<table border=0 cellspacing=0 cellpadding='5' align='center' class='widetable'>\n";
        echo "<tr><th>File</th><th>Comment</th><th></th></tr>\n";
-       printOpFormIntro ('addFile', array ('entity_type' => $entity_type, 'entity_id' => $entity_id, 'MAX_FILE_SIZE' => convertToBytes(get_cfg_var('upload_max_filesize'))), TRUE);
+       printOpFormIntro ('addFile', array (), TRUE);
        echo "<tr>";
        echo "<td class=tdleft><input type='file' size='10' name='file' tabindex=100></td>\n";
        echo "<td class=tdleft><textarea tabindex=101 name=comment rows=10 cols=80></textarea></td><td>\n";
@@ -5673,13 +5439,13 @@ function renderFilesForEntity ($entity_id = 0)
        $files = getAllUnlinkedFiles ($entity_type, $entity_id);
        if (count ($files))
        {
-               startPortlet ('Use existing');
+               startPortlet ('Link existing (' . count ($files) . ')');
                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);
+               printImageHREF ('ATTACH', 'Link file', TRUE);
                echo '</td></tr></table>';
                echo "</form>\n";
                finishPortlet();
@@ -5688,26 +5454,17 @@ function renderFilesForEntity ($entity_id = 0)
        $filelist = getFilesOfEntity ($entity_type, $entity_id);
        if (count ($filelist))
        {
-               startPortlet ('Manage linked');
+               startPortlet ('Manage linked (' . count ($filelist) . ')');
                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";
+               echo "<tr><th>File</th><th>Comment</th><th>Unlink</th></tr>\n";
                foreach ($filelist as $file_id => $file)
                {
-                       echo "<tr valign=top><td><a href='".makeHrefProcess(array('op'=>'deleteFile', 'file_id'=>$file_id, $id_name=>$entity_id, 'name'=>$file['name']))."'>";
-                       printImageHREF ('DESTROY', 'Unlink and delete file');
-                       echo '</a></td><td class=tdleft>';
-                       //printf("<a href='%/?page=file&file_id=%s'><strong>%s</strong></a>", $root, $file_id, $file['name']);
-                       renderFileCell ($file);;
-                       echo "<td class=tdleft>${file['comment']}</td>";
-                       printf("<td class=tdleft>%s</td>", formatFileSize($file['size']));
-                       echo "<td><a href='${root}download.php?file_id=${file_id}'>";
-                       printImageHREF ('DOWNLOAD', 'Download file');
-                       echo '</a> ';
-                       echo "<a href='".makeHrefProcess(array('op'=>'unlinkFile', 'link_id'=>$file['link_id'], $id_name=>$entity_id, 'name'=>$file['name']))."'>";
-                       printImageHREF ('CLEAR', 'Unlink file');
-                       echo '</a> ';
-                       printImageHREF ('SAVE', 'Save changes', TRUE);
-                       echo "</td></form></tr>\n";
+                       echo "<tr valign=top><td class=tdleft>";
+                       renderCell (spotEntity ('file', $file_id));
+                       echo "</td><td class=tdleft>${file['comment']}</td><td class=tdcenter>";
+                       echo "<a href='".makeHrefProcess(array('op'=>'unlinkFile', 'link_id'=>$file['link_id'], $id_name=>$entity_id))."'>";
+                       printImageHREF ('CUT', 'Unlink file');
+                       echo "</a></td></tr>\n";
                }
                echo "</table><br>\n";
                finishPortlet();
@@ -5724,11 +5481,11 @@ function printOpFormIntro ($opname, $extra = array(), $upload = FALSE)
        echo "<form method=post name=${opname} action='${root}process.php?page=${pageno}&tab=${tabno}&op=${opname}'";
        if ($upload)
                echo " enctype='multipart/form-data'";
-       echo ">\n";
+       echo ">";
        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";
+               echo "<input type=hidden name=${inputname} value='${inputvalue}'>";
 }
 
 // This is a dual-purpose formating function:
@@ -5748,7 +5505,6 @@ function niftyString ($string, $maxlen = 30)
 // Iterate over what findRouters() returned and output some text suitable for a TD element.
 function printRoutersTD ($rlist)
 {
-       global $root;
        $rtrclass = 'tdleft';
        foreach ($rlist as $rtr)
        {
@@ -5769,7 +5525,7 @@ function printRoutersTD ($rlist)
 function printIPv4NetInfoTDs ($netinfo, $tdclass = 'tdleft', $indent = 0, $symbol = 'spacer', $symbolurl = '')
 {
        global $root;
-       $tags = isset ($netinfo['id']) ? loadIPv4PrefixTags ($netinfo['id']) : array();
+       $tags = isset ($netinfo['id']) ? loadEntityTags ('ipv4net', $netinfo['id']) : array();
        if ($symbol == 'spacer')
        {
                $indent++;
@@ -5801,46 +5557,119 @@ function printIPv4NetInfoTDs ($netinfo, $tdclass = 'tdleft', $indent = 0, $symbo
        echo "</td>";
 }
 
-function renderLBCell ($object_id)
+function renderCell ($cell)
 {
        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>";
+       switch ($cell['realm'])
+       {
+       case 'user':
+               echo "<table class='slbcell vscell'><tr><td rowspan=3 width='5%'>";
+               printImageHREF ('USER');
+               echo '</td>';
+               echo "<td><a href='${root}?page=user&user_id=${cell['user_id']}'>${cell['user_name']}</a></td></tr>";
+               if (strlen ($cell['user_realname']))
+                       echo "<tr><td><strong>" . niftyString ($cell['user_realname']) . "</strong></td></tr>";
+               else
+                       echo "<tr><td class=sparenetwork>no name</td></tr>";
+               echo '<td>';
+               if (!isset ($cell['etags']))
+                       $cell['etags'] = loadEntityTags ('user', $cell['user_id']);
+               echo count ($cell['etags']) ? ("<small>" . serializeTags ($cell['etags']) . "</small>") : '&nbsp;';
+               echo "</td></tr></table>";
+               break;
+       case 'file':
+               echo "<table class='slbcell vscell'><tr><td rowspan=3 width='5%'>";
+               switch ($cell['type'])
+               {
+                       case 'text/plain':
+                               printImageHREF ('text file');
+                               break;
+                       case 'image/jpeg':
+                       case 'image/png':
+                       case 'image/gif':
+                               printImageHREF ('image file');
+                               break;
+                       default:
+                               printImageHREF ('empty file');
+                               break;
+               }
+               echo "</td><td>";
+               printf ("<a href='${root}?page=file&file_id=%s'><strong>%s</strong></a>", $cell['id'], niftyString ($cell['name']));
+               echo "</td><td rowspan=3 valign=top>";
+               if (isset ($cell['links']) and count ($cell['links']))
+                       printf ("<small>%s</small>", serializeFileLinks ($cell['links']));
+               echo "</td></tr><tr><td>";
+               echo count ($cell['etags']) ? ("<small>" . serializeTags ($cell['etags']) . "</small>") : '&nbsp;';
+               echo "</td></tr><tr><td><a href='${root}download.php?file_id=${cell['id']}'>";
+               printImageHREF ('download', 'Download file');
+               echo '</a>&nbsp;';
+               echo formatFileSize ($cell['size']);
+               echo "</td></tr></table>";
+               break;
+       case 'ipv4vs':
+               echo "<table class='slbcell vscell'><tr><td rowspan=3 width='5%'>";
+               printImageHREF ('VS');
+               echo "</td><td>";
+               echo "<a href='${root}?page=ipv4vs&vs_id=${cell['id']}'>";
+               echo $cell['dname'] . "</a></td></tr><tr><td>";
+               echo $cell['name'] . '</td></tr><tr><td>';
+               echo count ($cell['etags']) ? ("<small>" . serializeTags ($cell['etags']) . "</small>") : '&nbsp;';
+               echo "</td></tr></table>";
+               break;
+       case 'ipv4rspool':
+               echo "<table class=slbcell><tr><td>";
+               echo "<a href='${root}?page=ipv4rspool&pool_id=${cell['id']}'>";
+               echo empty ($cell['name']) ? "ANONYMOUS pool [${cell['id']}]" : niftyString ($cell['name']);
+               echo "</a></td></tr><tr><td>";
+               printImageHREF ('RS pool');
+               echo "</td></tr><tr><td>";
+               echo count ($cell['etags']) ? ("<small>" . serializeTags ($cell['etags']) . "</small>") : '&nbsp;';
+               echo "</td></tr></table>";
+               break;
+       case 'ipv4net':
+               echo "<table class='slbcell vscell'><tr><td rowspan=3 width='5%'>";
+               printImageHREF ('NET');
+               echo '</td>';
+               echo "<td><a href='${root}?page=ipv4net&id=${cell['id']}'>${cell['ip']}/${cell['mask']}</a></td></tr>";
+               if (strlen ($cell['name']))
+                       echo "<tr><td><strong>" . niftyString ($cell['name']) . "</strong></td></tr>";
+               else
+                       echo "<tr><td class=sparenetwork>no name</td></tr>";
+               echo '<td>';
+               echo count ($cell['etags']) ? ("<small>" . serializeTags ($cell['etags']) . "</small>") : '&nbsp;';
+               echo "</td></tr></table>";
+               break;
+       case 'rack':
+               echo "<table class='slbcell vscell'><tr><td rowspan=3 width='5%'>";
+               $thumbwidth = getRackImageWidth();
+               $thumbheight = getRackImageHeight ($cell['height']);
+               echo "<img border=0 width=${thumbwidth} height=${thumbheight} title='${cell['height']} units' ";
+               echo "src='render_image.php?img=minirack&rack_id=${cell['id']}'>";
+               echo "</td><td>";
+               printf ("<a href='${root}?page=rack&rack_id=%s'><strong>%s</strong></a>", $cell['id'], niftyString ($cell['name']));
+               echo "</td></tr><tr><td>";
+               echo niftyString ($cell['comment']);
+               echo "</td></tr><tr><td>";
+               echo count ($cell['etags']) ? ("<small>" . serializeTags ($cell['etags']) . "</small>") : '&nbsp;';
+               echo "</td></tr></table>";
+               break;
+       default:
+               showError ('odd data', __FUNCTION__);
+               break;
+       }
 }
 
-function renderRSPoolCell ($pool_id, $pool_name)
+function renderLBCell ($object_id)
 {
        global $root;
+       $oi = spotEntity ('object', $object_id);
        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 "<a href='${root}?page=object&object_id=${object_id}'>${oi['dname']}</a>";
+       echo "</td></tr><tr><td>";
+       printImageHREF ('LB');
+       echo "</td></tr><tr><td>";
+       if (count ($oi['etags']))
+               echo '<small>' . serializeTags ($oi['etags']) . '</small>';
        echo "</td></tr></table>";
 }
 
@@ -5855,23 +5684,15 @@ function renderRouterCell ($dottedquad, $ifname, $object_id, $object_dname)
        echo "</td></tr><tr><td>";
        printImageHREF ('router');
        echo "</td></tr><tr><td><small>";
-       echo serializeTags (loadRackObjectTags ($object_id));
+       echo serializeTags (loadEntityTags ('object', $object_id));
        echo "</small></td></tr></table>";
 }
 
-function renderFileCell ($fileinfo)
-{
-       global $root;
-       printf("<a href='${root}?page=file&file_id=%s'><strong>%s</strong></a>", $fileinfo['id'], niftyString ($fileinfo['name']));
-       echo '<br><small>';
-       echo serializeTags (loadFileTags ($fileinfo['id']));
-       echo "</small>";
-}
-
 // Return HTML code necessary to show a preview of the file give. Return an empty string,
 // if a preview cannot be shown
 function getFilePreviewCode ($file)
 {
+       global $root;
        $ret = '';
        switch ($file['type'])
        {
@@ -5904,7 +5725,7 @@ function getFilePreviewCode ($file)
                                $file = getFile ($file['id']);
                                $ret .= '<textarea readonly rows=' . getConfigVar ('PREVIEW_TEXT_ROWS');
                                $ret .= ' cols=' . getConfigVar ('PREVIEW_TEXT_COLS') . '>';
-                               $ret .= $file['contents'];
+                               $ret .= htmlspecialchars ($file['contents']);
                                $ret .= '</textarea>';
                        }
                        break;
@@ -5917,19 +5738,287 @@ function getFilePreviewCode ($file)
 function renderTextEditor ($file_id)
 {
        global $CodePressMap;
-       showMessageOrError();
        $fullInfo = getFile ($file_id);
-       printOpFormIntro ('updateFileText');
+       printOpFormIntro ('updateFileText', array ('mtime_copy' => $fullInfo['mtime']));
        preg_match('/.+\.([^.]*)$/', $fullInfo['name'], $matches); # get file extension
        if (isset ($matches[1]) && isset ($CodePressMap[$matches[1]]))
                $syntax = $CodePressMap[$matches[1]];
        else
                $syntax = "text";
        echo '<table border=0 align=center>';
-       echo "<tr><td><textarea rows=45 cols=180 id=file_text name=file_text tabindex=101 class='codepress " . $syntax . "'>";
-       echo $fullInfo['contents'] . '</textarea></td></tr>';
+       echo "<tr><td><textarea rows=45 cols=180 id=file_text name=file_text tabindex=101 class='codepress " . $syntax . "'>\n";
+       echo htmlspecialchars ($fullInfo['contents']) . '</textarea></td></tr>';
        echo "<tr><td class=submit><input type=submit value='Save' onclick='file_text.toggleEditor();'>";
        echo "</td></tr>\n</table></form>\n";
 }
 
+function showPathAndSearch ($pageno)
+{
+       // This function returns array of page numbers leading to the target page
+       // plus page number of target page itself. The first element is the target
+       // page number and the last element is the index page number.
+       function getPath ($targetno)
+       {
+               $self = __FUNCTION__;
+               global $page;
+               $path = array();
+               // Recursion breaks at first parentless page.
+               if (!isset ($page[$targetno]['parent']))
+                       $path = array ($targetno);
+               else
+               {
+                       $path = $self ($page[$targetno]['parent']);
+                       $path[] = $targetno;
+               }
+               return $path;
+       }
+       global $root, $page;
+       // Path.
+       echo "<td class=activemenuitem width='99%'>" . getConfigVar ('enterprise');
+       $path = getPath ($pageno);
+       foreach ($path as $no)
+       {
+               if (isset ($page[$no]['title']))
+                       $title = array
+                       (
+                               'name' => $page[$no]['title'],
+                               'params' => array()
+                       );
+               else
+                       $title = dynamic_title_decoder ($no);
+               echo ": <a href='${root}?page=${no}&tab=default";
+               foreach ($title['params'] as $param_name => $param_value)
+                       echo "&${param_name}=${param_value}";
+               echo "'>" . $title['name'] . "</a>";
+       }
+       echo "</td>";
+       // Search form.
+       echo "<td><table border=0 cellpadding=0 cellspacing=0><tr><td>Search:</td>";
+       echo "<form name=search method=get action='${root}'><td>";
+       echo '<input type=hidden name=page value=search>';
+       // This input will be the first, if we don't add ports or addresses.
+       echo "<input type=text name=q size=20 tabindex=1000></td></form></tr></table></td>";
+}
+
+function getTitle ($pageno)
+{
+       global $page;
+       if (isset ($page[$pageno]['title']))
+               return $page[$pageno]['title'];
+       $tmp = dynamic_title_decoder ($pageno);
+       return $tmp['name'];
+}
+
+function showTabs ($pageno, $tabno)
+{
+       global $tab, $root, $page, $trigger;
+       if (!isset ($tab[$pageno]['default']))
+               return;
+       echo "<td><div class=greynavbar><ul id=foldertab style='margin-bottom: 0px; padding-top: 10px;'>";
+       foreach ($tab[$pageno] as $tabidx => $tabtitle)
+       {
+               // Hide forbidden tabs.
+               if (!permitted ($pageno, $tabidx))
+                       continue;
+               // Dynamic tabs should only be shown in certain cases (trigger exists and returns true).
+               if (!isset ($trigger[$pageno][$tabidx]))
+                       $tabclass = 'std';
+               elseif (!strlen ($tabclass = $trigger[$pageno][$tabidx] ()))
+                       continue;
+               if ($tabidx == $tabno)
+                      $tabclass = 'current'; // override any class for an an active selection
+               echo "<li><a class=${tabclass}";
+               echo " href='${root}?page=${pageno}&tab=${tabidx}";
+               if (isset ($page[$pageno]['bypass']) and isset ($_REQUEST[$page[$pageno]['bypass']]))
+               {
+                       $bpname = $page[$pageno]['bypass'];
+                       $bpval = $_REQUEST[$bpname];
+                       echo "&${bpname}=${bpval}";
+               }
+               echo "'>${tabtitle}</a></li>\n";
+       }
+       echo "</ul></div></td>\n";
+}
+
+// Arg is path page number, which can be different from the primary page number,
+// for example title for 'ipv4net' can be requested to build navigation path for
+// both IPv4 network and IPv4 address. Another such page number is 'row', which
+// fires for both row and its racks. Use pageno for decision in such cases.
+function dynamic_title_decoder ($path_position)
+{
+       switch ($path_position)
+       {
+       case 'index':
+               return array
+               (
+                       'name' => '/' . getConfigVar ('enterprise'),
+                       'params' => array()
+               );
+       case 'chapter':
+               assertUIntArg ('chapter_no', __FUNCTION__);
+               $chapters = getChapterList();
+               $chapter_no = $_REQUEST['chapter_no'];
+               $chapter_name = isset ($chapters[$chapter_no]) ? $chapters[$chapter_no]['name'] : 'N/A';
+               return array
+               (
+                       'name' => "Chapter '${chapter_name}'",
+                       'params' => array ('chapter_no' => $chapter_no)
+               );
+       case 'user':
+               assertUIntArg ('user_id', __FUNCTION__);
+               $userinfo = spotEntity ('user', $_REQUEST['user_id']);
+               return array
+               (
+                       'name' => "Local user '" . $userinfo['user_name'] . "'",
+                       'params' => array ('user_id' => $_REQUEST['user_id'])
+               );
+       case 'ipv4rspool':
+               assertUIntArg ('pool_id', __FUNCTION__);
+               $poolInfo = spotEntity ('ipv4rspool', $_REQUEST['pool_id']);
+               return array
+               (
+                       'name' => empty ($poolInfo['name']) ? 'ANONYMOUS' : $poolInfo['name'],
+                       'params' => array ('pool_id' => $_REQUEST['pool_id'])
+               );
+       case 'ipv4vs':
+               assertUIntArg ('vs_id', __FUNCTION__);
+               $tmp = spotEntity ('ipv4vs', $_REQUEST['vs_id']);
+               return array
+               (
+                       'name' => $tmp['dname'],
+                       'params' => array ('vs_id' => $_REQUEST['vs_id'])
+               );
+       case 'object':
+               assertUIntArg ('object_id', __FUNCTION__);
+               $object = spotEntity ('object', $_REQUEST['object_id']);
+               if ($object == NULL)
+                       return array
+                       (
+                               'name' => __FUNCTION__ . '() failure',
+                               'params' => array()
+                       );
+               return array
+               (
+                       'name' => $object['dname'],
+                       'params' => array ('object_id' => $_REQUEST['object_id'])
+               );
+       case 'rack':
+               assertUIntArg ('rack_id', __FUNCTION__);
+               $rack = spotEntity ('rack', $_REQUEST['rack_id']);
+               return array
+               (
+                       'name' => $rack['name'],
+                       'params' => array ('rack_id' => $_REQUEST['rack_id'])
+               );
+       case 'search':
+               if (isset ($_REQUEST['q']))
+                       return array
+                       (
+                               'name' => "search results for '${_REQUEST['q']}'",
+                               'params' => array ('q' => $_REQUEST['q'])
+                       );
+               else
+                       return array
+                       (
+                               'name' => 'search results',
+                               'params' => array()
+                       );
+       case 'file':
+               assertUIntArg ('file_id', __FUNCTION__);
+               $file = spotEntity ('file', $_REQUEST['file_id']);
+               if ($file == NULL)
+                       return array
+                       (
+                               'name' => __FUNCTION__ . '() failure',
+                               'params' => array()
+                       );
+               return array
+               (
+                       'name' => htmlspecialchars ($file['name']),
+                       'params' => array ('file_id' => $_REQUEST['file_id'])
+               );
+       case 'ipaddress':
+               assertIPv4Arg ('ip', __FUNCTION__);
+               return array
+               (
+                       'name' => $_REQUEST['ip'],
+                       'params' => array ('ip' => $_REQUEST['ip'])
+               );
+       case 'ipv4net':
+               global $pageno;
+               switch ($pageno)
+               {
+               case 'ipv4net':
+                       assertUIntArg ('id', __FUNCTION__);
+                       $range = spotEntity ('ipv4net', $_REQUEST['id']);
+                       return array
+                       (
+                               'name' => $range['ip'] . '/' . $range['mask'],
+                               'params' => array ('id' => $_REQUEST['id'])
+                       );
+               case 'ipaddress':
+                       assertIPv4Arg ('ip', __FUNCTION__);
+                       $range = spotEntity ('ipv4net', getIPv4AddressNetworkId ($_REQUEST['ip']));
+                       return array
+                       (
+                               'name' => $range['ip'] . '/' . $range['mask'],
+                               'params' => array
+                               (
+                                       'id' => $range['id'],
+                                       'hl_ipv4_addr' => $_REQUEST['ip']
+                               )
+                       );
+               default:
+                       return array
+                       (
+                               'name' => __FUNCTION__ . '() failure',
+                               'params' => array()
+                       );
+               }
+       case 'row':
+               global $pageno;
+               switch ($pageno)
+               {
+               case 'rack':
+                       assertUIntArg ('rack_id', __FUNCTION__);
+                       $rack = spotEntity ('rack', $_REQUEST['rack_id']);
+                       if ($rack == NULL)
+                       {
+                               showError ('Rack not found', __FUNCTION__);
+                               return NULL;
+                       }
+                       return array
+                       (
+                               'name' => $rack['row_name'],
+                               'params' => array ('row_id' => $rack['row_id'])
+                       );
+               case 'row':
+                       assertUIntArg ('row_id', __FUNCTION__);
+                       $rowInfo = getRackRowInfo ($_REQUEST['row_id']);
+                       if ($rowInfo == NULL)
+                       {
+                               showError ('getRackRowInfo() failed', __FUNCTION__);
+                               return NULL;
+                       }
+                       return array
+                       (
+                               'name' => $rowInfo['name'],
+                               'params' => array ('row_id' => $_REQUEST['row_id'])
+                       );
+               default:
+                       return array
+                       (
+                               'name' => __FUNCTION__ . '() failure',
+                               'params' => array()
+                       );
+               }
+       default:
+               return array
+               (
+                       'name' => __FUNCTION__ . '() failure',
+                       'params' => array()
+               );
+       }
+}
+
 ?>