add colored tags
authorMaik Ehinger <m.ehinger@ltur.de>
Tue, 17 May 2016 15:22:54 +0000 (17:22 +0200)
committerDenis Ovsienko <denis@ovsienko.info>
Tue, 18 Jul 2017 12:36:50 +0000 (13:36 +0100)
This add colors to object depot, rack diagram, rack thumbnails and IPSpace.
The color depends on the tags assigned to an entity.
Each tag could be assigned one color. Multiple colors are displayed as a sharp gradient.

* listCells(), spotEntity(), getTagList(), commitUpdateTag(),
  renderTagRowForViewer(), printObjectDetailsForRenderRack(), renderRack(),
  renderDepot(), renderIPNetwork(), buildTagCheckboxRows(), updateTag(),
  printRackThumbImage(), renderTagTreeEditor(), enableTagsPicker(),
  getTagClassName(): add color
* objstyle_cache, color: new global variable
* isHTMLColor(), getColorSelect(), assertHTMLColorArg(),
  colorHex2Rgb(), getObjectClass(), setEntityColors(), destroyTag(),
  coloredObject(): new function
* genericAssertion(): add types "htmlcolor" and "htmlcolor0"
* rebuildTagChainForEntity(): clear RackThumbs on color change
* JS generateTagList(): add color class

wwwroot/inc/database.php
wwwroot/inc/functions.php
wwwroot/inc/install.php
wwwroot/inc/interface-config.php
wwwroot/inc/interface-lib.php
wwwroot/inc/interface.php
wwwroot/inc/navigation.php
wwwroot/inc/ophandlers.php
wwwroot/inc/solutions.php
wwwroot/inc/upgrade.php
wwwroot/js/tag-it-local.js

index 2be5ebe..ea410af 100644 (file)
@@ -495,6 +495,7 @@ function listCells ($realm, $parent_id = 0)
                                'id' => $tag_id,
                                'tag' => $taglist[$tag_id]['tag'],
                                'parent_id' => $taglist[$tag_id]['parent_id'],
+                               'color' =>  $taglist[$tag_id]['color'],
                                'user' => $row['tag_user'],
                                'time' => $row['tag_time'],
                        );
@@ -607,6 +608,7 @@ function spotEntity ($realm, $id, $ignore_cache = FALSE)
                                        'id' => $row['tag_id'],
                                        'tag' => $taglist[$row['tag_id']]['tag'],
                                        'parent_id' => $taglist[$row['tag_id']]['parent_id'],
+                                       'color' =>  $taglist[$row['tag_id']]['color'],
                                        'user' => $row['tag_user'],
                                        'time' => $row['tag_time'],
                                );
@@ -617,6 +619,7 @@ function spotEntity ($realm, $id, $ignore_cache = FALSE)
                                'id' => $row['tag_id'],
                                'tag' => $taglist[$row['tag_id']]['tag'],
                                'parent_id' => $taglist[$row['tag_id']]['parent_id'],
+                               'color' =>  $taglist[$row['tag_id']]['color'],
                                'user' => $row['tag_user'],
                                'time' => $row['tag_time'],
                        );
@@ -4300,7 +4303,7 @@ function generateEntityAutoTags ($cell)
 // results different from MySQL.
 function getTagList ($extra_sql = '')
 {
-       $result = usePreparedSelectBlade ("SELECT id, parent_id, is_assignable, tag FROM TagTree ORDER BY tag ${extra_sql}");
+       $result = usePreparedSelectBlade ("SELECT id, parent_id, is_assignable, tag, color FROM TagTree ORDER BY tag ${extra_sql}");
        return reindexById ($result->fetchAll (PDO::FETCH_ASSOC));
 }
 
@@ -4346,7 +4349,7 @@ function deleteTagForEntity ($entity_realm, $entity_id, $tag_id)
 
 // A tag's parent may not be one of its children, the tag itself or a tag
 // that does not belong to the forest of rooted trees because of a cycle.
-function commitUpdateTag ($tag_id, $tag_name, $parent_id, $is_assignable)
+function commitUpdateTag ($tag_id, $tag_name, $parent_id, $is_assignable, $color = NULL)
 {
        global $dbxlink;
        $dbxlink->beginTransaction();
@@ -4361,10 +4364,13 @@ function commitUpdateTag ($tag_id, $tag_name, $parent_id, $is_assignable)
                        (
                                'tag' => $tag_name,
                                'parent_id' => nullIfZero ($parent_id),
-                               'is_assignable' => $is_assignable
+                               'is_assignable' => $is_assignable,
+                               'color' => nullIfEmptyStr ($color)
                        ),
                        array ('id' => $tag_id)
                );
