From 32cee04b4358e7821daf81efaf0e92b3396b4163 Mon Sep 17 00:00:00 2001
From: Tomas Kopecek <tkopecek@redhat.com>
Date: Aug 03 2022 11:05:46 +0000
Subject: www: more generic taskinfo parameter handling


Related: https://pagure.io/koji/issue/3454

---

diff --git a/tests/test_www/test_taskinfo.py b/tests/test_www/test_taskinfo.py
index 4648bcb..daf270f 100644
--- a/tests/test_www/test_taskinfo.py
+++ b/tests/test_www/test_taskinfo.py
@@ -267,18 +267,27 @@ class TestTaskInfo(unittest.TestCase):
         server = self.__get_server(task)
         self.get_server.return_value = server
         webidx.taskinfo(self.environ, self.task_id)
-        server.getTag.assert_called_with('testTag')
+        server.getTag.assert_called_with('testTag', strict=True)
         server.getBuild.assert_called_with('testBuild')
 
         # case 5. newRepo, distRepo, createdstrepo
-        for m in ('newRepo', 'distRepo', 'createdistrepo'):
+        for m in ('newRepo', 'distRepo'):
             task = copy.deepcopy(self.task)
             task.update({'method': m})
-            task.update({'request': ['RepoTag']})
+            task.update({'request': ['RepoTag', 'repo_id', 'keys', 'task_opts']})
             server = self.__get_server(task)
             self.get_server.return_value = server
             webidx.taskinfo(self.environ, self.task_id)
-            server.getTag.assert_called_with('RepoTag')
+            server.getTag.assert_called_with('RepoTag', strict=True)
+
+        for m in ('createdistrepo',):
+            task = copy.deepcopy(self.task)
+            task.update({'method': m})
+            task.update({'request': ['RepoTag', 'repo_id', 'arch', 'keys', 'opts']})
+            server = self.__get_server(task)
+            self.get_server.return_value = server
+            webidx.taskinfo(self.environ, self.task_id)
+            server.getTag.assert_called_with('RepoTag', strict=True)
 
         # case 6. tagNotification
         task = copy.deepcopy(self.task)
@@ -287,18 +296,17 @@ class TestTaskInfo(unittest.TestCase):
         server = self.__get_server(task)
         self.get_server.return_value = server
         webidx.taskinfo(self.environ, self.task_id)
-        server.getTag.assert_has_calls([call('destTag'), call('srcTag')])
+        server.getTag.assert_has_calls([call('destTag', strict=True), call('srcTag', strict=True)])
         server.getBuild.assert_called_with('theBuild')
         server.getUser.assert_called_with('user')
 
         # case 7. dependentTask
         task = copy.deepcopy(self.task)
         task.update({'method': 'dependantTask'})
-        task.update({'request': [[]]})
+        task.update({'request': [[], []]})
         server = self.__get_server(task)
         self.get_server.return_value = server
         webidx.taskinfo(self.environ, self.task_id)
-        self.assertEqual(self.environ['koji.values']['deps'], [])
 
         # case 8. wrapperRPM
         task = copy.deepcopy(self.task)
@@ -311,13 +319,15 @@ class TestTaskInfo(unittest.TestCase):
             'wrapTask'
         ]
         self.get_server.return_value = server
+        server.getBuildTarget.return_value = 'tgt'
         webidx.taskinfo(self.environ, self.task_id)
-        self.assertEqual(self.environ['koji.values']['wrapTask'], 'wrapTask')
+        self.assertEqual(self.environ['koji.values']['params'], 
+                         {'build': '', 'build_target': 'tgt', 'spec_url': '', 'task': {'id': 999}})
 
         # case 7. restartVerify
         task = copy.deepcopy(self.task)
         task.update({'method': 'restartVerify'})
-        task.update({'request': [[]]})
+        task.update({'request': ['task_id', 'host']})
         server = self.__get_server(task)
         server.getTaskInfo.side_effect = [
             task,
@@ -326,7 +336,8 @@ class TestTaskInfo(unittest.TestCase):
         ]
         self.get_server.return_value = server
         webidx.taskinfo(self.environ, self.task_id)
