Unit tests, fixes, and cleanup

This commit is contained in:
Mike McLean 2025-03-08 15:00:51 -05:00 committed by Tomas Kopecek
parent 402f319283
commit 0321b7128b
50 changed files with 56672 additions and 250 deletions

View file

@ -20,8 +20,8 @@ indent_size = 4
indent_style = tab
tab_width = 8
# CHTML: 2-space indents
[**/*.chtml]
# jinja html: 2-space indents
[**/*.html.j2]
indent_style = space
indent_size = 2

View file

@ -137,6 +137,7 @@ def get_options():
# parser.add_option('--pdb', action='store_true',
# help='drop into pdb on error')
parser.add_option('--user', '-u', help='fake login as user')
parser.add_option('--time', '-t', type="float", help='mock time as value')
parser.add_option('-o', '--config-option', help='override config option',
action='append', metavar='NAME=VALUE')
opts, args = parser.parse_args()
@ -189,6 +190,17 @@ def main():
override_load_config(options.config_option)
if options.user:
fake_login(options.user)
if options.time:
from unittest import mock
import datetime
dt = datetime.datetime.fromtimestamp(options.time)
mock.patch('time.time', return_value=options.time).start()
# mocking datetime is tricky, can't mock the c object directly
# and can't mock the datetime class globally without breaking other code
# so we just mock the handlers import of it
my_datetime = mock.patch.object(kojiweb_handlers.kojiweb.util, 'datetime', wraps=datetime).start()
my_datetime.datetime.now.return_value = dt
# koji.add_file_logger('koji', 'fakeweb.log')
httpd = make_server('', 8000, application)
print("Serving kojiweb on http://localhost:8000 ...")

View file

@ -199,7 +199,7 @@ Koji is comprised of several components:
koji-hub via XML-RPC.
- **koji-web** is a set of scripts that run in mod\_wsgi and use the
Cheetah templating engine to provide an web interface to Koji.
Jinja templating engine to provide an web interface to Koji.
koji-web exposes a lot of information and also provides a means for
certain operations, such as cancelling builds.
- **koji** is a CLI written in Python that provides many hooks into Koji.

View file

@ -123,7 +123,7 @@ and communicates with koji-hub via XML-RPC.
Koji-Web
--------
koji-web is a set of scripts that run in mod_wsgi and use the Cheetah
koji-web is a set of scripts that run in mod_wsgi and use the Jinja
templating engine to provide a web interface to Koji. It acts as a client to
koji-hub providing a visual interface to perform a limited amount of
administration. koji-web exposes a lot of information and also provides a means

View file

@ -895,7 +895,7 @@ The following command will test your login to the hub:
Koji Web - Interface for the Masses
===================================
Koji-web is a set of scripts that run in mod_wsgi and use the Cheetah
Koji-web is a set of scripts that run in mod_wsgi and use the Jinja
templating engine to provide an web interface to Koji. koji-web exposes a lot
of information and also provides a means for certain operations, such as
cancelling builds.

View file

@ -565,16 +565,16 @@ can be turned on in ``/etc/kojid/kojid.conf``.
Koji-Web
--------
koji-web is a set of scripts that run in mod\_wsgi and use the Cheetah
koji-web is a set of scripts that run in mod\_wsgi and use the Jinja
templating engine to provide a web interface to Koji. It acts as a
client to koji-hub providing a visual interface to perform a limited
amount of administration. koji-web exposes a lot of information and also
provides a means for certain operations, such as cancelling builds.
The web pages are derived from Cheetah templates, the syntax of which
The web pages are derived from Jinja templates, the syntax of which
you can read up on
`here <http://cheetahtemplate.org/users_guide/>`__. These
templates are the ``chtml`` files sitting in ``www/kojiweb``. You'll
`here <https://jinja.palletsprojects.com/>`__. These
templates are the ``html.j2`` files sitting in ``www/kojiweb``. You'll
notice quickly that these templates are referencing variables, but where
do they come from?
@ -582,26 +582,12 @@ The ``www/kojiweb/index.py`` file provides them. There are several
functions named after the templates they support, and in each one a
dictionary called ``values`` is populated. This is how data is gathered
about the task, build, archive, or whatever the page is about. Take your
time with ``taskinfo.chtml`` in particular, as the conditionals there
time with ``taskinfo.html.j2`` in particular, as the conditionals there
have gotten quite long. If you are adding a new task to Koji, you will
need to extend this at a minimum. A new type of build task would require
this, and possibly another that is specific to viewing the archived
information about the build. (taskinfo vs. buildinfo)
If your web page needs to display the contents of a list or dictionary,
use the ``$printMap`` function to help with that. It is often sensible
to define a function that easily prints options and values in a
dictionary. An example of this is in taskinfo.chtml.
::
#def printOpts($opts)
#if $opts
<strong>Options:</strong><br/>
$printMap($opts, '&nbsp;&nbsp;')
#end if
#end def
Finally, if you need to expand the drop-down menus of "method" types
when searching for tasks in the WebUI, you will need to add them to the
``_TASKS`` list in ``www/kojiweb/index.py``. Add values where

View file

@ -289,6 +289,7 @@ Requires: python%{python3_pkgversion}-%{name} = %{version}-%{release}
Requires: python%{python3_pkgversion}-librepo
Requires: python%{python3_pkgversion}-multilib
Requires: python%{python3_pkgversion}-cheetah
# cheetah required for backwards compatibility in wrapperRPM task
%else
Requires: python2-%{name} = %{version}-%{release}
Requires: python2-multilib
@ -359,7 +360,7 @@ Requires: httpd
Requires: python%{python3_pkgversion}-mod_wsgi
Requires: mod_auth_gssapi
Requires: python%{python3_pkgversion}-psycopg2
Requires: python%{python3_pkgversion}-cheetah
Requires: python%{python3_pkgversion}-jinja2
Requires: python%{python3_pkgversion}-%{name} = %{version}-%{release}
Provides: %{name}-web-code = %{version}-%{release}

View file