+               // remove all rack thumbnails
+               usePreparedDeleteBlade ('RackThumbnail', array ('1' => '1'));
                $dbxlink->commit();
        }
        catch (PDOException $pe)
@@ -4409,6 +4415,7 @@ function addTagForEntity ($realm, $entity_id, $tag_id)
 // Return TRUE, if any changes were committed.
 function rebuildTagChainForEntity ($realm, $entity_id, $extrachain = array(), $replace = FALSE)
 {
+       global $dbxlink;
        // Put the current explicit sub-chain into a buffer and merge all tags from
        // the extra chain that aren't there yet.
        $oldchain = array();
@@ -4433,6 +4440,22 @@ function rebuildTagChainForEntity ($realm, $entity_id, $extrachain = array(), $r
                addTagForEntity ($realm, $entity_id, $tag_id);
                $result = TRUE;
        }
+
+       // remove Rack thumnail if Rack or Object tag changes
+       if ($result && ( $realm == 'rack' || $realm == 'object'))
+       {
+               if($realm == 'rack')
+               {
+                       usePreparedDeleteBlade ('RackThumbnail', array ('rack_id' => $entity_id));
+               }
+               else
+               {
+                       // update all rack thumbs for object
+                       foreach (getResidentRacksData ($entity_id, FALSE) as $rack_id)
+                               usePreparedDeleteBlade ('RackThumbnail', array ('rack_id' => $rack_id));
+               }
+       }
+
        return $result;
 }
 
index e342dee..61acdb5 100644 (file)
@@ -444,6 +444,10 @@ function genericAssertion ($argname, $argtype)
                if (! $expr = compileExpression ($sic[$argname]))
                        throw new InvalidRequestArgException ($argname, $sic[$argname], 'not a valid RackCode expression');
                return $expr;
+       case 'htmlcolor':
+               return assertHTMLColorArg ($argname);
+       case 'htmlcolor0':
+               return assertHTMLColorArg ($argname, TRUE);
        default:
                throw new InvalidArgException ('argtype', $argtype); // comes not from user's input
        }
@@ -1786,6 +1790,7 @@ function getObjectiveTagTree ($tree, $realm, $preselect)
                                'tag' => $taginfo['tag'],
                                'parent_id' => $taginfo['parent_id'],
                                'refcnt' => $taginfo['refcnt'],
+                               'color' => $taginfo['color'],
                                'kids' => $subsearch
                        );
                else
@@ -6664,3 +6669,28 @@ function syncObjectPorts ($object_id, $desiredPorts)
        $dbxlink->exec ('UNLOCK TABLES');
        showSuccess (sprintf ('Added ports: %u, changed: %u, deleted: %u', count ($to_add), count ($to_update), count ($to_delete)));
 }
+
+function isHTMLColor ($color, $ok_if_null = TRUE)
+{
+
+       if( $ok_if_null && $color === NULL)
+               return TRUE;
+
+       // also matches empty string
+       if (! preg_match ('/^(#?[0-9a-f]{6})?$/i', $color))
+               return FALSE;
+
+       return TRUE;
+}
+
+function assertHTMLColorArg ($argname, $ok_if_empty = FALSE)
+{
+       global $sic;
+       if (! isset ($_REQUEST[$argname]))
+               throw new InvalidRequestArgException ($argname, '', 'parameter is missing');
+       if (! $ok_if_empty && $_REQUEST[$argname] == '')
+               throw new InvalidRequestArgException ($argname, $_REQUEST[$argname], 'parameter is an empty HTML color');
+       if (! isHTMLColor ($_REQUEST[$argname]))
+               throw new InvalidRequestArgException ($argname, $_REQUEST[$argname], 'parameter is not an HTML color');
+       return $sic[$argname];
+}
index 447153c..e2a17dc 100644 (file)
@@ -1084,6 +1084,7 @@ function get_pseudo_file ($name)
   `parent_id` int(10) unsigned default NULL,
   `is_assignable` enum('yes','no') NOT NULL DEFAULT 'yes',
   `tag` char(255) default NULL,