-        self.assertEqual(self.environ['koji.values']['rtask'], 'restartVerify')
+        self.assertEqual(self.environ['koji.values']['params'],
+                         {'host': 'host', 'task': 'restartVerify'})
 
     def test_taskinfo_sorting_compare(self):
         """Test taskinfo function sorting results"""
diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py
index e3ec3b3..fe69ecf 100644
--- a/www/kojiweb/index.py
+++ b/www/kojiweb/index.py
@@ -33,6 +33,7 @@ import time
 import itertools
 
 import koji
+from koji.tasks import parse_task_params
 import kojiweb.util
 from koji.server import ServerRedirect
 from kojiweb.util import _genHTML, _getValidTokens, _initValues
@@ -641,9 +642,17 @@ def taskinfo(environ, taskID):
 
     values['title'] = koji.taskLabel(task) + ' | Task Info'
 
+    try:
+        params = parse_task_params(task['method'], task['request'])
+    except TypeError:
+        # unknown tasks/plugins
+        params = {'args': task['request']}
     values['task'] = task
-    params = task['request']
     values['params'] = params
+    if 'opts' in params:
+        values['opts'] = params.pop('opts')
+    else:
+        values['opts'] = {}
 
     if task['channel_id']:
         channel = server.getChannel(task['channel_id'])
@@ -687,49 +696,55 @@ def taskinfo(environ, taskID):
     buildroots = server.listBuildroots(taskID=task['id'])
     values['buildroots'] = buildroots
 
-    if task['method'] in ('buildArch', 'buildMaven', 'buildSRPMFromSCM'):
-        if len(params) > 1:
-            tag_id = params[1]
+    def _get_tag(tag_id):
+        if not tag_id:
+            return None
+        elif isinstance(tag_id, dict):
+            return tag_id
+        else:
             try:
-                values['buildTag'] = server.getTag(tag_id, strict=True)
+                return server.getTag(tag_id, strict=True)
             except koji.GenericError:
-                values['buildTag'] = {'name': "%d (deleted)" % tag_id, 'id': None}
-    elif task['method'] == 'tagBuild':
-        destTag = server.getTag(params[0])
-        build = server.getBuild(params[1])
-        values['destTag'] = destTag
-        values['build'] = build
-    elif task['method'] in ('newRepo', 'distRepo', 'createdistrepo'):
-        tag = server.getTag(params[0])
-        values['tag'] = tag
-    elif task['method'] == 'tagNotification':
-        destTag = None
-        if params[2]:
-            destTag = server.getTag(params[2])
-        srcTag = None
-        if params[3]:
-            srcTag = server.getTag(params[3])
-        build = server.getBuild(params[4])
-        user = server.getUser(params[5])
-        values['destTag'] = destTag
-        values['srcTag'] = srcTag
-        values['build'] = build
-        values['user'] = user
-    elif task['method'] == 'dependantTask':
-        deps = [server.getTaskInfo(depID, request=True) for depID in params[0]]
-        values['deps'] = deps
-    elif task['method'] == 'wrapperRPM':
-        buildTarget = params[1]
-        values['buildTarget'] = buildTarget
-        if params[3]:
-            wrapTask = server.getTaskInfo(params[3]['id'], request=True)
-            values['wrapTask'] = wrapTask
-    elif task['method'] == 'restartVerify':
-        values['rtask'] = server.getTaskInfo(params[0], request=True)
-    elif task['method'] == 'build':
-        if len(params) > 1:
-            if isinstance(params[1], dict):
-                params[1] = params[1].get('name')
+                return {'name': "%d (deleted)" % tag_id, 'id': None}
+
+    if 'root' in params:
+        params['build_tag'] = _get_tag(params.pop('root'))
+    if 'tag_id' in params:
+        params['destination_tag'] = _get_tag(params.pop('tag_id'))
+    if 'tag' in params:
+        params['tag'] = _get_tag(params.pop('tag'))
+    if 'tag_info' in params:
+        params['destination_tag'] = _get_tag(params.pop('tag_info'))
+    if 'from_info' in params:
+        params['source tag'] = _get_tag(params.pop('from_info'))
+    if 'build_info' in params:
+        params['build'] = server.getBuild(params.pop('build_info'))
+    if 'build_id' in params:
+        params['build'] = server.getBuild(params.pop('build_id'))
+    if 'user_info' in params:
+        params['user'] = server.getUser(params.pop('user_info'))
+    if 'task_list' in params:
+        tmp = []
+        for t in params.pop('task_list'):
+            base = parse_task_params(t[0], t[1])
+            base['method'] = t[0]
+            base['opts'] = t[2]
+            tmp.append(base)
+        params['task_list'] = tmp
+    if 'wait_list' in params:
+        params['wait_list'] = [server.getTaskInfo(t) for t in params['wait_list']]
+    if 'target' in params:
+        params['build_target'] = server.getBuildTarget(params.pop('target'))
+    if 'build_target' in params:
+        params['build_target'] = server.getBuildTarget(params.pop('build_target'))
+    if 'build_tag' in params:
+        params['build_tag'] = _get_tag(params.pop('build_tag'))
+    if 'task_id' in params:
+        params['task'] = server.getTaskInfo(params.pop('task_id'), request=True)
+    if 'repo_id' in params:
+        params['repo'] = server.repoInfo(params.pop('repo_id'))
+    if 'buildrootID' in params:
+        params['buildroot'] = server.getBuildroot(params.pop('buildrootID'))
 
     values['taskBuilds'] = []
     if task['state'] in (koji.TASK_STATES['CLOSED'], koji.TASK_STATES['FAILED']):
