merge master onto the mead branch

This commit is contained in:
Mike Bonnet 2009-06-12 14:04:55 -04:00
commit d8055a2274
11 changed files with 174 additions and 69 deletions

View file

@ -709,14 +709,16 @@ class BuildRoot(object):
if len(parts) < 2:
continue
#first field is formated by yum as [e:]n-v-r.a
nvra = "%(name)s-%(version)s-%(release)s" % koji.parse_NVRA(parts[0])
nvra = "%(name)s-%(version)s-%(release)s.%(arch)s" % koji.parse_NVRA(parts[0])
origin_idx[nvra] = parts[1]
fo2.close()
# mergerepo starts from a local repo in the task workdir, so internal
# rpms have an odd-looking origin that we need to look for
localtail = '/repo_%s_premerge/' % self.repo_info['id']
for rpm_info in rpmlist:
key = "%(name)s-%(version)s-%(release)s" % rpm_info
key = "%(name)s-%(version)s-%(release)s.%(arch)s" % rpm_info
# src rpms should not show up in rpmlist so we do not have to
# worry about fixing the arch for them
ext_url = origin_idx.get(key)
if not ext_url:
raise koji.BuildError, "No origin for %s" % key

View file

@ -4063,6 +4063,27 @@ def handle_cancel(options, session, args):
for build in blist:
session.cancelBuild(build)
def handle_set_task_priority(options, session, args):
"[admin] Set task priority"
usage = _("usage: %prog set-task-priority [options] --priority=<priority> <task-id> [task-id]...")
usage += _("\n(Specify the --help global option for a list of other help options)")
parser = OptionParser(usage=usage)
parser.add_option("--priority", type="int", help=_("New priority"))
parser.add_option("--recurse", action="store_true", default=False, help=_("Change priority of child tasks as well"))
(options, args) = parser.parse_args(args)
if len(args) == 0:
parser.error(_("You must specify at least one task id"))
assert False
if options.priority is None:
parser.error(_("You must specify --priority"))
assert False
activate_session(session)
for task_id in args:
session.setTaskPriority(task_id, options.priority, options.recurse)
def handle_list_tasks(options, session, args):
"Print the list of tasks"
usage = _("usage: %prog list-tasks [options]")

View file