+  `color` char(6) default NULL,
   PRIMARY KEY  (`id`),
   UNIQUE KEY `tag` (`tag`),
   KEY `TagTree-K-parent_id` (`parent_id`),
index 934c6a3..f02c126 100644 (file)
@@ -882,6 +882,9 @@ function renderTagRowForViewer ($taginfo, $level = 0)
        if (!count ($taginfo['kids']))
                $level++; // Shift instead of placing a spacer. This won't impact any nested nodes.
        $refc = $taginfo['refcnt']['total'];
+
+       $trclass .= getObjectClass ('tag', $taginfo);
+
        echo "<tr class='${trclass}'><td align=left style='padding-left: " . ($level * 16) . "px;'>";
        if (count ($taginfo['kids']))
                printImageHREF ('node-expanded-static');
@@ -928,6 +931,14 @@ function renderTagRowForEditor ($taginfo, $parent_name = NULL, $level = 0)
        $sparams = array ('name' => 'parent_id', 'id' => 'nodeid_' . $taginfo['id'], 'class' => 'nodelist-popup');
        echo getSelect ($poptions, $sparams, $taginfo['parent_id'], FALSE);
 
+       if ($taginfo['is_assignable'] == 'yes')
+       {
+               $class = getObjectClass ('tag', $taginfo);
+               echo "</td><td class='${class}'>" . getColorSelect ('colorid_'.$taginfo['id'], $taginfo['color']) . '</td>';
+       }
+       else
+               echo '<td><input type="hidden" name="color" id="colorid_' . $taginfo['id'] . '" value=""></input></td>';
+
        echo '</td><td>' . getImageHREF ('save', 'Save changes', TRUE) . '</form></td></tr>';
        foreach ($taginfo['kids'] as $kid)
                $self ($kid, $taginfo['tag'], $level + 1);
@@ -950,6 +961,50 @@ END
        );
 }
 
+function getColorSelect($id = 'color', $selected = NULL)
+{
+
+               if ($selected)
+                       $class = ' class='.getObjectClass ('tag', array('id' => $selected, 'color' => $selected));
+               else
+                       $class = '';
+
+               $ret = "<select tabindex='1' name='color' id='${id}' onchange='this.className=this.options[this.selectedIndex].className;'${class}>";
+               $ret .= '<option value=""option>';
+
+               $colors = array(
+                               'FFFFFF',
+                               'C0C0C0',
+                               '808080',
+                               '000000',
+                               'FF0000',
+                               '800000',
+                               'FF8000',
+                               'FFFF00',
+                               '808000',
+                               '00FF00',
+                               '008000',
+                               '00FFFF',
+                               '008080',
+                               '0000FF',
+                               '000080',
+                               'FF00FF',
+                               '800080'
+               );
+
+               if ($selected != NULL && !in_array ($selected, $colors))
+                       $colors[] = $selected;
+
+               foreach ($colors as $color)
+               {
+                       $class = getObjectClass ('tag', array('id' => $color, 'color' => $color));
+                       $ret .= "<option class='${class}' value='$color'" . ($color == $selected ? " selected" : "" ) . ">#$color</option>";
+               }
+
+               $ret .= '</select>';
+               return $ret;
+}
+
 function renderTagTreeEditor ()
 {
        function printNewItemTR ($options)
@@ -960,6 +1015,7 @@ function renderTagTreeEditor ()
                echo '<td><input type=text size=48 name=tag_name></td>';
                echo '<td class=tdleft>' . getSelect (array ('yes' => 'yes', 'no' => 'no'), array ('name' => 'is_assignable'), 'yes') . '</td>';
                echo '<td>' . getSelect ($options, array ('name' => 'parent_id')) . '</td>';
+               echo '<td>' . getColorSelect () . '</td>';
                echo '<td>' . getImageHREF ('create', 'Create tag', TRUE, 120) . '</td>';
                echo '</tr></form>';
        }
@@ -967,7 +1023,7 @@ function renderTagTreeEditor ()
        addParentNodeOptionsJS ('tageditor', 'existing tag');
        $options = getParentNodeOptionsNew ($taglist, 'tag');
        echo '<br><table cellspacing=0 cellpadding=5 align=center class=widetable>';