@ -3,9 +3,10 @@ try:
from unittest import mock
except ImportError:
import mock
import json
import koji
from koji.xmlrpcplus import Fault
from koji.xmlrpcplus import Fault, DateTime
class BaseFakeClientSession(koji.ClientSession):
@ -22,9 +23,11 @@ class BaseFakeClientSession(koji.ClientSession):
self._calls = []
for call in calls:
method = call['methodName']
args, kwargs = koji.decode_args(call['params'])
args, kwargs = koji.decode_args(*call['params'])
try:
result = self._callMethod(method, args, kwargs)
# multicall wraps non-fault results in a singleton
result = (result,)
ret.append(result)
except Fault as fault:
if strict:
@ -58,6 +61,12 @@ class FakeClientSession(BaseFakeClientSession):
key = self._munge([call['method'], call['args'], call['kwargs']])
self._calldata.setdefault(key, []).append(call)
def load(self, filename):
# load from json file
with open(filename, 'rt') as fp:
data = json.load(fp, object_hook=decode_data)
self.load_calls(data)
def _callMethod(self, name, args, kwargs=None, retry=True):
if self.multicall:
return super(FakeClientSession, self)._callMethod(name, args,
@ -103,6 +112,12 @@ class RecordingClientSession(BaseFakeClientSession):
def get_calls(self):
return self._calldata
def dump(self, filename):
with open(filename, 'wt') as fp:
# json.dump(self._calldata, fp, indent=4, sort_keys=True)
json.dump(self._calldata, fp, indent=4, sort_keys=True, default=encode_data)
self._calldata = []
def _callMethod(self, name, args, kwargs=None, retry=True):
if self.multicall:
return super(RecordingClientSession, self)._callMethod(name, args,
@ -128,3 +143,24 @@ class RecordingClientSession(BaseFakeClientSession):
'faultString': str(e)}
call['fault'] = err
raise
def encode_data(value):
"""Encode data for json"""
if isinstance(value, DateTime):
return {'__type': 'DateTime', 'value': value.value}
else:
raise TypeError('Unknown type for json encoding')
def decode_data(value):
"""Decode data encoded for json"""
if isinstance(value, dict):
_type = value.get('__type')
if _type == 'DateTime':
return DateTime(value['value'])
#else
return value
# the end

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,207 @@
from __future__ import absolute_import
import inspect
import json
try:
from unittest import mock
except ImportError:
import mock
import os
import six
import sys
import unittest
from six.moves import StringIO
import koji
from koji_cli.lib import watch_tasks
from koji_cli.commands import anon_handle_watch_task
from koji.util import dslice
from ..test_cli.fakeclient import FakeClientSession, RecordingClientSession, encode_data
from .loadwebindex import webidx
from kojiweb.util import FieldStorageCompat
class TestPages(unittest.TestCase):
@classmethod
def setUpClass(cls):
# recording session used across tests in recording mode
cls.cfile = os.path.dirname(__file__) + f'/data/pages_calls.json'
cls.recording = False
cls.rsession = RecordingClientSession('http://localhost/kojihub', {})
@classmethod
def tearDownClass(cls):
if cls.recording:
# save recorded calls
cls.rsession.dump(cls.cfile)
def setUp(self):
self.environ = {
'koji.options': {
'SiteName': 'test',
'KojiFilesURL': 'http://server.local/files',
'KojiHubURL': 'http://server.local/kojihub',
'KojiGreeting': 'Welcome to Koji Web',
'LoginDisabled': True,
'Tasks': [],
'ToplevelTasks': [],
'ParentTasks': [],
'MBS_WEB_URL': None,
},
'koji.currentUser': None,
'SCRIPT_FILENAME': webidx.__file__, # close enough
'SCRIPT_NAME': '',
'SERVER_PORT': '80',
'SERVER_NAME': 'server.local',
'wsgi.url_scheme': 'http',
'koji.headers': [],
}
self.get_server = mock.patch.object(webidx, "_getServer").start()
self._assertLogin = mock.patch.object(webidx, "_assertLogin").start()
self.server = None # set up setup_server
# mock time so that call args are reproduced
self.time = mock.patch('time.time').start()
self.time.return_value = 1735707600.0
def __get_server(env):
return self.server
self.get_server.side_effect = __get_server
self.setup_server()
def setup_server(self):
if self.recording:
self.server = self.rsession
else:
self.server = FakeClientSession('SERVER', {})
self.server.load(self.cfile)
return self.server
def tearDown(self):
mock.patch.stopall()
# Show long diffs in error output...
maxDiff = None
CALLS = [
['index', ''],
['packages', ''],
['packages', 'prefix=m&order=package_name&inherited=1&blocked=1'],
['packages', 'start=50&order=package_name&inherited=1&blocked=1'],
['builds', ''],
['builds', 'start=50&order=-build_id'],
['builds', 'prefix=d&order=-build_id'],
['builds', 'state=4&prefix=d&order=-build_id'],
['builds', 'type=image&prefix=d&order=-build_id'],
['tasks', ''],
['tasks', 'state=all&view=tree&order=-id&method=all'],
['tasks', 'state=failed&view=tree&order=-id&method=all'],
['tasks', 'view=flat&state=failed&order=-id&method=all'],
['tasks', 'method=buildArch&view=flat&state=failed&order=-id'],
['tasks', 'owner=mikem&view=flat&state=failed&order=-id&method=buildArch'],
['tags', ''],
['tags', 'start=50&order=name'],
['buildtargets', ''],
['buildtargets', 'order=-id'],
['users', ''],
['users', 'prefix=m&order=name'],
['hosts', ''],
['hosts', 'ready=yes&channel=all&state=all&order=name&arch=all'],
['hosts', 'channel=appliance&ready=all&state=all&order=name&arch=all'],
['hosts', 'arch=x86_64&channel=appliance&ready=all&state=all&order=name'],
['reports', ''],
['packagesbyuser', ''],
['buildsbyuser', ''],
['rpmsbyhost', ''],
['tasksbyuser', ''],
['tasksbyhost', ''],
['buildsbystatus', ''],
['buildsbytarget', ''],
['clusterhealth', ''],
['search', ''],
['search', 'terms=k*&type=package&match=glob'],
['api', ''],
['userinfo', 'userID=1'],
['activesession', ''],
['archiveinfo', 'archiveID=202'],
['buildinfo', 'buildID=628'],
['rpminfo', 'rpmID=6608'],
['buildrootinfo', 'buildrootID=966'],
['buildinfo', 'buildID=574'],
['buildrootinfo', 'buildrootID=934'],
['repoinfo', 'repoID=2580'],
['buildroots', 'repoID=2580'],
['buildroots', 'state=3&repoID=2580&order=id'],
['buildtargetedit', 'targetID=107&a='],
['buildtargetinfo', 'targetID=107'],
['taskinfo', 'taskID=14330'],
['channelinfo', 'channelID=1'],
#['channelinfo', 'channelID=MISSING'],
['taginfo', 'tagID=798'],
['externalrepoinfo', 'extrepoID=1'],
['fileinfo', 'rpmID=6608&filename=/etc/koji.conf'],
['hostinfo', 'hostID=1'],
['hostedit', 'hostID=1&a='],
['notificationcreate', 'a='],
['notificationedit', 'notificationID=1&a='],
['packageinfo', 'packageID=306'],
['recentbuilds', ''],
['recentbuilds', 'package=1'],
['repoinfo', 'repoID=88'],
['rpminfo', 'rpmID=6608'],
['rpmlist', 'buildrootID=657&type=component'],
['taginfo', 'tagID=2'],
['tagedit', 'tagID=2&a='],
['tagparent', 'tagID=2&parentID=1&action=edit&a='],
['taginfo', 'tagID=2090'],
['userinfo', 'userID=1'],
['taskinfo', 'taskID=1'],
['archivelist', 'buildrootID=363&type=built'],
['buildinfo', 'buildID=422'],
['archiveinfo', 'archiveID=130'],
['archivelist', 'buildrootID=345&type=built'],
['buildinfo', 'buildID=612'],
['buildroots', ''],
['buildroots', 'start=50&order=id'],
#['builds', 'start=50&order=id'],
]
def prep_handler(self, method, query):
"""Takes method name and query string, returns handler and data"""
# based loosely on publisher prep_handler
self.environ['QUERY_STRING'] = query
self.environ['koji.method'] = method
self.environ['SCRIPT_NAME'] = method
handler = getattr(webidx, method)
fs = FieldStorageCompat(self.environ)
self.environ['koji.form'] = fs
# even though we have curated urls, we need to filter args for some cases, e.g. search
args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, ann = \
inspect.getfullargspec(handler)
if not varkw:
data = dslice(fs.data, args, strict=False)
else:
data = fs.data.copy()
return handler, data
def test_web_handlers(self):
"""Test a bunch of web handlers"""
for method, query in self.CALLS:
handler, data = self.prep_handler(method, query)
result = handler(self.environ, **data)
# result should be a string containing the rendered template
self.assertIsInstance(result, str)
# none of these should return the error template
self.assertNotIn(r'<h4>Error</h4>', result)
# all except recentbuilds (rss) should render the header and footer
if method != 'recentbuilds':
self.assertIn(r'<div id="header">', result)
self.assertIn(r'<div id="mainNav">', result)
self.assertIn(r'<p id="footer">', result)
# the end

View file

@ -1,4 +1,5 @@
#include "includes/header.html.j2"
# from "includes/macros.html.j2" import rowToggle
#set _PASSTHROUGH = ['userID']
@ -14,7 +15,7 @@
<th><a href="activesession?order={{ util.toggleOrder('id') }}{{ util.passthrough_except('order') }}">Logout?</a> {{ util.sortImage('id') }}</th>
</tr>
#for act in activesess
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
<td>{{ act.id }}</td>
<td>{{ act.hostip }}</td>
<td>{{ act.authtype }}</td>
@ -24,3 +25,5 @@
</tr>
#endfor
</table>
#include "includes/footer.html.j2"

View file

@ -2,6 +2,7 @@
#set _PASSTHROUGH = ['archiveID', 'fileOrder', 'fileStart', 'buildrootOrder', 'buildrootStart']
#include "includes/header.html.j2"
# from "includes/macros.html.j2" import rowToggle
<h4>Information for archive <a href="archiveinfo?archiveID={{ archive.id }}">{{ archive.filename }}</a></h4>
<table>
@ -92,7 +93,7 @@
<th><a href="archiveinfo?fileOrder={{ util.toggleOrder('size', 'fileOrder') }}{{ util.passthrough_except('fileOrder', 'fileStart') }}#filelist">Size</a> {{ util.sortImage('size', 'fileOrder') }}</th>
</tr>
#for file in files
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
<td><a href="fileinfo?archiveID={{ archive.id }}&filename={{ file.name|urlencode }}">{{ file.name }}</a></td><td><span title="{{ util.formatThousands(file.size) }}">{{ util.formatNatural(file.size) }}</span></td>
</tr>
#endfor
@ -132,7 +133,7 @@
<th><a href="archiveinfo?buildrootOrder={{ util.toggleOrder('state', 'buildrootOrder') }}{{ util.passthrough_except('buildrootOrder', 'buildrootStart') }}#buildrootlist">State</a> {{ util.sortImage('state', 'buildrootOrder') }}</th>
</tr>
#for buildroot in buildroots
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
<td><a href="buildrootinfo?buildrootID={{ buildroot.id }}">{{ util.brLabel(buildroot) }}</a></td>
<td>{{ util.formatTime(buildroot.create_event_time) }}</td>
<td>{{ util.imageTag(util.brStateName(buildroot.state)) }}</td>

View file

@ -1,5 +1,6 @@
#include "includes/header.html.j2"
# from "includes/macros.html.j2" import rowToggle
#macro getID()
#if type == 'image'
@ -50,7 +51,7 @@ buildrootID={{ buildroot.id }}
</tr>
#if (archives |length) > 0
#for archive in archives
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
<td><a href="archiveinfo?archiveID={{ archive.id }}">{{ archive.filename }}</a></td>
<td>{{ archive.type_name }}</td>
#if type == 'component'

View file

@ -18,7 +18,7 @@
<th>Release</th><td>{{ build.release }}</td>
</tr>
<tr>
<th>Epoch</th><td>{{ build.epoch }}</td>
<th>Epoch</th><td>{{ build.epoch if build.epoch is not none else '' }}</td>
</tr>
<tr>
#if build.draft

View file

@ -2,6 +2,7 @@
#set _PASSTHROUGH = ['repoID', 'order', 'state']
#include "includes/header.html.j2"
# from "includes/macros.html.j2" import rowToggle
<h4>Buildroots in repo <a href="repoinfo?repoID={{ repoID }}">{{ repoID }}</a></h4>
@ -26,7 +27,7 @@
#if (buildrootPages |length) > 1
<form class="pageJump" action="">
Page:
<select onchange="javascript: window.location = 'builds?start=' + this.value * {{ buildrootRange }} + '{{ util.passthrough_except() }}';">
<select onchange="javascript: window.location = 'buildroots?start=' + this.value * {{ buildrootRange }} + '{{ util.passthrough_except() }}';">
#for pageNum in buildrootPages
<option value="{{ pageNum }}"{{ ' selected' if pageNum == buildrootCurrentPage else '' }}>{{ pageNum + 1 }}</option>
#endfor
@ -34,13 +35,13 @@
</form>
#endif
#if buildrootStart > 0
<a href="builds?start={{ buildrootStart - buildrootRange }}{{ util.passthrough_except() }}">&lt;&lt;&lt;</a>
<a href="buildroots?start={{ buildrootStart - buildrootRange }}{{ util.passthrough_except() }}">&lt;&lt;&lt;</a>
#endif
#if totalBuildroots != 0
<strong>Buildroots {{ buildrootStart + 1 }} through {{ buildrootStart + buildrootCount }} of {{ totalBuildroots }}</strong>
#endif
#if buildrootStart + buildrootCount < totalBuildroots
<a href="builds?start={{ buildrootStart + buildrootRange }}{{ util.passthrough_except() }}">&gt;&gt;&gt;</a>
<a href="buildroots?start={{ buildrootStart + buildrootRange }}{{ util.passthrough_except() }}">&gt;&gt;&gt;</a>
#endif
</td>
</tr>
@ -53,7 +54,7 @@
</tr>
#if (buildroots |length) > 0
#for buildroot in buildroots
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
<td><a href="buildrootinfo?buildrootID={{ buildroot.id }}">{{ buildroot.id }}</a></td>
<td><a href="repoinfo?repoID={{ buildroot.repo_id }}">{{ buildroot.repo_id }}</a></td>
<td><a href="taskinfo?taskID={{ buildroot.task_id }}">{{ buildroot.task_id }}</a></td>
@ -72,7 +73,7 @@
#if (buildrootPages |length) > 1
<form class="pageJump" action="">
Page:
<select onchange="javascript: window.location = 'builds?start=' + this.value * {{ buildrootRange }} + '{{ util.passthrough_except() }}';">
<select onchange="javascript: window.location = 'buildroots?start=' + this.value * {{ buildrootRange }} + '{{ util.passthrough_except() }}';">
#for pageNum in buildrootPages
<option value="{{ pageNum }}"{{ ' selected' if pageNum == buildrootCurrentPage else '' }}>{{ pageNum + 1 }}</option>
#endfor
@ -80,13 +81,13 @@
</form>
#endif
#if buildrootStart > 0
<a href="builds?start={{ buildrootStart - buildrootRange }}{{ util.passthrough_except() }}">&lt;&lt;&lt;</a>
<a href="buildroots?start={{ buildrootStart - buildrootRange }}{{ util.passthrough_except() }}">&lt;&lt;&lt;</a>
#endif
#if totalBuildroots != 0
<strong>Buildroots {{ buildrootStart + 1 }} through {{ buildrootStart + buildrootCount }} of {{ totalBuildroots }}</strong>
#endif
#if buildrootStart + buildrootCount < totalBuildroots
<a href="builds?start={{ buildrootStart + buildrootRange }}{{ util.passthrough_except() }}">&gt;&gt;&gt;</a>
<a href="buildroots?start={{ buildrootStart + buildrootRange }}{{ util.passthrough_except() }}">&gt;&gt;&gt;</a>
#endif
</td>
</tr>

View file

@ -2,6 +2,7 @@
#set _PASSTHROUGH = ['userID', 'tagID', 'packageID', 'order', 'prefix', 'state', 'inherited', 'latest', 'type']
#include "includes/header.html.j2"
# from "includes/macros.html.j2" import rowToggle
#macro getDescription()
#if latest
@ -142,7 +143,7 @@ in tag <a href="taginfo?tagID={{ tag.id }}">{{ tag.name }}</a>
</tr>
#if (builds |length) > 0
#for build in builds
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
<td>{{ build.build_id }}</td>
<td><a href="buildinfo?buildID={{ build.build_id }}">{{ koji.buildLabel(build) }}</a></td>
#if tag

View file

@ -7,6 +7,7 @@
#endmacro
#include "includes/header.html.j2"
# from "includes/macros.html.j2" import rowToggle
<h4>Builds by Target{{ ' in the last %i days' % days if days != -1 else '' }}</h4>
<table class="data-list">
@ -59,7 +60,7 @@
</tr>
#if (targets |length) > 0
#for target in targets
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
<td><a href="buildtargetinfo?name={{ target.name|urlencode }}">{{ target.name }}</a></td>
<td width="{{ graphWidth + 5 }}"><img src={{ util.themePath('images/1px.gif') }} width="{{ increment * target.builds }}" height="15" class="graphrow" alt="graph row"/></td>
<td>{{ target.builds }}</td>

View file

@ -1,5 +1,6 @@
#include "includes/header.html.j2"
# from "includes/macros.html.j2" import rowToggle
<h4>Builds by User</h4>
<table class="data-list">
@ -33,7 +34,7 @@
</tr>
#if (userBuilds |length) > 0
#for userBuild in userBuilds
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
<td><a href="userinfo?userID={{ userBuild.id }}">{{ userBuild.name }}</a></td>
<td width="{{ graphWidth + 5 }}"><img src="{{ util.themePath('images/1px.gif') }}" width="{{ increment * userBuild.builds }}" height="15" class="graphrow" alt="graph row"/></td>
<td>{{ userBuild.builds }}</td>

View file

@ -1,5 +1,6 @@
#include "includes/header.html.j2"
# from "includes/macros.html.j2" import rowToggle
<h4>Build Targets</h4>
<table class="data-list">
@ -32,7 +33,7 @@
</tr>
#if (targets |length) > 0
#for target in targets
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
<td>{{ target.id }}</td>
<td><a href="buildtargetinfo?targetID={{ target.id }}">{{ target.name }}</a></td>
</tr>

View file

@ -1,5 +1,6 @@
#include "includes/header.html.j2"
# from "includes/macros.html.j2" import rowToggle
<h4>Information for channel <a href="channelinfo?channelID={{ channel.id }}">{{ channel.name }}</a></h4>
@ -11,7 +12,7 @@
<th>ID</th><td>{{ channel.id }}</td>
</tr>
<tr>
<th>Description</th><td>{{ channel.description }}</td>
<th>Description</th><td>{{ channel.description or '' }}</td>
</tr>
<tr>
#set enabled = channel.enabled and 'yes' or 'no'
@ -21,7 +22,7 @@
</td>
</tr>
<tr>
<th>Comment</th><td>{{ channel.comment }}</td>
<th>Comment</th><td>{{ channel.comment or '' }}</td>
</tr>
<tr>
<th>Active Tasks</th><td><a href="tasks?view=flat&channelID={{ channel.id }}">{{ taskCount }}</a></td>
@ -37,7 +38,7 @@
<th>Ready</th>
</tr>
#for host in hosts
<tr class="{{ util.rowToggle }}({{ self }})">
<tr class="{{ rowToggle(loop) }}">
<td><a href="hostinfo?hostID={{ host.id }}">{{ host.name }}</a></td>
<td class="{{ host.enabled |string |lower }}">{{ util.imageTag('yes') if host.enabled else util.imageTag('no') }}</td>
<td class="{{ host.ready |string |lower }}">{{ util.imageTag('yes') if host.ready else util.imageTag('no') }}</td>

View file

@ -49,7 +49,7 @@
</tr>
<tr>
#set epoch = (rpm.epoch != None and str(rpm.epoch) + ':' or '')
<th>RPM</th><td><a href="rpminfo?rpmID={{ rpm.id }}">{{ rpm.name }}-{{ epoch }}{{ rpm.version }}-{{ rpm.release }}{{ rpm.arch }}.rpm</a></td>
<th>RPM</th><td><a href="rpminfo?rpmID={{ rpm.id }}">{{ rpm.name }}-{{ epoch }}{{ rpm.version }}-{{ rpm.release }}.{{ rpm.arch }}.rpm</a></td>
</tr>
#elif archive
<tr>

View file

@ -27,15 +27,15 @@
</tr>
<tr>
<th>Description</th>
<td><textarea name="description" rows="6" cols="50">{{ host.description }}</textarea></td>
<td><textarea name="description" rows="6" cols="50">{{ host.description or '' }}</textarea></td>
</tr>
<tr>
<th>Comment</th>
<td><textarea name="comment" rows="2" cols="50">{{ host.comment }}</textarea></td>
<td><textarea name="comment" rows="2" cols="50">{{ host.comment or '' }}</textarea></td>
</tr>
<tr>
<th>Enabled?</th>
<td><input type="checkbox" name="enabled" value="yes" {{ 'checked="checked"' if host.enabled else '' }}/>
<td><input type="checkbox" name="enabled" value="yes"{{ ' checked' if host.enabled else '' }}/>
</tr>
<tr>
<th>Channels</th>

View file

@ -1,5 +1,6 @@
#include "includes/header.html.j2"
# from "includes/macros.html.j2" import rowToggle
<h4>Information for host <a href="hostinfo?hostID={{ host.id }}">{{ host.name }}</a></h4>
@ -20,10 +21,10 @@
<th>Task Load</th><td><a href="tasks?hostID={{ host.id }}">{{ '%.2f' % host.task_load }}</a></td>
</tr>
<tr>
<th>Description</th><td class="usertext">{{ host.description }}</td>
<th>Description</th><td class="usertext">{{ host.description or '' }}</td>
</tr>
<tr>
<th>Comment</th><td class="usertext">{{ host.comment }}</td>
<th>Comment</th><td class="usertext">{{ host.comment or '' }}</td>
</tr>
<tr>
#set enabled = host.enabled and 'yes' or 'no'
@ -66,7 +67,7 @@
<th>Buildroot</th><th>Created</th><th>State</th>
</tr>
#for buildroot in buildroots
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
<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>

View file

@ -36,6 +36,7 @@ in {{ channel }} channel
#set _PASSTHROUGH = ['state', 'order', 'ready', 'channel', 'arch']
#include "includes/header.html.j2"
# from "includes/macros.html.j2" import rowToggle
<h4>{{ headerState(state) }} {{ headerReady(ready) }} {{ headerArch(arch) }} {{ headerChannel(channel) }}</h4>
<table class="data-list">
@ -119,7 +120,7 @@ in {{ channel }} channel
</tr>
#if (hosts |length) > 0
#for host in hosts
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
<td>{{ host.id }}</td>
<td><a href="hostinfo?hostID={{ host.id }}">{{ host.name }}</a></td>
<td>{{ host.arches }}</td>

View file

@ -23,19 +23,19 @@
<form action="search" id="headerSearch">
<input type="hidden" name="match" value="glob"/>
<select name="type">
<option {{ toggleSelected("", type, "package") }} value="package">Packages</option>
<option {{ toggleSelected("", type, "build") }} value="build">Builds</option>
<option {{ toggleSelected("", type, "tag") }} value="tag">Tags</option>
<option {{ toggleSelected("", type, "target") }} value="target">Build Targets</option>
<option {{ toggleSelected("", type, "user") }} value="user">Users</option>
<option {{ toggleSelected("", type, "host") }} value="host">Hosts</option>
<option {{ toggleSelected("", type, "rpm") }} value="rpm">RPMs</option>
{% if mavenEnabled %}
<option {{ toggleSelected("", type, "maven") }} value="maven">Maven Artifacts</option>
{% endif %}
{% if winEnabled %}
<option {{ toggleSelected("", type, "win") }} value="win">Windows Artifacts</option>
{% endif %}
<option {{ toggleSelected(type, "package") }} value="package">Packages</option>
<option {{ toggleSelected(type, "build") }} value="build">Builds</option>
<option {{ toggleSelected(type, "tag") }} value="tag">Tags</option>
<option {{ toggleSelected(type, "target") }} value="target">Build Targets</option>
<option {{ toggleSelected(type, "user") }} value="user">Users</option>
<option {{ toggleSelected(type, "host") }} value="host">Hosts</option>
<option {{ toggleSelected(type, "rpm") }} value="rpm">RPMs</option>
{%- if mavenEnabled %}
<option {{ toggleSelected(type, "maven") }} value="maven">Maven Artifacts</option>
{%- endif %}
{%- if winEnabled %}
<option {{ toggleSelected(type, "win") }} value="win">Windows Artifacts</option>
{%- endif %}
</select>
<input type="text" name="terms" title="You can use glob expressions here (e.g. 'bash-*')" value="{{ terms }}"/>
<input type="submit" value="Search"/>
@ -62,14 +62,15 @@
<span id="loginInfo">
{{ date_str }}
{% if not LoginDisabled %}
{%- if not LoginDisabled %}
|
{% if currentUser %}
{%- if currentUser %}
{{ greeting }}, <a href="userinfo?userID={{ currentUser.id }}">{{ currentUser.name }}</a> | <a href="logout">logout</a>
{% else %}
{%- else %}
<a href="login">login</a>
{% endif %}
{% endif %}
{%- endif %}
{%- endif %}
</span>
<div id="content">

View file

@ -0,0 +1,11 @@
{%- set TOGGLE = cycler('row-odd', 'row-even') %}
{%- macro rowToggle(loop=None) %}
{%- if loop is not none %}
{{- loop.cycle('row-odd', 'row-even') }}
{%- else %}
{# use the global one #}
{{- TOGGLE.next() }}
{%- endif %}
{%- endmacro %}

View file

@ -1,5 +1,6 @@
#include "includes/header.html.j2"
# from "includes/macros.html.j2" import rowToggle
<div class="pageHeader">{{ welcomeMessage }}</div>
@ -15,7 +16,7 @@
<th>State</th>
</tr>
#for build in builds
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
#set stateName = util.stateName(build.state)
<td>{{ build.build_id }}</td>
<td><a href="buildinfo?buildID={{ build.build_id }}">{{ build.nvr }}</a></td>
@ -49,7 +50,7 @@
</tr>
#for task in tasks
#set scratch = util.taskScratchClass(task)
<tr class="{{ util.rowToggle() }} {{ scratch }}">
<tr class="{{ rowToggle(loop) }} {{ scratch }}">
#set state = util.taskState(task.state)
<td>{{ task.id }}</td>
<td><a href="taskinfo?taskID={{ task.id }}" class="task{{ state }}" title="{{ state }}">{{ koji.taskLabel(task) }}</a></td>
@ -108,7 +109,7 @@
<th><a href="index?packageOrder={{ util.toggleOrder('blocked', 'packageOrder') }}{{ util.passthrough('buildOrder', 'buildStart', 'taskOrder', 'taskStart') }}#packagelist">Included?</a> {{ util.sortImage('blocked', 'packageOrder') }}</th>
</tr>
#for package in packages
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
<td><a href="packageinfo?packageID={{ package.package_id }}">{{ package.package_name }}</a></td>
<td><a href="taginfo?tagID={{ package.tag_id }}">{{ package.tag_name }}</a></td>
#set included = package.blocked and 'no' or 'yes'
@ -137,7 +138,7 @@
<th></th>
</tr>
#for notif in notifs
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
<td>{{ notif.package.name if notif.package else 'all' }}</td>
<td>{{ notif.tag.name if notif.tag else 'all' }}</td>
<td>{{ 'success only' if notif.success_only else 'all' }}</td>

View file

@ -386,7 +386,7 @@ def index(environ, packageOrder='package_name', packageStart=None):
values['koji'] = koji
return _genHTML(environ, 'index.html.j2', jinja=True)
return _genHTML(environ, 'index.html.j2')
def notificationedit(environ, notificationID):
@ -432,7 +432,7 @@ def notificationedit(environ, notificationID):
tags = server.listTags(queryOpts={'order': 'name'})
values['tags'] = tags
return _genHTML(environ, 'notificationedit.html.j2', jinja=True)
return _genHTML(environ, 'notificationedit.html.j2')
def notificationcreate(environ):
@ -477,7 +477,7 @@ def notificationcreate(environ):
tags = server.listTags(queryOpts={'order': 'name'})
values['tags'] = tags
return _genHTML(environ, 'notificationedit.html.j2', jinja=True)
return _genHTML(environ, 'notificationedit.html.j2')
def notificationdelete(environ, notificationID):
@ -640,7 +640,7 @@ def tasks(environ, owner=None, state='active', view='tree', method='all', hostID
values['S'] = SafeValue
values['koji'] = koji
return _genHTML(environ, 'tasks.html.j2', jinja=True)
return _genHTML(environ, 'tasks.html.j2')
def taskinfo(environ, taskID):
@ -800,7 +800,7 @@ def taskinfo(environ, taskID):
values['koji'] = koji
values['S'] = SafeValue
return _genHTML(environ, 'taskinfo.html.j2', jinja=True)
return _genHTML(environ, 'taskinfo.html.j2')
def taskstatus(environ, taskID):
@ -937,7 +937,7 @@ def tags(environ, start=None, order=None, childID=None):
else:
values['childID'] = int(childID)
return _genHTML(environ, 'tags.html.j2', jinja=True)
return _genHTML(environ, 'tags.html.j2')
_PREFIX_CHARS = [chr(char) for char in list(range(48, 58)) + list(range(97, 123))]
@ -980,7 +980,7 @@ def packages(environ, tagID=None, userID=None, order='package_name', start=None,
values['chars'] = _PREFIX_CHARS
return _genHTML(environ, 'packages.html.j2', jinja=True)
return _genHTML(environ, 'packages.html.j2')
def packageinfo(environ, packageID, tagOrder='name', tagStart=None, buildOrder='-completion_time',
@ -1004,7 +1004,7 @@ def packageinfo(environ, packageID, tagOrder='name', tagStart=None, buildOrder='
start=buildStart, dataName='builds', prefix='build',
order=buildOrder)
return _genHTML(environ, 'packageinfo.html.j2', jinja=True)
return _genHTML(environ, 'packageinfo.html.j2')
def taginfo(environ, tagID, all='0', packageOrder='package_name', packageStart=None,
@ -1020,7 +1020,7 @@ def taginfo(environ, tagID, all='0', packageOrder='package_name', packageStart=N
values['tagID'] = tag['id']
if 'revoke_event' in tag:
values['delete_ts'] = server.getEvent(tag['revoke_event'])['ts']
return _genHTML(environ, 'taginfo_deleted.html.j2', jinja=True)
return _genHTML(environ, 'taginfo_deleted.html.j2')
all = int(all)
@ -1068,7 +1068,7 @@ def taginfo(environ, tagID, all='0', packageOrder='package_name', packageStart=N
allPerms = dict([(perm['id'], perm['name']) for perm in permList])
values['allPerms'] = allPerms
return _genHTML(environ, 'taginfo.html.j2', jinja=True)
return _genHTML(environ, 'taginfo.html.j2')
def tagcreate(environ):
@ -1104,7 +1104,7 @@ def tagcreate(environ):
values['tag'] = None
values['permissions'] = server.getAllPerms()
return _genHTML(environ, 'tagedit.html.j2', jinja=True)
return _genHTML(environ, 'tagedit.html.j2')
def tagedit(environ, tagID):
@ -1134,7 +1134,7 @@ def tagedit(environ, tagID):
params['maven_support'] = bool('maven_support' in form)
params['maven_include_all'] = bool('maven_include_all' in form)
server.editTag(tag['id'], **params)
server.editTag2(tag['id'], **params)
_redirect(environ, 'taginfo?tagID=%i' % tag['id'])
elif 'cancel' in form:
@ -1147,7 +1147,7 @@ def tagedit(environ, tagID):
values['tag'] = tag
values['permissions'] = server.getAllPerms()
return _genHTML(environ, 'tagedit.html.j2', jinja=True)
return _genHTML(environ, 'tagedit.html.j2')
def tagdelete(environ, tagID):
@ -1213,7 +1213,7 @@ def tagparent(environ, tagID, parentID, action):
'tag %i has tag %i listed as a parent more than once' %
(tag['id'], parent['id']))
return _genHTML(environ, 'tagparent.html.j2', jinja=True)
return _genHTML(environ, 'tagparent.html.j2')
elif action == 'remove':
data = server.getInheritanceData(tag['id'])
for datum in data:
@ -1242,7 +1242,7 @@ def externalrepoinfo(environ, extrepoID):
values['extRepo'] = extRepo
values['repoTags'] = repoTags
return _genHTML(environ, 'externalrepoinfo.html.j2', jinja=True)
return _genHTML(environ, 'externalrepoinfo.html.j2')
def buildinfo(environ, buildID):
@ -1390,7 +1390,7 @@ def buildinfo(environ, buildID):
values['pathinfo'] = pathinfo
values['koji'] = koji
return _genHTML(environ, 'buildinfo.html.j2', jinja=True)
return _genHTML(environ, 'buildinfo.html.j2')
def builds(environ, userID=None, tagID=None, packageID=None, state=None, order='-build_id',
@ -1478,7 +1478,7 @@ def builds(environ, userID=None, tagID=None, packageID=None, state=None, order='
values['chars'] = _PREFIX_CHARS
values['koji'] = koji
return _genHTML(environ, 'builds.html.j2', jinja=True)
return _genHTML(environ, 'builds.html.j2')
def users(environ, order='name', start=None, prefix=None):
@ -1498,7 +1498,7 @@ def users(environ, order='name', start=None, prefix=None):
values['chars'] = _PREFIX_CHARS
return _genHTML(environ, 'users.html.j2', jinja=True)
return _genHTML(environ, 'users.html.j2')
def userinfo(environ, userID, packageOrder='package_name', packageStart=None,
@ -1526,7 +1526,7 @@ def userinfo(environ, userID, packageOrder='package_name', packageStart=None,
start=buildStart, dataName='builds', prefix='build',
order=buildOrder, pageSize=10)
return _genHTML(environ, 'userinfo.html.j2', jinja=True)
return _genHTML(environ, 'userinfo.html.j2')
# headers shown in rpminfo and buildinfo pages
@ -1589,7 +1589,7 @@ def rpminfo(environ, rpmID, fileOrder='name', fileStart=None, buildrootOrder='-i
values['koji'] = koji
values['time'] = time # TODO rework template so it doesn't need this
return _genHTML(environ, 'rpminfo.html.j2', jinja=True)
return _genHTML(environ, 'rpminfo.html.j2')
def archiveinfo(environ, archiveID, fileOrder='name', fileStart=None, buildrootOrder='-id',
@ -1635,7 +1635,7 @@ def archiveinfo(environ, archiveID, fileOrder='name', fileStart=None, buildrootO
values['koji'] = koji
return _genHTML(environ, 'archiveinfo.html.j2', jinja=True)
return _genHTML(environ, 'archiveinfo.html.j2')
def fileinfo(environ, filename, rpmID=None, archiveID=None):
@ -1670,7 +1670,7 @@ def fileinfo(environ, filename, rpmID=None, archiveID=None):
values['file'] = file
return _genHTML(environ, 'fileinfo.html.j2', jinja=True)
return _genHTML(environ, 'fileinfo.html.j2')
def cancelbuild(environ, buildID):
@ -1752,9 +1752,9 @@ def hosts(environ, state='enabled', start=None, order='name', ready='all', chann
# Paginate after retrieving last update info so we can sort on it
kojiweb.util.paginateList(values, hosts, start, 'hosts', 'host', order)
values['zip'] = zip # TODO FIXME
values['zip'] = zip # TODO avoid passing this in
return _genHTML(environ, 'hosts.html.j2', jinja=True)
return _genHTML(environ, 'hosts.html.j2')
def hostinfo(environ, hostID=None, userID=None):
@ -1800,7 +1800,7 @@ def hostinfo(environ, hostID=None, userID=None):
else:
values['perms'] = []
return _genHTML(environ, 'hostinfo.html.j2', jinja=2)
return _genHTML(environ, 'hostinfo.html.j2')
def hostedit(environ, hostID):
@ -1850,7 +1850,7 @@ def hostedit(environ, hostID):
values['allChannels'] = allChannels
values['hostChannels'] = server.listChannels(hostID=host['id'])
return _genHTML(environ, 'hostedit.html.j2', jinja=True)
return _genHTML(environ, 'hostedit.html.j2')
def disablehost(environ, hostID):
@ -1900,7 +1900,7 @@ def channelinfo(environ, channelID):
values['enabled_hosts'] = len([h for h in hosts if h['enabled']])
values['ready_hosts'] = len([h for h in hosts if h['ready']])
return _genHTML(environ, 'channelinfo.html.j2', jinja=True)
return _genHTML(environ, 'channelinfo.html.j2')
def buildrootinfo(environ, buildrootID):
@ -1925,7 +1925,7 @@ def buildrootinfo(environ, buildrootID):
values['buildroot'] = buildroot
values['koji'] = koji
return _genHTML(environ, template, jinja=True)
return _genHTML(environ, template)
def rpmlist(environ, type, buildrootID=None, imageID=None, start=None, order='nvr'):
@ -1980,7 +1980,7 @@ def rpmlist(environ, type, buildrootID=None, imageID=None, start=None, order='nv
values['type'] = type
values['order'] = order
return _genHTML(environ, 'rpmlist.html.j2', jinja=True)
return _genHTML(environ, 'rpmlist.html.j2')
def archivelist(environ, type, buildrootID=None, imageID=None, start=None, order='filename'):
@ -2027,7 +2027,7 @@ def archivelist(environ, type, buildrootID=None, imageID=None, start=None, order
values['type'] = type
values['order'] = order
return _genHTML(environ, 'archivelist.html.j2', jinja=True)
return _genHTML(environ, 'archivelist.html.j2')
def buildtargets(environ, start=None, order='name'):
@ -2043,7 +2043,7 @@ def buildtargets(environ, start=None, order='name'):
else:
values['perms'] = []
return _genHTML(environ, 'buildtargets.html.j2', jinja=True)
return _genHTML(environ, 'buildtargets.html.j2')
def buildtargetinfo(environ, targetID=None, name=None):
@ -2073,7 +2073,7 @@ def buildtargetinfo(environ, targetID=None, name=None):
else:
values['perms'] = []
return _genHTML(environ, 'buildtargetinfo.html.j2', jinja=True)
return _genHTML(environ, 'buildtargetinfo.html.j2')
def buildtargetedit(environ, targetID):
@ -2113,7 +2113,7 @@ def buildtargetedit(environ, targetID):
values['target'] = target
values['tags'] = tags
return _genHTML(environ, 'buildtargetedit.html.j2', jinja=True)
return _genHTML(environ, 'buildtargetedit.html.j2')
def buildtargetcreate(environ):
@ -2173,7 +2173,7 @@ def reports(environ):
values['loggedInUser'] = True
else:
values['loggedInUser'] = False
return _genHTML(environ, 'reports.html.j2', jinja=True)
return _genHTML(environ, 'reports.html.j2')
def buildsbyuser(environ, start=None, order='-builds'):
@ -2201,7 +2201,7 @@ def buildsbyuser(environ, start=None, order='-builds'):
values['increment'] = graphWidth / maxBuilds
kojiweb.util.paginateList(values, users, start, 'userBuilds', 'userBuild', order)
return _genHTML(environ, 'buildsbyuser.html.j2', jinja=True)
return _genHTML(environ, 'buildsbyuser.html.j2')
def rpmsbyhost(environ, start=None, order=None, hostArch=None, rpmArch=None):
@ -2246,7 +2246,7 @@ def rpmsbyhost(environ, start=None, order=None, hostArch=None, rpmArch=None):
values['increment'] = graphWidth / maxRPMs
kojiweb.util.paginateList(values, hosts, start, 'hosts', 'host', order)
return _genHTML(environ, 'rpmsbyhost.html.j2', jinja=True)
return _genHTML(environ, 'rpmsbyhost.html.j2')
def packagesbyuser(environ, start=None, order=None):
@ -2276,7 +2276,7 @@ def packagesbyuser(environ, start=None, order=None):
values['increment'] = graphWidth / maxPackages
kojiweb.util.paginateList(values, users, start, 'users', 'user', order)
return _genHTML(environ, 'packagesbyuser.html.j2', jinja=True)
return _genHTML(environ, 'packagesbyuser.html.j2')
def tasksbyhost(environ, start=None, order='-tasks', hostArch=None):
@ -2314,7 +2314,7 @@ def tasksbyhost(environ, start=None, order='-tasks', hostArch=None):
values['increment'] = graphWidth / maxTasks
kojiweb.util.paginateList(values, hosts, start, 'hosts', 'host', order)
return _genHTML(environ, 'tasksbyhost.html.j2', jinja=True)
return _genHTML(environ, 'tasksbyhost.html.j2')
def tasksbyuser(environ, start=None, order='-tasks'):
@ -2343,7 +2343,7 @@ def tasksbyuser(environ, start=None, order='-tasks'):
values['increment'] = graphWidth / maxTasks
kojiweb.util.paginateList(values, users, start, 'users', 'user', order)
return _genHTML(environ, 'tasksbyuser.html.j2', jinja=True)
return _genHTML(environ, 'tasksbyuser.html.j2')
def buildsbystatus(environ, days='7'):
@ -2382,7 +2382,7 @@ def buildsbystatus(environ, days='7'):
values['maxBuilds'] = maxBuilds
values['increment'] = graphWidth / maxBuilds
return _genHTML(environ, 'buildsbystatus.html.j2', jinja=True)
return _genHTML(environ, 'buildsbystatus.html.j2')
def buildsbytarget(environ, days='7', start=None, order='-builds'):
@ -2422,7 +2422,7 @@ def buildsbytarget(environ, days='7', start=None, order='-builds'):
values['maxBuilds'] = maxBuilds
values['increment'] = graphWidth / maxBuilds
return _genHTML(environ, 'buildsbytarget.html.j2', jinja=True)
return _genHTML(environ, 'buildsbytarget.html.j2')
def _filter_hosts_by_arch(hosts, arch):
@ -2482,7 +2482,7 @@ def clusterhealth(environ, arch='__all__'):
values['arches'] = sorted(arches)
values['graphWidth'] = graphWidth
values['channels'] = sorted(channels, key=lambda x: x['name'])
return _genHTML(environ, 'clusterhealth.html.j2', jinja=True)
return _genHTML(environ, 'clusterhealth.html.j2')
def recentbuilds(environ, user=None, tag=None, package=None):
@ -2553,7 +2553,7 @@ def recentbuilds(environ, user=None, tag=None, package=None):
values['koji'] = koji
environ['koji.headers'].append(['Content-Type', 'text/xml'])
return _genHTML(environ, 'recentbuilds.html.j2', jinja=True)
return _genHTML(environ, 'recentbuilds.html.j2')
_infoURLs = {'package': 'packageinfo?packageID=%(id)i',
@ -2630,9 +2630,9 @@ def search(environ, start=None, order=None):
else:
typeLabel = '%ss' % type
values['typeLabel'] = typeLabel
return _genHTML(environ, 'search.html.j2', jinja=True)
return _genHTML(environ, 'search.html.j2')
else:
return _genHTML(environ, 'search.html.j2', jinja=True)
return _genHTML(environ, 'search.html.j2')
def api(environ):
@ -2647,7 +2647,7 @@ def api(environ):
except koji.GenericError:
values['koji_version'] = "Can't determine (older then 1.23)"
return _genHTML(environ, 'api.html.j2', jinja=True)
return _genHTML(environ, 'api.html.j2')
def watchlogs(environ, taskID):
@ -2700,7 +2700,7 @@ def repoinfo(environ, repoID):
values['numBuildroots'] = num_buildroots
values['state_name'] = kojiweb.util.repoState(repo_info['state'])
values['create_time'] = kojiweb.util.formatTimeLong(repo_info['create_ts'])
return _genHTML(environ, 'repoinfo.html.j2', jinja=True)
return _genHTML(environ, 'repoinfo.html.j2')
def activesession(environ, start=None, order=None):
@ -2718,10 +2718,12 @@ def activesession(environ, start=None, order=None):
for a in activesess:
a['lengthSession'] = kojiweb.util.formatTimestampDifference(
a['start_time'], current_timestamp, in_days=True)
else:
activesess = []
kojiweb.util.paginateList(values, activesess, start, 'activesess', order=order)
return _genHTML(environ, 'activesession.html.j2', jinja=True)
return _genHTML(environ, 'activesession.html.j2')
def activesessiondelete(environ, sessionID):
@ -2752,4 +2754,4 @@ def buildroots(environ, repoID=None, order='id', start=None, state=None):
values['koji'] = koji
return _genHTML(environ, 'buildroots.html.j2', jinja=True)
return _genHTML(environ, 'buildroots.html.j2')

View file

@ -37,7 +37,7 @@
</tr>
<tr>
<th>Success Only?</th>
<td><input type="checkbox" name="success_only" value="yes"{{ ' checked="checked"' if notif and notif.success_only else '' }}/></td>
<td><input type="checkbox" name="success_only" value="yes"{{ ' checked' if notif and notif.success_only else '' }}/></td>
</tr>
<tr>
<td>

View file

@ -1,5 +1,6 @@
#include "includes/header.html.j2"
# from "includes/macros.html.j2" import rowToggle
<h4>Information for package <a href="packageinfo?packageID={{ package.id }}">{{ package.name }}</a></h4>
@ -44,7 +45,7 @@
<th><a href="packageinfo?buildOrder={{ util.toggleOrder('state', 'buildOrder') }}{{ util.passthrough('packageID', 'tagOrder', 'tagStart') }}#buildlist">State</a> {{ util.sortImage('state', 'buildOrder') }}</th>
</tr>
#for build in builds
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
<td><a href="buildinfo?buildID={{ build.build_id }}">{{ build.nvr }}</a></td>
<td class="user-{{ build.owner_name }}"><a href="userinfo?userID={{ build.owner_id }}">{{ build.owner_name }}</a></td>
<td>{{ util.formatTime(build.completion_ts) }}</td>
@ -99,12 +100,12 @@
<th><a href="packageinfo?tagOrder={{ util.toggleOrder('extra_arches', 'tagOrder') }}{{ util.passthrough('packageID', 'buildOrder', 'buildStart') }}#taglist">Extra Arches</a> {{ util.sortImage('extra_arches', 'tagOrder') }}</th>
</tr>
#for tag in tags
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
<td><a href="taginfo?tagID={{ tag.id }}">{{ tag.name }}</a></td>
<td><a href="userinfo?userID={{ tag.owner_id }}">{{ tag.owner_name }}</a></td>
#set included = tag.blocked and 'no' or 'yes'
<td>{{ util.imageTag(included) }}</td>
<td>{{ tag.extra_arches }}</td>
<td>{{ tag.extra_arches or '' }}</td>
</tr>
#endfor
</table>

View file

@ -2,6 +2,7 @@
#set _PASSTHROUGH = ['userID', 'tagID', 'order', 'prefix', 'inherited', 'blocked']
#include "includes/header.html.j2"
# from "includes/macros.html.j2" import rowToggle
#macro getDescription()
Packages
@ -93,7 +94,7 @@ owned by <a href="userinfo?userID={{ user.id }}">{{ user.name }}</a>
</tr>
#if (packages |length) > 0
#for package in packages
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
<td>{{ package.package_id }}</td>
<td><a href="packageinfo?packageID={{ package.package_id }}">{{ package.package_name }}</a></td>
#if tag or user

View file

@ -1,5 +1,6 @@
#include "includes/header.html.j2"
# from "includes/macros.html.j2" import rowToggle
<h4>Packages by User</h4>
<table class="data-list">
@ -33,7 +34,7 @@
</tr>
#if (users |length) > 0
#for user in users
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
<td><a href="userinfo?userID={{ user.id }}">{{ user.name }}</a></td>
<td width="{{ graphWidth + 5 }}"><img src="{{ util.themePath('images/1px.gif') }}" width="{{ increment * user.packages }}" height="15" class="graphrow" alt="graph row"/></td>
<td>{{ user.packages }}</td>

View file

@ -2,13 +2,13 @@
#macro linkURL()
#set query = []
#if tag
#set _tmp = query.append('tagID=%i' % (tag.id|urlencode))
#set _tmp = query.append('tagID=%s' % (tag.id|urlencode))
#endif
#if user
#set _tmp = query.append('userID=%i' % (user.id|urlencode))
#set _tmp = query.append('userID=%s' % (user.id|urlencode))
#endif
#if package
#set _tmp = query.append('packageID=%i' % (package.id|urlencode))
#set _tmp = query.append('packageID=%s' % (package.id|urlencode))
#endif
#if query
{{ '%s/%s?%s' % (weburl, 'builds', '&amp;'.join(query)) }}

View file

@ -11,10 +11,10 @@
{% endif %}
<tr><th>State</th><td class="repo{{ state_name }}">{{ state_name }}</td></tr>
<tr><th>Event</th><td>{{ repo.create_event }} ({{ create_time }})</td></tr>
{% if state_name != 'DELETED' %}
{%- if state_name != 'deleted' %}
<tr><th>URL</th><td><a href="{{ url }}">repodata</a></td></tr>
<tr><th>Repo json</th><td><a href="{{ repo_json }}">repo.json</a></td></tr>
{% endif %}
{%- endif %}
<tr><th>Dist repo?</th><td class="{{ repo.dist | lower }}">{{ repo.dist }}</td></tr>
<tr><th>Number of buildroots: </th><td><a href="buildroots?repoID={{ repo.id }}">{{ numBuildroots }}</a></td></tr>
</table>

View file

@ -2,6 +2,7 @@
#set _PASSTHROUGH = ['rpmID', 'fileOrder', 'fileStart', 'buildrootOrder', 'buildrootStart']
#include "includes/header.html.j2"
# from "includes/macros.html.j2" import rowToggle
#set epoch = (rpm.epoch|string + ':' if rpm.epoch != None else '')
<h4>Information for RPM <a href="rpminfo?rpmID={{ rpm.id }}">{{ rpm.name }}-{{ epoch }}{{ rpm.version }}-{{ rpm.release }}.{{rpm.arch}}.rpm</a></h4>
@ -32,7 +33,7 @@
<th>Release</th><td>{{ rpm.release }}</td>
</tr>
<tr>
<th>Epoch</th><td>{{ rpm.epoch }}</td>
<th>Epoch</th><td>{{ rpm.epoch if rpm.epoch is not none else '' }}</td>
</tr>
<tr>
<th>Arch</th><td>{{ rpm.arch }}</td>
@ -255,7 +256,7 @@
<th align="right"><a href="rpminfo?fileOrder={{ util.toggleOrder('size', 'fileOrder') }}{{ util.passthrough_except('fileOrder', 'fileStart') }}#filelist">Size</a> {{ util.sortImage('size', 'fileOrder') }}</th>
</tr>
#for file in files
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
<td><a href="fileinfo?rpmID={{ rpm.id }}&amp;filename={{ file.name|urlencode }}">{{ file.name }}</a></td><td align="right"><span title="{{ util.formatThousands(file.size) }}">{{ util.formatNatural(file.size) }}</span></td>
</tr>
#endfor
@ -298,7 +299,7 @@
<th><a href="rpminfo?buildrootOrder={{ util.toggleOrder('state', 'buildrootOrder') }}{{ util.passthrough_except('buildrootOrder', 'buildrootStart') }}#buildrootlist">State</a> {{ util.sortImage('state', 'buildrootOrder') }}</th>
</tr>
#for buildroot in buildroots
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
<td><a href="buildrootinfo?buildrootID={{ buildroot.id }}">{{ util.brLabel(buildroot) }}</a></td>
<td>{{ util.formatTime(buildroot.create_event_time) }}</td>
<td>{{ util.imageTag(util.brStateName(buildroot.state)) }}</td>

View file

@ -1,5 +1,6 @@
#include "includes/header.html.j2"
# from "includes/macros.html.j2" import rowToggle
#macro getID()
#if type == 'image'
@ -60,7 +61,7 @@ colspan="2"
</tr>
#if (rpms |length) > 0
#for rpm in rpms
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
#set epoch = (rpm.epoch|string + ':' if rpm.epoch != None else '')
<td>{{ util.formatRPM(rpm) }}</td>
#if type in ['component', 'image']

View file

@ -1,5 +1,6 @@
#include "includes/header.html.j2"
# from "includes/macros.html.j2" import rowToggle
<h4>{{ rpmArch + ' ' if rpmArch else '' }}RPMs by Host{{ ' (%s)' % hostArch if hostArch else '' }}</h4>
<table class="data-list">
@ -67,7 +68,7 @@
</tr>
#if (hosts |length) > 0
#for host in hosts
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
<td><a href="hostinfo?hostID={{ host.id }}">{{ host.name }}</a></td>
<td width="{{ graphWidth + 5 }}"><img src="{{ util.themePath('images/1px.gif') }}" width="{{ increment * host.rpms }}" height="15" class="graphrow" alt="graph row"/></td>
<td>{{ host.rpms }}</td>

View file

@ -1,4 +1,5 @@
{% include "includes/header.html.j2" %}
{% from 'includes/macros.html.j2' import rowToggle %}
<h4>Search</h4>
@ -19,18 +20,18 @@
<option {{ toggleSelected(type, "user") }} value="user">Users</option>
<option {{ toggleSelected(type, "host") }} value="host">Hosts</option>
<option {{ toggleSelected(type, "rpm") }} value="rpm">RPMs</option>
{% if mavenEnabled %}
{%- if mavenEnabled %}
<option {{ toggleSelected(type, "maven") }} value="maven">Maven Artifacts</option>
{% endif %}
{% if winEnabled %}
{%- endif %}
{%- if winEnabled %}
<option {{ toggleSelected(type, "win") }} value="win">Windows Artifacts</option>
{% endif %}
{%- endif %}
</select>
</td>
<td>
{% if match is not defined %}
{% set match='glob' %}
{% endif %}
{%- if match is not defined %}
{%- set match='glob' %}
{%- endif %}
<input type="radio" name="match" value="glob" {{ toggleSelected(match, "glob", True) }} id="radioglob"/><abbr title="? will match any single character, * will match any sequence of zero or more characters" id="abbrglob">glob</abbr>
<input type="radio" name="match" value="regexp" {{ toggleSelected(match, "regexp", True) }} id="radioregexp"/><abbr title="full POSIX regular expressions" id="abbrregexp">regexp</abbr>
<input type="radio" name="match" value="exact" {{ toggleSelected(match, "exact", True) }} id="radioexact"/><abbr title="exact matches only" id="abbrexact">exact</abbr>
@ -49,43 +50,43 @@
<table class="data-list">
<tr>
<td class="paginate" colspan="2">
{% if resultPages|length > 1 %}
{%- if resultPages|length > 1 %}
<form class="pageJump" action="">
Page:
<select onchange="javascript: window.location = 'search?start=' + this.value * {{ resultRange }} + '{{ passthrough('order', 'terms', 'type', 'match') }}';">
{% for pageNum in resultPages %}
<option value="{{ pageNum }}"{{ 'selected' if pageNum == resultCurrentPage else '' }}>{{ pageNum + 1 }}</option>
<option value="{{ pageNum }}"{{ ' selected' if pageNum == resultCurrentPage else '' }}>{{ pageNum + 1 }}</option>
{% endfor %}
</select>
</form>
{% endif %}
{% if resultStart > 0 %}
{%- endif %}
{%- if resultStart > 0 %}
<a href="search?start={{ resultStart - resultRange }}{{ passthrough('order', 'terms', 'type', 'match') }}">&lt;&lt;&lt;</a>
{% endif %}
{% if totalResults %}
{%- endif %}
{%- if totalResults %}
<strong>Results {{ resultStart + 1 }} through {{ resultStart + resultCount }} of {{ totalResults }}</strong>
{% endif %}
{% if resultStart + resultCount < totalResults %}
{%- endif %}
{%- if resultStart + resultCount < totalResults %}
<a href="search?start={{ resultStart + resultRange }}{{ passthrough('order', 'terms', 'type', 'match') }}">&gt;&gt;&gt;</a>
{% endif %}
{%- endif %}
</td>
</tr>
<tr class="list-header">
<th><a href="search?order={{ toggleOrder('id') }}{{ passthrough('terms', 'type', 'match') }}">ID</a> {{ sortImage('id') }}</th>
<th><a href="search?order={{ toggleOrder('name') }}{{ passthrough('terms', 'type', 'match') }}">Name</a> {{ sortImage('name') }}</th>
</tr>
{% if results %}
{% for result in results %}
<tr class="{{ rowToggle() }}">
{%- if results %}
{%- for result in results %}
<tr class="{{ rowToggle(loop) }}">
<td>{{ result.id }}</td>
<td><a href="{{ infoURL % result }}">{{ result.name }}</a></td>
</tr>
{% endfor %}
{% else %}
{%- endfor %}
{%- else %}
<tr class="row-odd">
<td colspan="2">No search results</td>
</tr>
{% endif %}
{%- endif %}
<tr>
<td class="paginate" colspan="2">
{% if resultPages|length > 1 %}

View file

@ -25,7 +25,7 @@
</tr>
<tr>
<th>Locked</th>
<td><input type="checkbox" name="locked" value="yes" {{ 'checked="checked"' if tag and tag.locked else '' }}/></td>
<td><input type="checkbox" name="locked" value="yes"{{ ' checked' if tag and tag.locked else '' }}/></td>
</tr>
<tr>
<th>Permission</th>
@ -41,11 +41,11 @@
#if mavenEnabled
<tr>
<th>Maven Support?</th>
<td><input type="checkbox" name="maven_support" value="yes" {{ 'checked="checked"' if tag and tag.maven_support else '' }}>
<td><input type="checkbox" name="maven_support" value="yes"{{ 'checked' if tag and tag.maven_support else '' }}>
</tr>
<tr>
<th>Include All Maven Builds?</th>
<td><input type="checkbox" name="maven_include_all" value="yes" {{ 'checked="checked"' if tag and tag.maven_include_all else '' }}>
<td><input type="checkbox" name="maven_include_all" value="yes"{{ ' checked' if tag and tag.maven_include_all else '' }}>
</tr>
#endif
<tr>

View file

@ -38,7 +38,7 @@
<span class="root">{{ tag.name }}</span>
#set numParents = (inheritance |length)
#set iter = 0
#set all_depths = []
#set all_depths = [0]
#set TRUNC_DEPTH = 7
<ul>
#for parent in inheritance
@ -46,7 +46,7 @@
#set nextDepth = (loop.nextitem.currdepth if not loop.last else 1)
#set depth = parent.currdepth
#set _junk = all_depths.append(depth)
## ^ TODO FIXME
## ^ TODO refactor to avoid this
#if depth == TRUNC_DEPTH and not all
<li><span class="treeBranch"><span class="treeToggle treeLabel">...</span></span></li>
<li class="hidden">
@ -56,7 +56,7 @@
<li>
#endif
#set _junk = tagsByChild[parent.child_id].pop()
## ^ TODO FIXME
## ^ TODO refactor to avoid this
<span class="treeBranch">
<span class="treeLabel">
<a href="taginfo?tagID={{ parent.parent_id }}">{{ parent.name }}</a>
@ -134,7 +134,7 @@
<td>
#if (srcTargets |length)
#for target in srcTargets
<a href="buildtargetinfo?name={{ quote(target.name) }}">{{ target.name }}</a><br/>
<a href="buildtargetinfo?name={{ target.name|urlencode }}">{{ target.name }}</a><br/>
#endfor
#else
No build targets
@ -146,7 +146,7 @@
<td>
#if (destTargets |length)
#for target in destTargets
<a href="buildtargetinfo?name={{ quote(target.name) }}">{{ target.name }}</a><br/>
<a href="buildtargetinfo?name={{ target.name|urlencode }}">{{ target.name }}</a><br/>
#endfor
#else
No build targets
@ -168,7 +168,7 @@
#for key in tag['extra']
<tr>
<th>{{ key }}</th>
<td>{{ pprint.pformat(tag.extra[key]) }}</td>
<td>{{ tag.extra[key]|pprint }}</td>
</tr>
#endfor
#endif

View file

@ -34,19 +34,19 @@
<tr>
<th>Max Depth</th>
<td>
<input type="text" name="maxdepth" value="{{ inheritanceData.maxdepth if inheritanceData else '' }}"/>
<input type="text" name="maxdepth" value="{{ inheritanceData.maxdepth if inheritanceData and inheritanceData.maxdepth is not none else '' }}"/>
</td>
</tr>
<tr>
<th>Intransitive</th>
<td>
<input type="checkbox" name="intransitive" value="yes" {{ 'checked="checked"' if inheritanceData and inheritanceData.intransitive else '' }}/>
<input type="checkbox" name="intransitive" value="yes"{{ ' checked' if inheritanceData and inheritanceData.intransitive else '' }}/>
</td>
</tr>
<tr>
<th>Packages Only</th>
<td>
<input type="checkbox" name="noconfig" value="yes" {{ 'checked="checked"' if inheritanceData and inheritanceData.noconfig else '' }}/>
<input type="checkbox" name="noconfig" value="yes"{{ ' checked' if inheritanceData and inheritanceData.noconfig else '' }}/>
</td>
</tr>
<tr>

View file

@ -1,5 +1,6 @@
#include "includes/header.html.j2"
# from "includes/macros.html.j2" import rowToggle
<h4>Tags</h4>
<table class="data-list">
@ -10,7 +11,7 @@
Page:
<select onchange="javascript: window.location = 'tags?start=' + this.value * {{ tagRange }} + '{{ passthrough('userID', 'tagID', 'order', 'childID') }}';">
#for pageNum in tagPages
<option value="{{ pageNum }}"{{ 'selected' if pageNum == tagCurrentPage else '' }}>{{ pageNum + 1 }}</option>
<option value="{{ pageNum }}"{{ ' selected' if pageNum == tagCurrentPage else '' }}>{{ pageNum + 1 }}</option>
#endfor
</select>
</form>
@ -32,7 +33,7 @@
</tr>
#if tags | length > 0
#for tag in tags
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
<td>{{ tag.id }}</td>
<td><a href="taginfo?tagID={{ tag.id }}{{ passthrough('childID') }}">{{ tag.name }}</a></td>
</tr>

View file

@ -1,12 +1,10 @@
#macro printChildren(taskID, childMap)
#set iter = 0
#set children = childMap[taskID|string]
#if children
<ul>
#for child in children
#set iter = iter + 1
#if iter < children|length
#if not loop.last
<li class="sibling">
#else
<li>
@ -60,10 +58,12 @@ None
#elif key in ('build_target', 'target_info')
<a href="buildtargetinfo?targetID={{ value['id'] }}">{{ value['name'] }}</a>
#elif key in ('repo_info', 'oldrepo', 'repo')
#if key == 'repo' and value is sequence and value is not string
#if value is mapping
<a href="repoinfo?repoID={{ value.id }}">{{ value.id }}</a> ({{ koji.formatTimeLong(value.create_ts) }})
#elif value is sequence and value is not string
{{ value|join(sep) }}
#else
<a href="repoinfo?repoID={{ value.id }}">{{ value.id }}</a> ({{ koji.formatTimeLong(value.create_ts) }})
{{ value }}
#endif
#elif key == 'task_list'
#for task in params['task_list']
@ -71,20 +71,27 @@ None
{{ printMap(task, S('&nbsp;&nbsp;')) }}
#endfor
#elif value is mapping
##{{ sep.join(['%s=%s' % ((n == '' and "''" or n), v) for n, v in value.items()])
{{ value }} TODO FIXME
{{ printSimpleMap(value, sep) }}
#elif value is sequence and value is not string
## XXX this logic doesn't quite convert
{{ value|join(sep) }}
#else
{{ value }}
#endif
#endmacro
{%- macro printSimpleMap(value, sep) %}
{%- for name, value in value|dictsort -%}
{{- '%s=%s' % ("''" if name == '' else name, value) -}}
{{- sep if not loop.last else '' -}}
{%- endfor -%}
{%- endmacro %}
#macro printProperties(props)
##{{ ', '.join([v is not None and '%s=%s' % (n, v) or n|string for n, v in props.items() ]) }}
TODO FIXME
{{ props | pprint }}
## TODO this needs refactoring, but for now we simply port the logic
{%- for name, value in props|dictsort -%}
{{- name if value is none else '%s=%s' % (name, value) -}}
{{- ', ' if not loop.last else '' -}}
{%- endfor -%}
#endmacro
@ -155,9 +162,7 @@ None
#set end_ts = task.completion_ts
#endif
#if not end_ts
##set end_ts = koji.util.parseTime(koji.util.encode_datetime(datetime.datetime.utcnow()))
#set end_ts = koji.time.time()
## XXX is this right?
#endif
<tr>
<th title="From task's creation">Total time</th>

View file

@ -1,12 +1,10 @@
#macro printChildren(taskID, childMap)
#set iter = 0
#set children = childMap[taskID|string]
#if children
<ul>
#for child in children
#set iter = iter + 1
#if iter < children|length
#if not loop.last
<li class="sibling">
#else
<li>
@ -37,6 +35,7 @@ All
#set _PASSTHROUGH = ['owner', 'state', 'view', 'method', 'hostID', 'channelID', 'order']
#include "includes/header.html.j2"
# from "includes/macros.html.j2" import rowToggle
#macro getDescription()
{{ headerPrefix(state) }}
@ -96,23 +95,15 @@ in channel <a href="channelinfo?channelID={{ channel.id }}">{{ channel.name }}</
<select name="method" class="filterlist" onchange="javascript: window.location = 'tasks?method=' + this.value + '{{ util.passthrough_except('method') }}';">
<option value="all" {{ util.toggleSelected(method, 'all') }}>all</option>
#for task_type in alltasks
#if task_type in ('maven', 'buildMaven') and not mavenEnabled
## XXX continue
#elif task_type in ('winbuild', 'vmExec') and not winEnabled
## XXX continue
#elif task_type == 'wrapperRPM' and not (mavenEnabled or winEnabled)
## XXX continue
#else
<option value="{{ task_type }}" {{ 'selected' if method == task_type else '' }}>{{ task_type }}</option>
#endif
#endfor
</select>
</td><td>
<strong>View</strong>:
</td><td>
<select name="view" class="filterlist" onchange="javascript: window.location = 'tasks?view=' + this.value + '{{ util.passthrough_except('view') }}';">
<option value="tree" {{ util.toggleSelected(view, 'tree') }} {{ 'disabled="disabled"' if not treeEnabled else '' }}>tree</option>
<option value="toplevel" {{ util.toggleSelected(view, 'toplevel') }} {{ 'disabled="disabled"' if not toplevelEnabled else '' }}>toplevel</option>
<option value="tree" {{ util.toggleSelected(view, 'tree') }}{{ ' disabled' if not treeEnabled else '' }}>tree</option>
<option value="toplevel" {{ util.toggleSelected(view, 'toplevel') }}{{ ' disabled' if not toplevelEnabled else '' }}>toplevel</option>
<option value="flat" {{ util.toggleSelected(view, 'flat') }}>flat</option>
</select>
</td></tr>
@ -165,7 +156,7 @@ in channel <a href="channelinfo?channelID={{ channel.id }}">{{ channel.name }}</
</tr>
#if tasks
#for task in tasks
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
#set taskState = util.taskState(task.state)
<td>{{ task.id }}</td>
<td{{ S(' class="tree"') if treeDisplay else '' }}>

View file

@ -1,5 +1,6 @@
#include "includes/header.html.j2"
# from "includes/macros.html.j2" import rowToggle
<h4>Tasks by Host{{ ' (%s)' % hostArch if hostArch else '' }}</h4>
<table class="data-list">
@ -50,7 +51,7 @@
</tr>
#if (hosts |length) > 0
#for host in hosts
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
<td><a href="hostinfo?hostID={{ host.id }}">{{ host.name }}</a></td>
<td width="{{ graphWidth + 5 }}"><img src="{{ util.themePath('images/1px.gif') }}" width="{{ increment * host.tasks }}" height="15" class="graphrow" alt="graph row"/></td>
<td>{{ host.tasks }}</td>

View file

@ -1,5 +1,6 @@
#include "includes/header.html.j2"
# from "includes/macros.html.j2" import rowToggle
<h4>Tasks by User</h4>
<table class="data-list">
@ -33,7 +34,7 @@
</tr>
#if (users |length) > 0
#for user in users
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
<td><a href="userinfo?userID={{ user.id }}">{{ user.name }}</a></td>
<td width="{{ graphWidth + 5 }}"><img src="{{ util.themePath('images/1px.gif') }}" width="{{ increment * user.tasks }}" height="15" class="graphrow" alt="graph row"/></td>
<td>{{ user.tasks }}</td>

View file

@ -1,5 +1,6 @@
#include "includes/header.html.j2"
# from "includes/macros.html.j2" import rowToggle
<h4>Information for user <a href="userinfo?userID={{ user.id }}">{{ user.name }}</a></h4>
@ -45,10 +46,10 @@
<th><a href="userinfo?packageOrder={{ util.toggleOrder('blocked', 'packageOrder') }}{{ util.passthrough('userID', 'buildOrder', 'buildStart') }}#packagelist">Included?</a> {{ util.sortImage('blocked', 'packageOrder') }}</th>
</tr>
#for package in packages
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
<td><a href="packageinfo?packageID={{ package.package_id }}">{{ package.package_name }}</a></td>
<td><a href="taginfo?tagID={{ package.tag_id }}">{{ package.tag_name }}</a></td>
<td class="{{ package.blocked|lower }}">{{ util.imageTag('no') if package.blocked else util.imageTag('yes') }}</td>
<td class="{{ (not package.blocked)|lower }}">{{ util.imageTag('no') if package.blocked else util.imageTag('yes') }}</td>
</tr>
#endfor
</table>
@ -89,7 +90,7 @@
<th><a href="userinfo?buildOrder={{ util.toggleOrder('state', 'buildOrder') }}{{ util.passthrough('userID', 'packageOrder', 'packageStart') }}#buildlist">State</a> {{ util.sortImage('state', 'buildOrder') }}</th>
</tr>
#for build in builds
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
#set stateName = util.stateName(build.state)
<td><a href="buildinfo?buildID={{ build.build_id }}">{{ build.nvr }}</a></td>
<td>{{ util.formatTime(build.completion_ts) }}</td>

View file

@ -1,5 +1,6 @@
#include "includes/header.html.j2"
# from "includes/macros.html.j2" import rowToggle
<h4>Users {{ ' starting with "%s"' % prefix if prefix else '' }}</h4>
<table class="data-list">
@ -27,7 +28,7 @@
Page:
<select onchange="javascript: window.location = 'users?start=' + this.value * {{ userRange }} + '{{ util.passthrough('order', 'prefix') }}';">
#for pageNum in userPages
<option value="{{ pageNum }}"{{ 'selected' if pageNum == userCurrentPage else '' }}>{{ pageNum + 1 }}</option>
<option value="{{ pageNum }}"{{ ' selected' if pageNum == userCurrentPage else '' }}>{{ pageNum + 1 }}</option>
#endfor
</select>
</form>
@ -52,7 +53,7 @@
</tr>
#if users |length > 0
#for user in users
<tr class="{{ util.rowToggle() }}">
<tr class="{{ rowToggle(loop) }}">
<td>{{ user.id }}</td>
<td><a href="userinfo?userID={{ user.name }}">{{ user.name }}</a></td>
<td><a href="packages?userID={{ user.name }}">view</a></td>
@ -72,7 +73,7 @@
Page:
<select onchange="javascript: window.location = 'users?start=' + this.value * {{ userRange }} + '{{ util.passthrough('order', 'prefix') }}';">
#for pageNum in userPages
<option value="{{ pageNum }}"{{ 'selected' if pageNum == userCurrentPage else '' }}>{{ pageNum + 1 }}</option>
<option value="{{ pageNum }}"{{ ' selected' if pageNum == userCurrentPage else '' }}>{{ pageNum + 1 }}</option>
#endfor
</select>
</form>

View file

@ -344,7 +344,7 @@ class Dispatcher(object):
environ['koji.values'].setdefault('mavenEnabled', False)
environ['koji.values'].setdefault('winEnabled', False)
# import pdb; pdb.set_trace()
result = _genHTML(environ, 'error.html.j2', jinja=True)
result = _genHTML(environ, 'error.html.j2')
result = self._tobytes(result)
headers = [
('Allow', 'GET, POST, HEAD'),

View file

@ -30,6 +30,7 @@ import urllib
from collections.abc import Mapping
from functools import wraps
from socket import error as socket_error
from threading import local
from urllib.parse import parse_qs
from xml.parsers.expat import ExpatError
@ -68,7 +69,6 @@ def _initValues(environ, title='Build System Info', pageID='summary'):
values['themePath'] = themePath
values['toggleOrder'] = toggleOrder
values['toggleSelected'] = toggleSelected
values['rowToggle'] = rowToggle
values['sortImage'] = sortImage
values['passthrough'] = passthrough
values['passthrough_except'] = passthrough_except
@ -138,10 +138,25 @@ def safe_return(func):
return _safe
TEMPLATES = {}
# threadlocal cache
JINJA_CACHE = local()
def _genHTML(environ, fileName, jinja=True):
def get_jinja_env(dirpath):
if hasattr(JINJA_CACHE, 'env'):
return JINJA_CACHE.env
# otherwise
env = jinja2.Environment(
loader=jinja2.FileSystemLoader(dirpath),
autoescape=True,
line_statement_prefix='#', # for ease of porting Cheetah templates
line_comment_prefix='##'
)
JINJA_CACHE.env = env
return env
def _genHTML(environ, fileName):
reqdir = os.path.dirname(environ['SCRIPT_FILENAME'])
if os.getcwd() != reqdir:
os.chdir(reqdir)
@ -168,18 +183,9 @@ def _genHTML(environ, fileName, jinja=True):
else:
environ['koji.values']['LoginDisabled'] = False
if jinja:
# TODO clean up and optimize
env = jinja2.Environment(
loader=jinja2.FileSystemLoader(reqdir),
autoescape=True,
line_statement_prefix='#', # for ease of porting Cheetah templates
line_comment_prefix='##'
)
template = env.get_template(fileName)
return template.render(**environ['koji.values'])
else:
raise NotImplementedError('no more Cheetah')
env = get_jinja_env(reqdir)
template = env.get_template(fileName)
return template.render(**environ['koji.values'])
def _truncTime():
@ -264,8 +270,15 @@ class FieldCompat:
def __init__(self, value):
self.value = value
# compat with jinja 2.x
try:
pass_context = jinja2.pass_context
except AttributeError:
pass_context = jinja2.contextfunction
# (all our uses are functions, not filters)
@jinja2.pass_context
@pass_context
def toggleOrder(context, sortKey, orderVar='order'):
"""Toggle order for jinja templates"""
value = context.get(orderVar)
@ -308,7 +321,7 @@ def toggleSelected(var, option, checked=False):
@safe_return
@jinja2.pass_context
@pass_context
def sortImage(context, sortKey, orderVar='order'):
"""jinja version"""
orderVal = context.get(orderVar)
@ -331,7 +344,7 @@ def _sortImage(orderVal, sortKey, orderVar):
@safe_return
@jinja2.pass_context
@pass_context
def passthrough(context, *varnames, prefix='&', invert=False):
if invert:
_PASSTHROUGH = context.get('_PASSTHROUGH', None)
@ -357,7 +370,7 @@ def _passthrough(data, prefix='&'):
The prefix value (default '&') is prepended if any values were found
"""
result = []
for var in data:
for var in sorted(data):
value = data[var]
if value is not None:
if isinstance(value, str):
@ -374,7 +387,7 @@ def _passthrough(data, prefix='&'):
return ''
@jinja2.pass_context
@pass_context
def passthrough_except(context, *exclude, prefix='&'):
"""
Construct a string suitable for use as URL
@ -387,7 +400,7 @@ def passthrough_except(context, *exclude, prefix='&'):
"""
# note that we have to pass context ourselves here
# the decorator only works when called directly from the template
return passthrough(context, *exclude, prefix='&', invert=True)
return passthrough(context, *exclude, prefix=prefix, invert=True)
def sortByKeyFuncNoneGreatest(key):
@ -719,21 +732,6 @@ def formatRPM(rpminfo, link=True):
return label
@jinja2.pass_context
def rowToggle(context):
# XXX avoid modifying context
rowNum = getattr(context, '_rowNum', 0) + 1
context._rowNum = rowNum
return _rowToggle(rowNum)
def _rowToggle(rowNum):
if rowNum % 2:
return 'row-odd'
else:
return 'row-even'
def taskScratchClass(task_object):
""" Return a css class indicating whether or not this task is a scratch
build.
@ -798,7 +796,7 @@ def escapeHTML(value):
@safe_return
@jinja2.pass_context
@pass_context
def authToken(context, first=False, form=False):
token = context.get('authToken', None)
return _authToken(token, first, form)