@ -219,6 +219,21 @@ class Task(object):
q = """UPDATE task SET weight=%(weight)s WHERE id = %(task_id)s"""
_dml(q,locals())
def setPriority(self, priority, recurse=False):
"""Set priority for task"""
task_id = self.id
priority = int(priority)
# access checks should be performed by calling function
q = """UPDATE task SET priority=%(priority)s WHERE id = %(task_id)s"""
_dml(q,locals())
if recurse:
"""Change priority of child tasks"""
q = """SELECT id FROM task WHERE parent = %(task_id)s"""
for (child_id,) in _fetchMulti(q, locals()):
Task(child_id).setPriority(priority, recurse=True)
def _close(self,result,state):
"""Mark task closed and set response
@ -3472,7 +3487,7 @@ def get_channel(channelInfo, strict=False):
return _singleRow(query, locals(), fields, strict)
def query_buildroots(hostID=None, tagID=None, state=None, rpmID=None, archiveID=None, taskID=None, buildrootID=None):
def query_buildroots(hostID=None, tagID=None, state=None, rpmID=None, archiveID=None, taskID=None, buildrootID=None, queryOpts=None):
"""Return a list of matching buildroots
Optional args:
@ -3493,14 +3508,13 @@ def query_buildroots(hostID=None, tagID=None, state=None, rpmID=None, archiveID=
('EXTRACT(EPOCH FROM retire_events.time)','retire_ts'),
('repo_create.id', 'repo_create_event_id'), ('repo_create.time', 'repo_create_event_time')]
query = """SELECT %s FROM buildroot
JOIN host ON host.id = buildroot.host_id
JOIN repo ON repo.id = buildroot.repo_id
JOIN tag ON tag.id = repo.tag_id
JOIN events AS create_events ON create_events.id = buildroot.create_event
LEFT OUTER JOIN events AS retire_events ON buildroot.retire_event = retire_events.id
JOIN events AS repo_create ON repo_create.id = repo.create_event
"""
tables = ['buildroot']
joins=['host ON host.id = buildroot.host_id',
'repo ON repo.id = buildroot.repo_id',
'tag ON tag.id = repo.tag_id',
'events AS create_events ON create_events.id = buildroot.create_event',
'LEFT OUTER JOIN events AS retire_events ON buildroot.retire_event = retire_events.id',
'events AS repo_create ON repo_create.id = repo.create_event']
clauses = []
if buildrootID != None:
@ -3518,24 +3532,19 @@ def query_buildroots(hostID=None, tagID=None, state=None, rpmID=None, archiveID=
else:
clauses.append('buildroot.state = %(state)i')
if rpmID != None:
query += """JOIN buildroot_listing ON buildroot.id = buildroot_listing.buildroot_id
"""
joins.append('buildroot_listing ON buildroot.id = buildroot_listing.buildroot_id')
fields.append(('buildroot_listing.is_update', 'is_update'))
clauses.append('buildroot_listing.rpm_id = %(rpmID)i')
if archiveID != None:
query += """JOIN buildroot_archives ON buildroot.id = buildroot_archives.buildroot_id
"""
joins.append('buildroot_archives ON buildroot.id = buildroot_archives.buildroot_id')
clauses.append('buildroot_archives.archive_id = %(archiveID)i')
if taskID != None:
clauses.append('buildroot.task_id = %(taskID)i')
query = query % ', '.join([pair[0] for pair in fields])
if len(clauses) > 0:
query += 'WHERE ' + ' AND '.join(clauses)
return _multiRow(query, locals(), [pair[1] for pair in fields])
query = QueryProcessor(columns=[f[0] for f in fields], aliases=[f[1] for f in fields],
tables=tables, joins=joins, clauses=clauses, values=locals(),
opts=queryOpts)
return query.execute()
def get_buildroot(buildrootID, strict=False):
"""Return information about a buildroot. buildrootID must be an int ID."""
@ -6263,6 +6272,12 @@ class RootExports(object):
raise koji.ActionNotAllowed, 'Cannot cancel task, not owner'
task.cancelChildren()
def setTaskPriority(self, task_id, priority, recurse=True):
"""Set task priority"""
context.session.assertPerm('admin')
task = Task(task_id)
task.setPriority(priority, recurse=recurse)
def listTagged(self,tag,event=None,inherit=False,prefix=None,latest=False,package=None,owner=None,maven_only=False):
"""List builds tagged with tag"""
if not isinstance(tag,int):
@ -7146,9 +7161,15 @@ class RootExports(object):
joins.append('host_channels on host.id = host_channels.host_id')
clauses.append('host_channels.channel_id = %(channelID)i')
if ready != None:
clauses.append('ready is %s' % ready)
if ready:
clauses.append('ready is true')
else:
clauses.append('ready is false')
if enabled != None:
clauses.append('enabled is %s' % enabled)
if enabled:
clauses.append('enabled is true')
else:
clauses.append('enabled is false')
if userID != None:
clauses.append('user_id = %(userID)i')

View file

@ -4,18 +4,19 @@
# Authors: Frederic Lepied, Florian Festi
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Library General Public License as published by
# the Free Software Foundation; version 2 only
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Library General Public License for more details.
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Library General Public License
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# This library and program is heavily based on rpmdiff from the rpmlint package
# It was modified to be used as standalone library for the Koji project.

View file

@ -856,7 +856,8 @@ def handle_prune():
#get builds
history = session.tagHistory(tag=tagname)
if not history:
print "No history for %s" % tagname
if options.debug:
print "No history for %s" % tagname
continue
history = [(h['create_ts'], h) for h in history if h['active']]
history.sort()

View file

@ -1,15 +1,32 @@
#from kojiweb import util
#attr _PASSTHROUGH = ['state', 'order']
#include "includes/header.chtml"
<h4>Hosts</h4>
<table class="data-list">
<tr>
<td colspan="6">
<table class="nested">
<tr><td>
<strong>State</strong>:
</td><td>
<select name="state" class="filterlist" onchange="javascript: window.location = 'hosts?state=' + this.value + '$util.passthrough_except($self, 'state')';">
<option value="enabled" #if $state == 'enabled' then 'selected="selected"' else ''#>enabled</option>
<option value="disabled" #if $state == 'disabled' then 'selected="selected"' else ''#>disabled</option>
<option value="all" #if $state == 'all' then 'selected="selected"' else ''#>all</option>
</select>
</td></tr>
</table>
</td>
</tr>
<tr>
<td class="paginate" colspan="6">
#if $len($hostPages) > 1
<form class="pageJump" action="">
Page:
<select onchange="javascript: window.location = 'hosts?start=' + this.value * $hostRange + '$util.passthrough($self, 'order')';">
<select onchange="javascript: window.location = 'hosts?start=' + this.value * $hostRange + '$util.passthrough_except($self)';">
#for $pageNum in $hostPages
<option value="$pageNum"#if $pageNum == $hostCurrentPage then ' selected="selected"' else ''#>#echo $pageNum + 1#</option>
#end for
@ -17,23 +34,23 @@
</form>
#end if
#if $hostStart > 0
<a href="hosts?start=#echo $hostStart - $hostRange #$util.passthrough($self, 'order')">&lt;&lt;&lt;</a>
<a href="hosts?start=#echo $hostStart - $hostRange #$util.passthrough_except($self)">&lt;&lt;&lt;</a>
#end if
#if $totalHosts != 0
<strong>Hosts #echo $hostStart + 1 # through #echo $hostStart + $hostCount # of $totalHosts</strong>
#end if
#if $hostStart + $hostCount < $totalHosts
<a href="hosts?start=#echo $hostStart + $hostRange#$util.passthrough($self, 'order')">&gt;&gt;&gt;</a>
<a href="hosts?start=#echo $hostStart + $hostRange#$util.passthrough_except($self)">&gt;&gt;&gt;</a>
#end if
</td>
</tr>
<tr class="list-header">
<th><a href="hosts?order=$util.toggleOrder($self, 'id')">ID</a> $util.sortImage($self, 'id')</th>
<th><a href="hosts?order=$util.toggleOrder($self, 'name')">Name</a> $util.sortImage($self, 'name')</th>
<th><a href="hosts?order=$util.toggleOrder($self, 'arches')">Arches</a> $util.sortImage($self, 'arches')</th>
<th><a href="hosts?order=$util.toggleOrder($self, 'enabled')">Enabled?</a> $util.sortImage($self, 'enabled')</th>
<th><a href="hosts?order=$util.toggleOrder($self, 'ready')">Ready?</a> $util.sortImage($self, 'ready')</th>
<th><a href="hosts?order=$util.toggleOrder($self, 'last_update')">Last Update</a> $util.sortImage($self, 'last_update')</th>
<th><a href="hosts?order=$util.toggleOrder($self, 'id')$util.passthrough_except($self, 'order')">ID</a> $util.sortImage($self, 'id')</th>
<th><a href="hosts?order=$util.toggleOrder($self, 'name')$util.passthrough_except($self, 'order')">Name</a> $util.sortImage($self, 'name')</th>
<th><a href="hosts?order=$util.toggleOrder($self, 'arches')$util.passthrough_except($self, 'order')">Arches</a> $util.sortImage($self, 'arches')</th>
<th><a href="hosts?order=$util.toggleOrder($self, 'enabled')$util.passthrough_except($self, 'order')">Enabled?</a> $util.sortImage($self, 'enabled')</th>
<th><a href="hosts?order=$util.toggleOrder($self, 'ready')$util.passthrough_except($self, 'order')">Ready?</a> $util.sortImage($self, 'ready')</th>
<th><a href="hosts?order=$util.toggleOrder($self, 'last_update')$util.passthrough_except($self, 'order')">Last Update</a> $util.sortImage($self, 'last_update')</th>
</tr>
#if $len($hosts) > 0
#for $host in $hosts
@ -56,7 +73,7 @@
#if $len($hostPages) > 1
<form class="pageJump" action="">
Page:
<select onchange="javascript: window.location = 'hosts?start=' + this.value * $hostRange + '$util.passthrough($self, 'order')';">
<select onchange="javascript: window.location = 'hosts?start=' + this.value * $hostRange + '$util.passthrough_except($self)';">
#for $pageNum in $hostPages
<option value="$pageNum"#if $pageNum == $hostCurrentPage then ' selected="selected"' else ''#>#echo $pageNum + 1#</option>
#end for
@ -64,13 +81,13 @@
</form>
#end if
#if $hostStart > 0
<a href="hosts?start=#echo $hostStart - $hostRange #$util.passthrough($self, 'order')">&lt;&lt;&lt;</a>
<a href="hosts?start=#echo $hostStart - $hostRange #$util.passthrough_except($self)">&lt;&lt;&lt;</a>
#end if
#if $totalHosts != 0
<strong>Hosts #echo $hostStart + 1 # through #echo $hostStart + $hostCount # of $totalHosts</strong>
#end if
#if $hostStart + $hostCount < $totalHosts
<a href="hosts?start=#echo $hostStart + $hostRange#$util.passthrough($self, 'order')">&gt;&gt;&gt;</a>
<a href="hosts?start=#echo $hostStart + $hostRange#$util.passthrough_except($self)">&gt;&gt;&gt;</a>
#end if
</td>
</tr>

View file

@ -689,14 +689,16 @@ def packages(req, tagID=None, userID=None, order='package_name', start=None, pre
server = _getServer(req)
tag = None
if tagID != None:
tagID = int(tagID)
tag = server.getTag(tagID)
if tagID.isdigit():
tagID = int(tagID)
tag = server.getTag(tagID, strict=True)
values['tagID'] = tagID
values['tag'] = tag
user = None
if userID != None:
userID = int(userID)
user = server.getUser(userID)
if userID.isdigit():
userID = int(userID)
user = server.getUser(userID, strict=True)
values['userID'] = userID
values['user'] = user
values['order'] = order
@ -1166,7 +1168,8 @@ def userinfo(req, userID, packageOrder='package_name', packageStart=None, buildO
values['user'] = user
values['userID'] = userID
values['taskCount'] = server.listTasks(opts={'owner': user['id'], 'parent': None}, queryOpts={'countOnly': True})
packages = kojiweb.util.paginateResults(server, values, 'listPackages', kw={'userID': user['id'], 'with_dups': True},
start=packageStart, dataName='packages', prefix='package', order=packageOrder, pageSize=10)
@ -1175,7 +1178,7 @@ def userinfo(req, userID, packageOrder='package_name', packageStart=None, buildO
return _genHTML(req, 'userinfo.chtml')
def rpminfo(req, rpmID, fileOrder='name', fileStart=None):
def rpminfo(req, rpmID, fileOrder='name', fileStart=None, buildrootOrder='-id', buildrootStart=None):
values = _initValues(req, 'RPM Info', 'builds')
server = _getServer(req)
@ -1206,8 +1209,9 @@ def rpminfo(req, rpmID, fileOrder='name', fileStart=None):
headers = server.getRPMHeaders(rpm['id'], headers=['summary', 'description'])
values['summary'] = koji.fixEncoding(headers.get('summary'))
values['description'] = koji.fixEncoding(headers.get('description'))
buildroots = server.listBuildroots(rpmID=rpm['id'])
buildroots.sort(kojiweb.util.sortByKeyFunc('-create_event_time'))
buildroots = kojiweb.util.paginateMethod(server, values, 'listBuildroots', kw={'rpmID': rpm['id']},
start=buildrootStart, dataName='buildroots', prefix='buildroot',
order=buildrootOrder)
values['rpmID'] = rpmID
values['rpm'] = rpm
@ -1298,13 +1302,23 @@ def cancelbuild(req, buildID):
mod_python.util.redirect(req, 'buildinfo?buildID=%i' % build['id'])
def hosts(req, start=None, order='name'):
def hosts(req, state='enabled', start=None, order='name'):
values = _initValues(req, 'Hosts', 'hosts')
server = _getServer(req)
values['order'] = order
hosts = server.listHosts()
args = {}
if state == 'enabled':
args['enabled'] = True
elif state == 'disabled':
args['enabled'] = False
else:
state = 'all'
values['state'] = state
hosts = server.listHosts(**args)
server.multicall = True
for host in hosts:

View file

@ -3,6 +3,8 @@
#import time
#import urllib
#attr _PASSTHROUGH = ['rpmID', 'fileOrder', 'fileStart', 'buildrootOrder', 'buildrootStart']
#include "includes/header.chtml"
#set $epoch = ($rpm.epoch != None and $str($rpm.epoch) + ':' or '')
<h4>Information for RPM <a href="rpminfo?rpmID=$rpm.id">$rpm.name-$epoch$rpm.version-$rpm.release.${rpm.arch}.rpm</a></h4>
@ -141,7 +143,7 @@
#if $len($filePages) > 1
<form class="pageJump" action="">
Page:
<select onchange="javascript: window.location = 'rpminfo?fileStart=' + this.value * $fileRange + '$util.passthrough($self, 'rpmID', 'fileOrder')#filelist';">
<select onchange="javascript: window.location = 'rpminfo?fileStart=' + this.value * $fileRange + '$util.passthrough_except($self, 'fileStart')#filelist';">
#for $pageNum in $filePages
<option value="$pageNum"#if $pageNum == $fileCurrentPage then ' selected="selected"' else ''#>#echo $pageNum + 1#</option>
#end for
@ -149,17 +151,17 @@
</form>
#end if
#if $fileStart > 0
<a href="rpminfo?fileStart=#echo $fileStart - $fileRange #$util.passthrough($self, 'rpmID', 'fileOrder')#filelist">&lt;&lt;&lt;</a>
<a href="rpminfo?fileStart=#echo $fileStart - $fileRange #$util.passthrough_except($self, 'fileStart')#filelist">&lt;&lt;&lt;</a>
#end if
<strong>#echo $fileStart + 1 # through #echo $fileStart + $fileCount # of $totalFiles</strong>
#if $fileStart + $fileCount < $totalFiles
<a href="rpminfo?fileStart=#echo $fileStart + $fileRange#$util.passthrough($self, 'rpmID', 'fileOrder')#filelist">&gt;&gt;&gt;</a>
<a href="rpminfo?fileStart=#echo $fileStart + $fileRange#$util.passthrough_except($self, 'fileStart')#filelist">&gt;&gt;&gt;</a>
#end if
</td>
</tr>
<tr class="list-header">
<th><a href="rpminfo?fileOrder=$util.toggleOrder($self, 'name', 'fileOrder')$util.passthrough($self, 'rpmID')#filelist">Name</a> $util.sortImage($self, 'name', 'fileOrder')</th>
<th><a href="rpminfo?fileOrder=$util.toggleOrder($self, 'size', 'fileOrder')$util.passthrough($self, 'rpmID')#filelist">Size</a> $util.sortImage($self, 'size', 'fileOrder')</th>
<th><a href="rpminfo?fileOrder=$util.toggleOrder($self, 'name', 'fileOrder')$util.passthrough_except($self, 'fileOrder', 'fileStart')#filelist">Name</a> $util.sortImage($self, 'name', 'fileOrder')</th>
<th><a href="rpminfo?fileOrder=$util.toggleOrder($self, 'size', 'fileOrder')$util.passthrough_except($self, 'fileOrder', 'fileStart')#filelist">Size</a> $util.sortImage($self, 'size', 'fileOrder')</th>
</tr>
#for $file in $files
<tr class="$util.rowToggle($self)">
@ -174,26 +176,48 @@
</tr>
#end if
<tr>
<th>Component&nbsp;of</th>
<th id="buildrootlist">Component&nbsp;of</th>
<td class="container">
#if $len($buildroots) > 0
<table class="nested data-list">
<tr>
<td class="paginate" colspan="3">
#if $len($buildrootPages) > 1
<form class="pageJump" action="">
Page:
<select onchange="javascript: window.location = 'rpminfo?buildrootStart=' + this.value * $buildrootRange + '$util.passthrough_except($self, 'buildrootStart')#buildrootlist';">
#for $pageNum in $buildrootPages
<option value="$pageNum"#if $pageNum == $buildrootCurrentPage then ' selected="selected"' else ''#>#echo $pageNum + 1#</option>
#end for
</select>
</form>
#end if
#if $buildrootStart > 0
<a href="rpminfo?buildrootStart=#echo $buildrootStart - $buildrootRange #$util.passthrough_except($self, 'buildrootStart')#buildrootlist">&lt;&lt;&lt;</a>
#end if
<strong>#echo $buildrootStart + 1 # through #echo $buildrootStart + $buildrootCount # of $totalBuildroots</strong>
#if $buildrootStart + $buildrootCount < $totalBuildroots
<a href="rpminfo?buildrootStart=#echo $buildrootStart + $buildrootRange#$util.passthrough_except($self, 'buildrootStart')#buildrootlist">&gt;&gt;&gt;</a>
#end if
</td>
</tr>
<tr class="list-header">
<th>Buildroot</th><th>State</th><th>Update?</th>
<th><a href="rpminfo?buildrootOrder=$util.toggleOrder($self, 'id', 'buildrootOrder')$util.passthrough_except($self, 'buildrootOrder', 'buildrootStart')#buildrootlist">Buildroot</a> $util.sortImage($self, 'id', 'buildrootOrder')</th>
<th><a href="rpminfo?buildrootOrder=$util.toggleOrder($self, 'create_event_time', 'buildrootOrder')$util.passthrough_except($self, 'buildrootOrder', 'buildrootStart')#buildrootlist">Created</a> $util.sortImage($self, 'create_event_time', 'buildrootOrder')</th>
<th><a href="rpminfo?buildrootOrder=$util.toggleOrder($self, 'state', 'buildrootOrder')$util.passthrough_except($self, 'buildrootOrder', 'buildrootStart')#buildrootlist">State</a> $util.sortImage($self, 'state', 'buildrootOrder')</th>
</tr>
#for $buildroot in $buildroots
<tr class="$util.rowToggle($self)">
<td><a href="buildrootinfo?buildrootID=$buildroot.id">$buildroot.tag_name-$buildroot.id-$buildroot.repo_id</a></td>
<td>$util.formatTime($buildroot.create_event_time)</td>
<td>$util.imageTag($util.brStateName($buildroot.state))</td>
#set $update = $buildroot.is_update and 'yes' or 'no'
<td class="$update">$util.imageTag($update)</td>
</tr>
#end for
</table>
#else
No Buildroots
#end if
</td>
</td>
</tr>
</table>

View file

@ -313,7 +313,11 @@ ${excClass.__name__}: $cgi.escape($str($result))
<th>Output</th>
<td>
#for $filename in $output
<a href="getfile?taskID=$task.id&name=$urllib.quote($filename)">$filename</a><br/>
<a href="getfile?taskID=$task.id&name=$urllib.quote($filename)">$filename</a>
#if $filename.endswith('.log')
(<a href="getfile?taskID=$task.id&name=$urllib.quote($filename)&offset=-2000">tail</a>)
#end if
<br/>
#end for
#if $task.state not in ($koji.TASK_STATES.CLOSED, $koji.TASK_STATES.CANCELED, $koji.TASK_STATES.FAILED) and \
$task.method in ('buildSRPMFromSCM', 'buildArch', 'buildMaven', 'wrapperRPM', 'createrepo')

View file

@ -12,7 +12,7 @@
<th>ID</th><td>$user.id</td>
</tr>
<tr>
<th>Tasks</th><td><a href="tasks?owner=$user.id">view</a></td>
<th>Tasks</th><td><a href="tasks?owner=$user.name&state=all">$taskCount</a></td>
</tr>
<tr>
<th id="packagelist">Packages</th>

View file

@ -55,10 +55,10 @@
#for $user in $users
<tr class="$util.rowToggle($self)">
<td>$user.id</td>
<td><a href="userinfo?userID=$user.id">$user.name</a></td>
<td><a href="packages?userID=$user.id">view</a></td>
<td><a href="builds?userID=$user.id">view</a></td>
<td><a href="tasks?owner=$user.id">view</a></td>
<td><a href="userinfo?userID=$user.name">$user.name</a></td>
<td><a href="packages?userID=$user.name">view</a></td>
<td><a href="builds?userID=$user.name">view</a></td>
<td><a href="tasks?owner=$user.name">view</a></td>
</tr>
#end for
#else