-       echo '<tr><th>&nbsp;</th><th>tag name</th><th>assignable</th><th>parent tag</th><th>&nbsp;</th></tr>';
+       echo '<tr><th>&nbsp;</th><th>tag name</th><th>assignable</th><th>parent tag</th><th>color</th><th>&nbsp;</th></tr>';
        if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
                printNewItemTR ($options);
        foreach (getTagTree() as $taginfo)
index 7344ed5..1687a7f 100644 (file)
@@ -302,6 +302,8 @@ $page_by_realm['ipv4rspool'] = 'ipv4slb';
 $page_by_realm['file'] = 'files';
 $page_by_realm['user'] = 'userlist';
 
+$objstyles_cache = array();
+
 function getSelectOptions ($options, $selected_id = NULL)
 {
        $ret = '';
@@ -752,6 +754,8 @@ function getTagClassName ($tagid)
                $class .= 'tag-' . $parent . ' ';
        $class .= 'tag-' . $tagid . ' etag-' . $tagid;
 
+       $class .= getObjectClass('tag', $taglist[$tagid]);
+
        return $class;
 }
 
@@ -1154,7 +1158,11 @@ function enableTagsPicker ()
        {
                $taglist_filtered = array();
                foreach ($taglist as $key => $taginfo) # remove unused fields
+               {
                        $taglist_filtered[$key] = array_sub ($taginfo, array("tag", "is_assignable", "trace"));
+                       if ($taginfo['color'] != NULL)
+                               $taglist_filtered[$key]['tagclass'] = getObjectClass ('tag', $taginfo);
+               }
                addJS ('var taglist = ' . json_encode ($taglist_filtered) . ';', TRUE);
                $taglist_inserted = TRUE;
        }
@@ -1206,3 +1214,85 @@ function showMySQLWarnings()
        }
        $rtdebug_mysql_warnings = array();
 }
+
+function colorHex2Rgb($color)
+{
+       $color = trim ($color, '#');
+       $rgb = hexdec (substr ($color, 0, 2)) . ',' . hexdec (substr ($color, 2, 2)) . ',' . hexdec (substr ($color, 4, 2));
+       return $rgb;
+}
+
+function getObjectClass($realm, $object, $extrastyle = "")
+{
+       global $objstyles_cache;
+
+       $style = '';
+       switch ($realm)
+       {
+               case 'object':
+                       if (isset ($object['colors'][0]))
+                       {
+                               $step = 100 / count ($object['colors']);
+                               $percent = 0;
+                               $gradient = '';
+                               foreach ($object['colors'] as $color)
+                               {
+                                       $rgb = colorHex2Rgb ($color);
+                                       $gradient .= "rgba($rgb,0.2) $percent%, rgba($rgb,0.3) ".($percent + $step)."%,";
+                                       $percent += $step;
+                               }
+
+                               $style = "${extrastyle}background-image:linear-gradient(135deg," . trim($gradient, ',') . ") !important;";
+                               $class = 'objectcolor-' . $object['id'];
+                       }
+                       break;
+               case 'tag':
+                       if (isset ($object['color']))
+                       {
+                               $rgb = colorHex2Rgb ($object['color']);
+                               $style = "${extrastyle}background: white linear-gradient(rgba($rgb,0.3), rgba($rgb,0.3));";
+
+                               $class = 'tagcolor-' . $object['id'];
+                       }
+                       break;
+       }
+
+       if ($style != '')
+       {
+               $cachedclass = array_search ($style, $objstyles_cache);
+
+               if ($cachedclass === FALSE)
+               {
+                       addCSS (".{$class} {{$style}}", TRUE);
+                       $objstyles_cache[$class] = $style;
+                       return " $class";
+               }
+               else
+                       return " $cachedclass";
+
+       }
+
+       return '';
+}
+
+function setEntityColors(&$entity)
+{
+       $colors = false;
+       $entity['colors'] = array();
+       foreach ($entity['etags'] as $taginfo)
+       {
+               if ($taginfo['color'] !== NULL )
+               {
+                       $colors = true;
+                       $color = $taginfo['color'];
+                       if (!array_key_exists ($color, $entity['colors']))
+                       {
+                               $entity['colors'][] = $color;
+                               getObjectClass ('tag', $taginfo); // set tag css class
+                       }
+               }
+
+       }
+
+       return $colors;
+}
index 75e0858..5470da3 100644 (file)
@@ -875,6 +875,11 @@ function printObjectDetailsForRenderRack ($object_id, $hl_obj_id = 0)
                                        $slotClass[$slot] .= 'h';
                                if ($childData['has_problems'] == 'yes')
                                        $slotClass[$slot] .= 'w';