@@ -767,10 +782,6 @@ def taskinfo(environ, taskID):
     else:
         values['perms'] = []
 
-    try:
-        values['params_parsed'] = _genHTML(environ, 'taskinfo_params.chtml')
-    except Exception:
-        values['params_parsed'] = None
     return _genHTML(environ, 'taskinfo.chtml')
 
 
diff --git a/www/kojiweb/taskinfo.chtml b/www/kojiweb/taskinfo.chtml
index 45e736b..d966a5a 100644
--- a/www/kojiweb/taskinfo.chtml
+++ b/www/kojiweb/taskinfo.chtml
@@ -30,6 +30,65 @@
   #end if
 #end def
 
+#def printMap($vals, $prefix='')
+  #for $key, $value in $vals.items()
+  #if $key == 'properties'
+  ${prefix}properties&nbsp;=&nbsp;$printProperties($value)<br/>
+  #elif $key != '__starstar'
+  $prefix$key&nbsp;=&nbsp;$printValue($key, $value)<br/>
+  #end if
+  #end for
+#end def
+
+#def printOpts($opts)
+  #if $opts
+  <strong>Options:</strong><br/>
+  $printMap($opts, '&nbsp;&nbsp;')
+  #end if
+#end def
+
+#def printValue($key, $value, $sep=', ')
+  #if $value is None
+None
+  #elif $key == 'user'
+<a href="userinfo?userID=$value['id']">$value['name']</a>
+  #elif $key == 'task'
+<a href="taskinfo?taskID=$value['id']">$value['id']</a>
+  #elif $key == 'host'
+<a href="hostinfo?hostID=$value['id']">$value['name']</a>
+  #elif $key == 'build'
+<a href="buildinfo?buildID=$value['id']">$value['nvr']</a>
+  #elif $key == 'buildroot'
+<a href="buildrootinfo?buildrootID=$value['id']">$value['id']</a>
+  #elif $key in ('tag', 'destination_tag', 'build_tag')
+<a href="taginfo?tagID=$value['id']">$value['name']</a>
+  #elif $key in ('build_target', 'target_info')
+<a href="targetinfo?targetID=$value['id']">$value['name']</a>
+  #elif $key in ('repo_info', 'oldrepo', 'repo')
+    #if $key == 'repo' and isinstance($value, list)
+$sep.join([$str($val) for $val in $value])
+    #else
+<a href="repoinfo?repoID=$value.id">$value.id</a> ($koji.formatTimeLong($value.create_ts))
+    #end if
+  #elif $key == 'task_list'
+    #for $task in $params['task_list']
+      <br/><strong>&nbsp;Task:</strong><br/>
+      $printMap($task, '&nbsp;&nbsp;')
+    #end for
+  #elif $isinstance($value, list)
+$sep.join([$str($val) for $val in $value])
+  #elif $isinstance($value, dict)
+$sep.join(['%s=%s' % (($n == '' and "''" or $n), $v) for $n, $v in $value.items()])
+  #else
+$value
+  #end if
+#end def
+
+#def printProperties($props)
+  #echo ', '.join([$v is not None and '%s=%s' % ($n, $v) or $str($n) for $n, $v in $props.items()])
+#end def
+
+
 #include "includes/header.chtml"
 
   <h4>Information for task <a href="taskinfo?taskID=$task.id">$util.escapeHTML($koji.taskLabel($task))</a></h4>
