generalize the graph cycles resolver
authorDenis Ovsienko <denis@ovsienko.info>
Thu, 21 Jan 2016 18:31:24 +0000 (18:31 +0000)
committerDenis Ovsienko <denis@ovsienko.info>
Thu, 21 Jan 2016 18:31:24 +0000 (18:31 +0000)
The tag forest editor tab used to have a separate form for resolving
graph cycle issues in the TagTree database table. This change reworks
the form into a standalone generic tab that makes it possible for the
user to fix similar data structures given these are declared in the
function.

* getOrphanedTags(): generalize and rename to getInvalidNodes()
* renderTagTreeEditor(): handle the forest structure only
* renderGraphCycleResolver(): new function with the tabhandler
* renderDataIntegrityReport(): amend
* triggerGraphCycleResolver(): new function

wwwroot/inc/functions.php
wwwroot/inc/interface-config.php
wwwroot/inc/interface-reports.php
wwwroot/inc/navigation.php
wwwroot/inc/triggers.php

index 9fea439..d16add8 100644 (file)
@@ -1437,13 +1437,12 @@ function treeFromList ($nodelist, $threshold = 0)
 
 // Return those tags that belong to the full list of tags but don't belong
 // to the forest of rooted trees as found by addTraceToNodes().
-function getOrphanedTags ()
+function getInvalidNodes ($nodelist)
 {
-       global $taglist;
        $ret = array();
-       foreach ($taglist as $tagid => $taginfo)
-               if (! array_key_exists ('trace', $taginfo))
-                       $ret[$tagid] = $taginfo;
+       foreach ($nodelist as $node_id => $node)
+               if (! array_key_exists ('trace', $node))
+                       $ret[$node_id] = $node;
        return $ret;
 }
 
index 2330020..7089a32 100644 (file)
@@ -940,31 +940,8 @@ END
                echo '</tr></form>';
        }
        global $taglist;
-
        $options = getParentNodeOptionsNew ($taglist, 'tag');
-       $otags = getOrphanedTags();
-       if (count ($otags))
-       {
-               startPortlet ('circular references');
-               echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
-               echo '<tr class=trerror><th>tag name</th><th>current parent tag</th><th>new parent tag</th><th>&nbsp;</th></tr>';
-               foreach ($otags as $taginfo)
-               {
-                       printOpFormIntro ('updateTag', array ('tag_id' => $taginfo['id'], 'tag_name' => $taginfo['tag']));
-                       echo "<input type=hidden name=is_assignable value=${taginfo['is_assignable']}>";
-                       echo '<tr>';
-                       echo '<td class=tdleft>' . stringForLabel ($taginfo['tag']) . '</td>';
-                       echo '<td class=tdleft>' . stringForLabel ($taglist[$taginfo['parent_id']]['tag']) . '</td>';
-                       echo '<td>' . getSelect ($options, array ('name' => 'parent_id'), $taginfo['parent_id']) . '</td>';
-                       echo '<td>' . getImageHREF ('save', 'Save changes', TRUE) . '</td>';
-                       echo '</tr></form>';
-               }
-               echo '</table>';
-               finishPortlet();
-       }
-
-       startPortlet ('tag tree');
-       echo "<table cellspacing=0 cellpadding=5 align=center class=widetable>\n";
+       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>';
        if (getConfigVar ('ADDNEW_AT_TOP') == 'yes')
                printNewItemTR ($options);
@@ -973,7 +950,47 @@ END
        if (getConfigVar ('ADDNEW_AT_TOP') != 'yes')
                printNewItemTR ($options);
        echo '</table>';
-       finishPortlet();
+}
+
+function renderGraphCycleResolver()
+{
+       global $pageno;
+       // $fieldmap below does not contain 'parent_id' as it is done by the SELECT.
+       switch ($pageno)
+       {
+               case 'tagtree';
+                       global $taglist;
+                       $nodelist = $taglist;
+                       $textfield = 'tag';
+                       $opcode = 'updateTag';
+                       $fieldmap = array
+                       (
+                               'tag_id' => 'id',
+                               'tag_name' => 'tag',
+                               'is_assignable' => 'is_assignable',
+                       );
+                       break;
+               default:
+                       throw new RackTablesError ('unexpected call to tabhandler function', RackTablesError::INTERNAL);
+       }
+       $invalids = getInvalidNodes ($nodelist);
+       $options = getParentNodeOptionsNew ($nodelist, $textfield);
+       echo '<br><table cellspacing=0 cellpadding=5 align=center class=widetable>';
+       echo '<tr><th>node</th><th>current parent node</th><th>new parent node</th><th>&nbsp;</th></tr>';
+       foreach ($invalids as $node)
+       {
+               $formvalues = array();
+               foreach ($fieldmap as $form_param => $nodefield)
+                       $formvalues[$form_param] = $node[$nodefield];
+               printOpFormIntro ($opcode, $formvalues);
+               echo '<tr>';
+               echo '<td class=tdleft>' . stringForLabel ($node[$textfield]) . '</td>';
+               echo '<td class="tdleft trerror">' . stringForLabel ($invalids[$node['parent_id']][$textfield]) . '</td>';
+               echo '<td>' . getSelect ($options, array ('name' => 'parent_id'), $node['parent_id']) . '</td>';
+               echo '<td>' . getImageHREF ('save', 'Save changes', TRUE) . '</td>';
+               echo '</tr></form>';
+       }
+       echo '</table>';
 }
 
 function renderMyAccount ()
index 6327d14..ef4f4db 100644 (file)
@@ -932,7 +932,7 @@ function renderDataIntegrityReport ()
 
        // check 10.3: tags
        global $taglist;
-       $invalids = getOrphanedTags();
+       $invalids = getInvalidNodes ($taglist);
        if (count ($invalids))
        {
                $violations = TRUE;
index 9ab4d07..c3eeaea 100644 (file)
@@ -649,11 +649,15 @@ $page['tagtree']['title'] = 'Tag tree';
 $page['tagtree']['parent'] = 'config';
 $tab['tagtree']['default'] = 'View';
 $tab['tagtree']['edit'] = 'Edit';
+$tab['tagtree']['resolve'] = 'Circular references';
 $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']['updateTag'] = 'updateTag';
+$ophandler['tagtree']['resolve']['updateTag'] = 'updateTag';
+$trigger['tagtree']['resolve'] = 'triggerGraphCycleResolver';
 $interface_requires['tagtree-*'] = 'interface-config.php';
 
 $page['myaccount']['title'] = 'My account';
index 68a9b78..66e366f 100644 (file)
@@ -342,4 +342,19 @@ function triggerPatchCableHeapsConfigured()
        return count (getPatchCableHeapSummary()) ? 'std' : '';
 }
 
+function triggerGraphCycleResolver()
+{
+       global $pageno;
+       switch ($pageno)
+       {
+               case 'tagtree':
+                       global $taglist;
+                       $nodelist = $taglist;
+                       break;
+               default:
+                       throw new RackTablesError ('unexpected call to trigger function', RackTablesError::INTERNAL);
+       }
+       return count (getInvalidNodes ($nodelist)) ? 'attn' : '';
+}
+
 ?>