+
+                               $child = spotEntity ('object', $childData['id']);
+                               setEntityColors ($child);
+                               $slotClass[$slot] .= getObjectClass ('object', $child, 'background:white;');
+
                        }
                }
                natsort($childNames);
@@ -948,6 +953,7 @@ function renderRack ($rack_id, $hl_obj_id = 0)
        $rackData = spotEntity ('rack', $rack_id);
        amplifyCell ($rackData);
        markAllSpans ($rackData);
+       setEntityColors ($rackData);
        if ($hl_obj_id > 0)
                highlightObject ($rackData, $hl_obj_id);
        $neighbors = getRackNeighbors ($rackData['row_id'], $rack_id);
@@ -972,10 +978,21 @@ function renderRack ($rack_id, $hl_obj_id = 0)
                        if (isset ($rackData[$i][$locidx]['skipped']))
                                continue;
                        $state = $rackData[$i][$locidx]['state'];
-                       echo "<td class='atom state_${state}";
+
+                       $class = "atom state_${state}";
+
                        if (isset ($rackData[$i][$locidx]['hl']))
-                               echo $rackData[$i][$locidx]['hl'];
-                       echo "'";
+                               $class .= $rackData[$i][$locidx]['hl'];
+
+                       if($state == 'T')
+                       {
+                               $objectData = spotEntity ('object', $rackData[$i][$locidx]['object_id']);
+                               setEntityColors ($objectData);
+                               $class .= getObjectClass ('object', $objectData, (isset ($rackData[$i][$locidx]['hl']) && $rackData[$i][$locidx]['hl'] != "" ? "border:3px solid #80ffff !important;" : "")."background:white;");
+                       }
+
+                       echo "<td class='${class}'";
+
                        if (isset ($rackData[$i][$locidx]['colspan']))
                                echo ' colspan=' . $rackData[$i][$locidx]['colspan'];
                        if (isset ($rackData[$i][$locidx]['rowspan']))
@@ -1016,8 +1033,13 @@ function renderRack ($rack_id, $hl_obj_id = 0)
                        $state = ($zeroUObject['id'] == $hl_obj_id) ? 'Th' : 'T';
                        if ($zeroUObject['has_problems'] == 'yes')
                                $state .= 'w';
-                       echo "<tr><td class='atom state_${state}'>";
-                       printObjectDetailsForRenderRack($zeroUObject['id']);
+
+                       $class = "atom state_${state}";
+                       setEntityColors ($zeroUObject);
+                       $class .= getObjectClass ('object', $zeroUObject, 'background:white;');
+
+                       echo "<tr><td class='${class}'>";
+                       printObjectDetailsForRenderRack ($zeroUObject['id']);
                        echo "</td></tr>\n";
                }
                echo "</table>\n";