@@ -44,12 +103,12 @@
     <tr>
       <th>Parameters</th>
       <td>
-      #if $params_parsed
-      $params_parsed
-      #else
-      <div class="error">Parameters are not correct for this method.</div>
-      $params
-      #end if
+        #for key in $sorted($params)
+          #if $params[$key] is not None
+        <strong>$key.capitalize().replace('_', ' ')</strong>: $printValue($key, $params[$key])<br/>
+          #end if
+        #end for
+        $printOpts($opts)
       </td>
     </tr>
     <tr>
diff --git a/www/kojiweb/taskinfo_params.chtml b/www/kojiweb/taskinfo_params.chtml
deleted file mode 100644
index 6cdd371..0000000
--- a/www/kojiweb/taskinfo_params.chtml
+++ /dev/null
@@ -1,279 +0,0 @@
-#from urllib.parse import quote
-#import koji
-#from kojiweb import util
-
-#def printValue($key, $value, $sep=', ')
-  #if $key in ('brootid', 'buildroot_id')
-<a href="buildrootinfo?buildrootID=$value">$value</a>
-  #elif $isinstance($value, list)
-$sep.join([$str($val) for $val in $value])
-  #elif $isinstance($value, dict)
-$sep.join(['%s=%s' % (($n == '' and "''" or $n), $v) for $n, $v in $value.items()])
-  #else
-$value
-  #end if
-#end def
-
-#def printProperties($props)
-  #echo ', '.join([$v is not None and '%s=%s' % ($n, $v) or $str($n) for $n, $v in $props.items()])
-#end def
-
-#def printMap($vals, $prefix='')
-  #for $key, $value in $vals.items()
-  #if $key == 'properties'
-  ${prefix}properties&nbsp;=&nbsp;$printProperties($value)<br/>
-  #elif $key != '__starstar'
-  $prefix$key&nbsp;=&nbsp;$printValue($key, $value)<br/>
-  #end if
-  #end for
-#end def
-
-#def printOpts($opts)
-  #if $opts
-  <strong>Options:</strong><br/>
-  $printMap($opts, '&nbsp;&nbsp;')
-  #end if
-#end def
-
-#if $task.method == 'buildSRPMFromSCM'
-<strong>SCM URL:</strong> $params[0]<br/>
-#if $len($params) > 1
-<strong>Build Tag:</strong>
-#if $buildTag.id
-<a href="taginfo?tagID=$buildTag.id">$util.escapeHTML($buildTag.name)</a>
-#else
-$util.escapeHTML($buildTag.name)
-#end if
-<br/>
-#end if
-#if $len($params) > 2
-$printOpts($params[2])
-#end if
-#elif $task.method == 'buildSRPMFromCVS'
-<strong>CVS URL:</strong> $params[0]
-#elif $task.method == 'buildArch'
-<strong>SRPM:</strong> $params[0]<br/>
-<strong>Build Tag:</strong>
-#if $buildTag.id
-<a href="taginfo?tagID=$buildTag.id">$util.escapeHTML($buildTag.name)</a>
-#else
-$util.escapeHTML($buildTag.name)
-#end if
-<br/>
-<strong>Arch:</strong> $params[2]<br/>
-<strong>Keep SRPM?</strong> #if $params[3] then 'yes' else 'no'#<br/>
-#if $len($params) > 4
-$printOpts($params[4])
-#end if
-#elif $task.method == 'tagBuild'
-<strong>Destination Tag:</strong> <a href="taginfo?tagID=$destTag.id">$util.escapeHTML($destTag.name)</a><br/>
-<strong>Build:</strong> <a href="buildinfo?buildID=$build.id">$util.escapeHTML($koji.buildLabel($build))</a>
-#elif $task.method == 'buildNotification'
-#set $build = $params[1]
-#set $buildTarget = $params[2]
-<strong>Recipients:</strong>&nbsp;$printValue('', $params[0])<br/>
-<strong>Build:</strong> <a href="buildinfo?buildID=$build.id">$util.escapeHTML($koji.buildLabel($build))</a><br/>
-#if $buildTarget
-<strong>Build Target:</strong> <a href="buildtargetinfo?targetID=$buildTarget.id">$util.escapeHTML($buildTarget.name)</a><br/>
-#else
-<strong>Build Target:</strong> (no build target)<br/>
-#end if
-<strong>Web URL:</strong> <a href="$quote($params[3])">$params[3]</a>
-#elif $task.method == 'tagNotification'
-<strong>Recipients:</strong>&nbsp;$printValue('', $params[0])<br/>
-<strong>Successful?:</strong> #if $params[1] then 'yes' else 'no'#<br/>
-#if $destTag
-<strong>Tagged Into:</strong> <a href="taginfo?tagID=$destTag.id">$util.escapeHTML($destTag.name)</a><br/>
-#end if
-#if $srcTag
-<strong>#if $destTag then 'Moved From:' else 'Untagged From:'#</strong> <a href="taginfo?tagID=$srcTag.id">$util.escapeHTML($srcTag.name)</a><br/>
-#end if
-<strong>Build:</strong> <a href="buildinfo?buildID=$build.id">$util.escapeHTML($koji.buildLabel($build))</a><br/>
-<strong>#if $destTag then 'Tagged By:' else 'Untagged By:'#</strong> <a href="userinfo?userID=$user.id">$util.escapeHTML($user.name)</a><br/>
-<strong>Ignore Success?:</strong> #if $params[6] then 'yes' else 'no'#<br/>
-#if $params[7]
-<strong>Failure Message:</strong> $params[7]
-#end if
-#elif $task.method == 'build'
-<strong>Source:</strong> $params[0]<br/>
-<strong>Build Target:</strong> <a href="buildtargetinfo?name=$quote($params[1])">$util.escapeHTML($params[1])</a><br/>
-$printOpts($params[2])
-#elif $task.method == 'maven'
-<strong>SCM URL:</strong> $util.escapeHTML($params[0])<br/>
-<strong>Build Target:</strong> <a href="buildtargetinfo?name=$quote($params[1])">$util.escapeHTML($params[1])</a><br/>
-$printOpts($params[2])
-#elif $task.method == 'buildMaven'
-<strong>SCM URL:</strong> $util.escapeHTML($params[0])<br/>
-<strong>Build Tag:</strong>
-#if $buildTag.id
-<a href="taginfo?tagID=$buildTag.id">$util.escapeHTML($buildTag.name)</a>
-#else
-$util.escapeHTML($buildTag.name)
-#end if
-<br/>
-#if $len($params) > 2
-$printOpts($params[2])
-#end if
-#elif $task.method == 'wrapperRPM'
-<strong>Spec File URL:</strong> $params[0]<br/>
-#if 'locked' in $buildTarget
-#set $buildTag = $buildTarget.name
-<strong>Build Tag:</strong>
-#if $buildTag.id
-<a href="taginfo?tagID=$buildTag.id">$util.escapeHTML($buildTag.name)</a>
-#else
-$util.escapeHTML($buildTag.name)
-#end if
-<br/>
-#else
-<strong>Build Target:</strong> <a href="buildtargetinfo?targetID=$buildTarget.id">$util.escapeHTML($buildTarget.name)</a><br/>
-#end if
-#if $params[2]
-<strong>Build:</strong> <a href="buildinfo?buildID=$params[2].id">$koji.buildLabel($params[2])</a><br/>
-#end if
-#if $params[3]
-<strong>Task:</strong> <a href="taskinfo?taskID=$wrapTask.id">$koji.taskLabel($wrapTask)</a><br/>
-#end if
-#if $len($params) > 4
-$printOpts($params[4])
-#end if
-#elif $task.method == 'chainmaven'
-<strong>Builds:</strong><br/>
-<table>
-#for $key, $val in $params[0].items()
-<tr><td><strong>$key:</strong></td><td>$printMap($val)</td></tr>
-#end for
-</table>
-<strong>Build Target:</strong> <a href="buildtargetinfo?name=$quote($params[1])">$util.escapeHTML($params[1])</a><br/>
-#if $len($params) > 2
-$printOpts($params[2])
-#end if
-#elif $task.method == 'livecd' or $task.method == 'appliance' or $task.method == 'livemedia'
-<strong>Name:</strong> $util.escapeHTML($params[0])<br/>
-<strong>Version:</strong> $util.escapeHTML($params[1])<br/>
-<strong>Arch:</strong> $util.escapeHTML($params[2])<br/>
-<strong>Build Target:</strong> <a href="buildtargetinfo?name=$quote($params[3])">$util.escapeHTML($params[3])</a><br/>
-<strong>Kickstart File:</strong> $util.escapeHTML($params[4])<br/>
-$printOpts($params[5])
-#elif $task.method == 'image'
-<strong>Arches:</strong> #echo ', '.join($params[2])#<br/>
-<strong>Build Target:</strong> <a href="buildtargetinfo?name=$quote($params[3])">$util.escapeHTML($params[3])</a><br/>
-<strong>Installation Tree:</strong> $params[4]<br/>
-$printOpts($params[5])
-#elif $task.method == 'createLiveCD' or $task.method == 'createAppliance' or $task.method == 'createLiveMedia'
-#if $len($params) > 4:
-## new method signature
-<strong>Arch:</strong> $util.escapeHTML($params[3])<br/>
-<strong>Kickstart File:</strong> $util.escapeHTML($params[7])<br/>
-#if $len($params) > 8
-$printOpts($params[8])
-#end if
-#else
-## old method signature
-<strong>Arch:</strong> $util.escapeHTML($params[0])<br/>
-<strong>Build Target:</strong> <a href="buildtargetinfo?name=$quote($params[1])">$util.escapeHTML($params[1])</a><br/>
-<strong>Kickstart File:</strong> $util.escapeHTML($params[2])<br/>
-#if $len($params) > 3
-$printOpts($params[3])
-#end if
-#end if
-#elif $task.method == 'createImage'
-#set $target = $params[4]
-<strong>Build Target:</strong> <a href="buildtargetinfo?targetID=$target.id">$util.escapeHTML($target.name)</a><br/>
-<strong>Install Tree:</strong> $util.escapeHTML($params[7])<br/>
-$printOpts($params[8])
-#elif $task.method == 'winbuild'
-<strong>VM:</strong> $util.escapeHTML($params[0])<br/>
-<strong>SCM URL:</strong> $util.escapeHTML($params[1])<br/>
-<strong>Build Target:</strong> <a href="buildtargetinfo?name=$quote($params[2])">$util.escapeHTML($params[2])</a><br/>
-#if $len($params) > 3
-$printOpts($params[3])
-#end if
-#elif $task.method == 'vmExec'
-<strong>VM:</strong> $util.escapeHTML($params[0])<br/>
-<strong>Exec Params:</strong><br/>
-#for $info in $params[1]
-#if $isinstance($info, dict)
-$printMap($info, '&nbsp;&nbsp;&nbsp;&nbsp;')
-#else
-&nbsp;&nbsp;$info<br/>
-#end if
-#end for
-#if $len($params) > 2
-$printOpts($params[2])
-#end if
-#elif $task.method == 'newRepo'
-<strong>Tag:</strong> <a href="taginfo?tagID=$tag.id">$util.escapeHTML($tag.name)</a><br/>
-#if $len($params) > 1
-  $printOpts($params[1])
-#end if
-#elif $task.method == 'distRepo'
-<strong>Tag:</strong> <a href="taginfo?tagID=$tag.id">$util.escapeHTML($tag.name)</a><br/>
-<strong>Repo ID:</strong> <a href="repoinfo?repoID=$params[1]">$params[1]</a></br>
-<strong>Keys:</strong> $printValue(0, $params[2])<br/>
-$printOpts($params[3])
-#elif $task.method == 'prepRepo'
-<strong>Tag:</strong> <a href="taginfo?tagID=$params[0].id">$util.escapeHTML($params[0].name)</a>
-#elif $task.method == 'createrepo'
-<strong>Repo ID:</strong> <a href="repoinfo?repoID=$params[0]">$params[0]</a><br/>
-<strong>Arch:</strong> $util.escapeHTML($params[1])<br/>
-#set $oldrepo = $params[2]
-#if $oldrepo
-    <strong>Old Repo ID:</strong> <a href="repoinfo?repoID=$oldrepo.id">$oldrepo.id</a><br/>
-    <strong>Old Repo Creation:</strong> $koji.formatTimeLong($oldrepo.create_ts)<br/>
-#end if
-#if $len($params) > 4 and $params[4]
-    <strong>External Repos:</strong> $printValue(None, [ext['external_repo_name'] for ext in $params[3]])<br/>
-#end if
-#elif $task.method == 'createdistrepo'
-<strong>Tag:</strong> <a href="taginfo?tagID=$tag.id">$util.escapeHTML($tag.name)</a><br/>
-<strong>Repo ID:</strong> <a href="repoinfo?repoID=$params[1]">$params[1]</a></br>
-<strong>Arch:</strong> $printValue(0, $params[2])<br/>
-<strong>Keys:</strong> $printValue(0, $params[3])<br/>
-<strong>Options:</strong> $printMap($params[4], '&nbsp;&nbsp;&nbsp;&nbsp;')
-#elif $task.method == 'dependantTask'
-<strong>Dependant Tasks:</strong><br/>
-#for $dep in $deps
-&nbsp;&nbsp;<a href="taskinfo?taskID=$dep.id" class="task$util.taskState($dep.state)">$koji.taskLabel($dep)</a><br/>
-#end for
-<strong>Subtasks:</strong><br/>
-#for $subtask in $params[1]
-&nbsp;&nbsp;<strong>Method:</strong> $subtask[0]<br/>
-&nbsp;&nbsp;<strong>Parameters:</strong> #echo ', '.join([$str($subparam) for $subparam in $subtask[1]])#<br/>
-#if $len($subtask) > 2 and $subtask[2]
-&nbsp;&nbsp;<strong>Options:</strong><br/>
-$printMap($subtask[2], '&nbsp;&nbsp;&nbsp;&nbsp;')
-#end if
-<br/>
-#end for
-#elif $task.method == 'chainbuild'
-<strong>Build Groups:</strong><br/>
-#set $groupNum = 0
-#for $urls in $params[0]
-#set $groupNum += 1
-&nbsp;&nbsp;<strong>$groupNum</strong>: #echo ', '.join($urls)#<br/>
-#end for
-<strong>Build Target:</strong> <a href="buildtargetinfo?name=$quote($params[1])">$util.escapeHTML($params[1])</a><br/>
-$printOpts($params[2])
-#elif $task.method == 'waitrepo'
-<strong>Build Tag:</strong> $util.escapeHTML($params[0])<br/>
-#if $params[1]
-<strong>Newer Than:</strong> $util.escapeHTML($params[1])<br/>
-#end if
-#if $params[2]
-<strong>NVRs:</strong> $printValue('', $params[2])
-#end if
-#elif $task.method == 'restart'
-<strong>Host:</strong> <a href="hostinfo?hostID=$params[0].id">$util.escapeHTML($params[0].name)</a><br/>
-#elif $task.method == 'restartVerify'
-<strong>Host:</strong> <a href="hostinfo?hostID=$params[1].id">$util.escapeHTML($params[1].name)</a><br/>
-<strong>Restart Task:</strong>
-    <a href="taskinfo?taskID=$rtask.id" class="task$util.taskState($rtask.state)">$koji.taskLabel($rtask)</a><br/>
-#elif $task.method == 'runroot'
-<strong>Build Tag:</strong> <a href="taginfo?tagID=$params[0]">$params[0]</a><br/>
-<strong>Arch:</strong> $util.escapeHTML($params[1])<br/>
-$printOpts($params[3])
-<strong>Commands:</strong> $util.escapeHTML($params[2])<br/>
-#else
-$params
-#end if