@@ -2338,7 +2360,11 @@ function renderDepot ()
                        foreach ($objects as $obj)
                        {
                                $problem = ($obj['has_problems'] == 'yes') ? 'has_problems' : '';
-                               echo "<tr class='row_${order} tdleft ${problem}' valign=top><td>" . mkA ("<strong>${obj['dname']}</strong>", 'object', $obj['id']);
+
+                               setEntityColors ($obj);
+                               $class = getObjectClass ('object', $obj, ($order == 'even' ? 'background:white;' : ''));
+
+                               echo "<tr class='row_${order} tdleft ${problem}${class}' valign=top><td>" . mkA ("<strong>${obj['dname']}</strong>", 'object', $obj['id']);
                                if (count ($obj['etags']))
                                        echo '<br><small>' . serializeTags ($obj['etags'], makeHref(array('page'=>$pageno, 'tab'=>'default')) . '&') . '</small>';
                                echo '</td>';
@@ -2506,11 +2532,22 @@ function renderIPSpaceRecords ($tree, $baseurl, $target = 0, $level = 1)
                        elseif ($item['symbol'] == 'node-expanded')
                                $decor['symbolurl'] = $baseurl . ($item['parent_id'] ? "&eid=${item['parent_id']}&hl_net=1" : '');
                        $tr_class = '';
+                       $extrastyle = '';
                        if ($target == $item['id'] && isset ($_REQUEST['hl_net']))
                        {
-                               $decor['tdclass'] = ' highlight';
-                               $tr_class = $decor['tdclass'];
+                               if (isset ($item['colors'][0]))
+                                       $extrastyle = 'outline: 3px solid #0aff0a;';
+                               else
+                               {
+                                       $decor['tdclass'] = ' highlight';
+                                       $tr_class = $decor['tdclass'];
+                               }
+
                        }
+
+                       setEntityColors ($item);
+                       $tr_class .= getObjectClass ('object', $item, $extrastyle);
+
                        echo "<tr valign=top class=\"$tr_class\">";
                        printIPNetInfoTDs ($item, $decor);
 
@@ -2737,6 +2774,7 @@ function renderIPNetwork ($id)
        $realm = $pageno; // 'ipv4net', 'ipv6net'
        $range = spotEntity ($realm, $id);
        loadIPAddrList ($range);
+       setEntityColors ($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>";
        echo htmlspecialchars ($range['name'], ENT_QUOTES, 'UTF-8') . "</h2></td></tr>\n";
@@ -4316,6 +4354,7 @@ function buildTagCheckboxRows ($inputname, $preselect, $neg_preselect, $taginfo,
                'input_class' => $level ? 'tag-cb' : 'tag-cb root',
                'input_value' => $taginfo['id'],
                'text_tagname' => $taginfo['tag'],
+               'color' => (isset($taginfo['color']) ? $taginfo['color'] : NULL),
        );
        $is_first_time = FALSE;
        $prepared_inputname = $inputname;
@@ -4335,6 +4374,7 @@ function buildTagCheckboxRows ($inputname, $preselect, $neg_preselect, $taginfo,
                $ret['input_extraattrs'] = 'disabled';
                $ret['tr_class'] .= (array_key_exists ('kidc', $taginfo) && $taginfo['kidc'] == 0) ? ' trwarning' : ' trnull';
        }
+
        if ($refcnt_realm != '' && isset ($taginfo['refcnt'][$refcnt_realm]))
                $ret['text_refcnt'] = $taginfo['refcnt'][$refcnt_realm];
        $ret = array ($ret);
index 5155c9f..5a20c7a 100644 (file)
@@ -659,7 +659,7 @@ $tabhandler['tagtree']['default'] = 'renderTagTree';
 $tabhandler['tagtree']['edit'] = 'renderTagTreeEditor';
 $tabhandler['tagtree']['resolve'] = 'renderGraphCycleResolver';
 $ophandler['tagtree']['edit']['createTag'] = 'tableHandler';
-$ophandler['tagtree']['edit']['destroyTag'] = 'tableHandler';
+$ophandler['tagtree']['edit']['destroyTag'] = 'destroyTag';
 $ophandler['tagtree']['edit']['updateTag'] = 'updateTag';
 $ophandler['tagtree']['resolve']['updateTag'] = 'updateTag';
 $trigger['tagtree']['resolve'] = 'triggerGraphCycleResolver';
index 44dee83..10cb96e 100644 (file)
@@ -393,6 +393,7 @@ $opspec_list['tagtree-edit-createTag'] = array
                array ('url_argname' => 'tag_name', 'table_colname' => 'tag', 'assertion' => 'tag'),
                array ('url_argname' => 'parent_id', 'assertion' => 'uint0', 'translator' => 'nullIfZero'),
                array ('url_argname' => 'is_assignable', 'assertion' => 'enum/yesno'),
+               array ('url_argname' => 'color', 'assertion' => 'string0', 'translator' => 'nullIfEmptyStr'),
        ),
 );
 $opspec_list['tagtree-edit-destroyTag'] = array
@@ -2224,7 +2225,8 @@ function updateTag ()
                        genericAssertion ('tag_id', 'uint'),
                        genericAssertion ('tag_name', 'tag'),
                        genericAssertion ('parent_id', 'uint0'),
-                       genericAssertion ('is_assignable', 'enum/yesno')
+                       genericAssertion ('is_assignable', 'enum/yesno'),
+                       genericAssertion ('color', 'htmlcolor0')
                );
        }
        catch (InvalidArgException $iae)
@@ -3850,3 +3852,17 @@ function updateVLANDomain()
        usePreparedUpdateBlade ('VLANDomain', array ('group_id' => $group_id, 'description' => $description), array ('id' => $domain_id));
        showSuccess ("VLAN domain updated successfully");
 }
+
+function destroyTag()
+{
+       global $taglist;
+       $tag_id = genericAssertion ('tag_id', 'uint');
+
+       if (isset ($taglist[$tag_id]) && isset ($taglist[$tag_id]['color']))
+       {
+               // remove all rack thumbnails
+               usePreparedDeleteBlade ('RackThumbnail', array ('1' => '1'));
+       }
+
+       tableHandler();
+}
index 541737f..d1cdbec 100644 (file)
@@ -17,6 +17,8 @@ require_once 'slb-interface.php';
 
 define ('RE_STATIC_URI', '#^(?:[[:alnum:]]+[[:alnum:]_.-]*/)+[[:alnum:]\._-]+\.([[:alpha:]]+)$#');
 
+$color = array();
+
 function castRackImageException ($e)
 {
        $m = array
@@ -166,13 +168,96 @@ function dispatchMiniRackThumbRequest ($rack_id)
        }
 }
 
+function coloredObject($entity, $img, $posx, $posy, $height, $width, $vertical = TRUE)
+{
+       global $color;
+
+       if ($entity == NULL)
+               return;
+
+       $colors = $entity['colors'];
+       $count = count ($colors);
+       if ($count == 0)
+       {
+               if (isset ($entity['state']))
+                       $colorcode = $entity['state'];
+               else
+                       $colorcode = 'T';
+
+               imagefilledrectangle
+               (
+                       $img,
+                       $posx,
+                       $posy,
+                       $posx + $width - 1,
+                       $posy + $height - 1,
+                       $color[$colorcode]
+               );
+               return;
+       }
+
+       $colorsize = (($vertical ? $width : $height) - 1) / $count;
+       $diagonal = 1;
+
+       $i = 0;
+       foreach ($colors as $colorcode)
+       {
+               if (!isset ($color[$colorcode]))
+                       $color[$colorcode] = colorFromHex ($img, $colorcode);
+
+               if ($vertical)
+                       $points = array
+                       (
+                               $posx + $i * $colorsize + ($i > 0 ? $diagonal : 0),
+                               $posy,
+
+                               $posx + $i * $colorsize - ($i > 0 ?  $diagonal : 0),
+                               $posy + $height - 1,
+
+                               $posx + ($i+1) * $colorsize - ($i+1 < $count ? $diagonal : 0),
+                               $posy + $height - 1,
+
+                               $posx + ($i+1) * $colorsize + ($i+1 < $count ? $diagonal : 0),
+                               $posy
+                        );
+               else
+                       $points = array
+                       (
+                               $posx,
+                               $posy + $i * $colorsize + ($i > 0 ? $diagonal : 0),
+
+                               $posx + $width - 1,
+                               $posy + $i * $colorsize - ($i > 0 ?  $diagonal : 0),
+
+                               $posx + $width - 1,
+                               $posy + ($i+1) * $colorsize - ($i+1 < $count ? $diagonal : 0),
+
+                               $posx,
+                               $posy + ($i+1) * $colorsize + ($i+1 < $count ? $diagonal : 0)
+                       );
+
+               imagefilledpolygon
+               (
+                       $img,
+                       $points,
+                       4,
+                       $color[$colorcode]
+               );
+
+               $i++;
+       }
+}
+
 # Generate a binary PNG image for a rack contents.
 function printRackThumbImage ($rack_id, $scale = 1, $object_id = NULL)
 {
+       global $color;
        $rackData = spotEntity ('rack', $rack_id);
        amplifyCell ($rackData);
        if ($object_id !== NULL)
                highlightObject ($rackData, $object_id);
+       markAllSpans ($rackData);
+       setEntityColors ($rackData);
        global $rtwidth;
        $offset[0] = 3;
        $offset[1] = 3 + $rtwidth[0];
@@ -195,25 +280,57 @@ function printRackThumbImage ($rack_id, $scale = 1, $object_id = NULL)
                'black' => colorFromHex ($img, '000000'),
                'gray' => colorFromHex ($img, 'c0c0c0'),
        );
-       $border_color = ($rackData['has_problems'] == 'yes') ? $color['Thw'] : $color['gray'];
+
+       // keep for compatibility
+       if ($rackData['has_problems'] == 'yes')
+                       $rackData['colors'][] = 'ff8080';
+
        imagerectangle ($img, 0, 0, $totalwidth - 1, $totalheight - 1, $color['black']);
-       imagerectangle ($img, 1, 1, $totalwidth - 2, $totalheight - 2, $border_color);
+       coloredObject ($rackData, $img, 1, 1, $totalheight - 2,  $totalwidth - 2, false);
        imagerectangle ($img, 2, 2, $totalwidth - 3, $totalheight - 3, $color['black']);
+       imagefilledrectangle ($img, 3, 3, $totalwidth - 4, $totalheight - 4, $color['F']);
        for ($unit_no = 1; $unit_no <= $rackData['height']; $unit_no++)
                for ($locidx = 0; $locidx < 3; $locidx++)
                {
-                       $colorcode = $rackData[$unit_no][$locidx]['state'];
-                       if (isset ($rackData[$unit_no][$locidx]['hl']))
-                               $colorcode = $colorcode . $rackData[$unit_no][$locidx]['hl'];
-                       imagerectangle
-                       (
-                               $img,
-                               $offset[$locidx],
-                               3 + ($rackData['height'] - $unit_no) * 2,
-                               $offset[$locidx] + $rtwidth[$locidx] - 1,
-                               3 + ($rackData['height'] - $unit_no) * 2 + 1,
-                               $color[$colorcode]
-                       );
+                       if (isset ($rackData[$unit_no][$locidx]['skipped']))
+                               continue;
+
+                       if (isset ($rackData[$unit_no][$locidx]['colspan']))
+                       {
+                               $locwidth = 0;
+                               for($i = 0; $i<$rackData[$unit_no][$locidx]['colspan']; $i++)
+                                       $locwidth += $rtwidth[$locidx + $i];
+                       }
+                       else
+                               $locwidth = $rtwidth[$locidx];
+
+                       $locheight = 2;
+                       if (isset ($rackData[$unit_no][$locidx]['rowspan']))
+                       {
+                               $locheight = $rackData[$unit_no][$locidx]['rowspan'] * 2;
+                       }
+
+                       $object_id = (isset ($rackData[$unit_no][$locidx]['object_id']) ? $rackData[$unit_no][$locidx]['object_id'] : NULL);
+                       if ($object_id)
+                       {
+                               $object = spotEntity ('object', $object_id);
+                               setEntityColors ($object);
+                               coloredObject ($object, $img, $offset[$locidx], 3 + ($rackData['height'] - $unit_no) * 2, $locheight,  $locwidth);
+                       }
+                       else
+                       {
+                               $state = $rackData[$unit_no][$locidx]['state'];
+                               if (isset ($rackData[$unit_no][$locidx]['hl']))
+                                       $state = $state . $rackData[$unit_no][$locidx]['hl'];
+
+                               $entity = array(
+                                       'id' => $state,
+                                       'colors' => array(),
+                                       'state' => $state
+                               );
+
+                               coloredObject ($entity, $img, $offset[$locidx], 3 + ($rackData['height'] - $unit_no) * 2, $locheight,  $locwidth);
+                       }
                }
        if ($scale > 1)
        {
index 7d17ad0..edba7ae 100644 (file)
@@ -1229,6 +1229,7 @@ ENDOFTRIGGER;
                        $query[] = "UPDATE Port SET label = NULL WHERE label = ''";
                        $query[] = "DELETE FROM RackThumbnail";
                        $query[] = "UPDATE Config SET varvalue = '0.21.0' WHERE varname = 'DB_VERSION'";
+                       $query[] = "ALTER TABLE TagTree ADD COLUMN color char(6) DEFAULT NULL";
                        break;
                case 'dictionary':
                        $query = reloadDictionary();
index 71f052e..ac641c5 100644 (file)
@@ -165,6 +165,9 @@ function generateTagList(input, ul, taglist, preselect, value_name, tag_limit, e
                        tag_trace = [];
                $.each(tag_trace, function (index, value) {result.push("tag-" + value);});
                result.push("tag-level" + tag_trace.length);
+               tagclass = taglist[tags_name_to_id[tag]].tagclass;
+               if(tagclass != 'undefined')
+                       result.push(tagclass);
                return result;
        }