Using twitter bootstrap for more consistent looks for Hydra

This commit is contained in:
Rob Vermaas 2012-04-12 20:12:07 +02:00
parent ed23a6e6d3
commit 17d30cd179
23 changed files with 312 additions and 563 deletions

View file

@ -6,8 +6,9 @@ STATIC = \
FLOT = flot-0.6.zip
TABLESORTER = jquery.tablesorter.zip
JQUERY = jquery-ui-1.8.5.custom.zip
BOOTSTRAP = bootstrap.zip
ZIPS = $(FLOT) $(TABLESORTER) $(JQUERY)
ZIPS = $(FLOT) $(TABLESORTER) $(JQUERY) $(BOOTSTRAP)
EXTRA_DIST = $(TEMPLATES) $(STATIC) $(ZIPS)
@ -17,6 +18,7 @@ nobase_hydra_DATA = $(EXTRA_DIST)
all:
mkdir -p $(srcdir)/static/js
unzip -u -d $(srcdir)/static/js/jquery $(JQUERY)
unzip -u -d $(srcdir)/static $(BOOTSTRAP)
rm -rf $(srcdir)/static/js/tablesorter
unzip -u -d $(srcdir)/static/js $(TABLESORTER)
unzip -u -d $(srcdir)/static/js $(FLOT)
@ -24,3 +26,5 @@ all:
install-data-local: $(ZIPS)
mkdir -p $(hydradir)/static/js
cp -prvd $(srcdir)/static/js/* $(hydradir)/static/js
mkdir -p $(hydradir)/static/bootstrap
cp -prvd $(srcdir)/static/bootstrap/* $(hydradir)/static/bootstrap

View file

@ -3,24 +3,6 @@
<h1>Admin</h1>
<ul>
<li>[% INCLUDE maybeLink uri = c.uri_for(c.controller('Project').action_for('create')) content = "Create project" %]</li>
<li>[% INCLUDE maybeLink uri = c.uri_for(c.controller('Admin').action_for('users')) content = "Manage users" %]</li>
<li>[% INCLUDE maybeLink uri = c.uri_for(c.controller('Admin').action_for('news')) content = "Manage news" %]</li>
<li>Queue
<ul>
<li>[% INCLUDE maybeLink uri = c.uri_for(c.controller('Admin').action_for('clear_queue')) content = "Clear all non-running builds from queue." confirmmsg = "Are you sure you want to clear the queue?" %]</li>
<li>[% INCLUDE maybeLink uri = c.uri_for(c.controller('Admin').action_for('clear_queue_non_current')) content = "Clear all non-running old builds from queue." confirmmsg = "Are you sure you want to clear the queue?" %]</li>
</ul>
</li>
<li>Caching
<ul>
<li>[% INCLUDE maybeLink uri = c.uri_for(c.controller('Admin').action_for('clearfailedcache')) content = "Clear failed builds cache" confirmmsg = "Are you sure you want to clear the failed builds cache?" %]</li>
<li>[% INCLUDE maybeLink uri = c.uri_for(c.controller('Admin').action_for('clearvcscache')) content = "Clear VCS caches" confirmmsg = "Are you sure you want to clear the VCS caches?" %]</li>
</ul>
</li>
</ul>
<h2>Status</h2>
[% FOREACH m IN machines %]

View file

@ -11,7 +11,7 @@
[% BLOCK renderBuildSteps %]
<h2 id="buildsteps">[% type %] build steps</h2>
<table class="tablesorter">
<table class="tablesorter table table-striped table-condensed">
<thead>
<tr><th>Nr</th><th>What</th><th>Duration</th><th>Machine</th><th>Status</th></tr>
</thead>
@ -82,17 +82,17 @@
<p class="error">[% flashMsg %]</p>
[% END %]
<div id="generic-tabs">
<ul>
<li><a href="#tabs-summary">Summary</a></li>
<li><a href="#tabs-information">Information</a></li>
<li><a href="#tabs-buildinputs">Build Inputs</a></li>
[% IF relatedbuilds %]<li><a href="#tabs-relatedbuilds">Related builds</a></li>[% END %]
[% IF build.buildsteps %]<li><a href="#tabs-buildsteps">Build Steps</a></li>[% END %]
[% IF build.dependents %]<li><a href="#tabs-usedby">Used by</a></li>[% END%]
[% IF prevBuilds %]<li><a href="#tabs-history">History chart</a></li>[% END %]
<ul id="tab" class="nav nav-tabs">
<li><a href="#tabs-summary" data-toggle="tab">Summary</a></li>
<li><a href="#tabs-information" data-toggle="tab">Information</a></li>
<li><a href="#tabs-buildinputs" data-toggle="tab">Build Inputs</a></li>
[% IF relatedbuilds %]<li><a href="#tabs-relatedbuilds" data-toggle="tab">Related builds</a></li>[% END %]
[% IF build.buildsteps %]<li><a href="#tabs-buildsteps" data-toggle="tab">Build Steps</a></li>[% END %]
[% IF build.dependents %]<li><a href="#tabs-usedby" data-toggle="tab">Used by</a></li>[% END%]
[% IF prevBuilds %]<li><a href="#tabs-history" data-toggle="tab">History chart</a></li>[% END %]
</ul>
<div id="tabs-summary">
<div id="generic-tabs" class="tab-content">
<div id="tabs-summary" class="tab-pane active">
<table class="layoutTable">
<tr>
@ -188,7 +188,7 @@
[% IF prevSuccessfulBuild %]
<h2>Changes</h2>
<br/>
<table>
<table class="table table-striped table-condensed">
<thead>
<th>Last successful build <tt>[% INCLUDE renderDateTime timestamp = prevSuccessfulBuild.timestamp %]</tt></th>
[% IF prevSuccessfulBuild && firstBrokenBuild && firstBrokenBuild.id != build.id %]
@ -201,9 +201,9 @@
</th>
</thead>
<tr>
<td valign="center">[% INCLUDE renderBuildStatusIcon build=prevSuccessfulBuild size=32 %] [% INCLUDE renderFullBuildLink build=prevSuccessfulBuild %]</td>
[% IF prevSuccessfulBuild && firstBrokenBuild && firstBrokenBuild.id != build.id %]<td valign="center">[% INCLUDE renderBuildStatusIcon build=firstBrokenBuild size=32 %] [% INCLUDE renderFullBuildLink build=firstBrokenBuild %]</td>[% END %]
<td>[% INCLUDE renderBuildStatusIcon build=build size=32 %] [% INCLUDE renderFullBuildLink build=build %]</td>
<td valign="center">[% INCLUDE renderBuildStatusIcon build=prevSuccessfulBuild size=32 %] [% INCLUDE renderFullBuildLink build=prevSuccessfulBuild, hideProjectName=1, hideJobsetName=1 %]</td>
[% IF prevSuccessfulBuild && firstBrokenBuild && firstBrokenBuild.id != build.id %]<td valign="center">[% INCLUDE renderBuildStatusIcon build=firstBrokenBuild size=32 %] [% INCLUDE renderFullBuildLink build=firstBrokenBuild, hideProjectName=1, hideJobsetName=1 %]</td>[% END %]
<td>[% INCLUDE renderBuildStatusIcon build=build size=32 %] [% INCLUDE renderFullBuildLink build=build, hideProjectName=1, hideJobsetName=1 %]</td>
</tr>
<tr>
<td></td>
@ -226,7 +226,7 @@
<pre class="buildlog">[% HTML.escape(logtext) -%]</pre>
[% END %]
</div>
<div id="tabs-information">
<div id="tabs-information" class="tab-pane">
<h2>Information</h2>
@ -396,11 +396,11 @@
[% END %]
</table>
</div>
<div id="tabs-buildinputs">
<div id="tabs-buildinputs" class="tab-pane">
<h2>Build inputs</h2>
<table class="tablesorter">
<table class="tablesorter table table-striped table-condensed">
<thead>
<tr><th>Name</th><th>Type</th><th>Value</th><th>Revision</th><th>Store path</th></tr>
</thead>
@ -428,14 +428,14 @@
<p/>
[% IF prevBuild %]
<h3>Changes since previous build : [% INCLUDE renderFullBuildLink build=prevBuild %]</h3>
<h3>Changes since previous build : [% INCLUDE renderFullBuildLink build=prevBuild, hideProjectName=1, hideJobsetName=1 %]</h3>
[% INCLUDE renderInputDiff build2=build , build1=prevBuild %]
[% END %]
</div>
[% IF relatedbuilds %]
<div id="tabs-relatedbuilds">
<div id="tabs-relatedbuilds" class="tab-pane">
<h2>Related builds</h2>
<p>The following builds are part of the same jobset evaluation that produced this build.</p>
[% INCLUDE renderBuildList builds=relatedbuilds hideProjectName=1 hideJobsetName=1 %]
@ -443,20 +443,20 @@
[% END %]
[% IF build.buildsteps %]
<div id="tabs-buildsteps">
<div id="tabs-buildsteps" class="tab-pane">
[% INCLUDE renderBuildSteps type="All" %]
</div>
[% END %]
[% IF build.dependents %]
<div id="tabs-usedby">
<div id="tabs-usedby" class="tab-pane">
<h2>Used by</h2>
<p>The following builds have used this build as an input:</p>
<table class="tablesorter">
<table class="tablesorter table table-condensed table-striped">
<thead>
<tr><th>Build</th><th>Input name</th><th>System</th><th>Timestamp</th></tr>
</thead>
@ -474,7 +474,7 @@
</div>
[% END %]
[% IF prevBuilds %]
<div id="tabs-history">
<div id="tabs-history" class="tab-pane">
<h2>Build time history (in seconds)</h2>
<div id="placeholder" style="width:800px;height:400px;"></div>
<div id="overview" style="margin-left:50px;margin-top:20px;width:600px;height:50px"></div>
@ -629,7 +629,7 @@
</div>
<script type="text/javascript">
$("#generic-tabs").tabs();
$('#tab').tab('show');
</script>

View file

@ -34,7 +34,7 @@ install the package simply by clicking on the packages below.</p>
<p>This channel contains the following packages.</p>
<table class="tablesorter">
<table class="tablesorter table table-condensed table-striped">
<thead>
<tr>

View file

@ -21,7 +21,7 @@
<h2>Build inputs</h2>
<table class="tablesorter">
<table class="tablesorter table table-condensed table-striped">
<thead>
<tr><th>Name</th><th>Type</th><th>Value</th></tr>
</thead>

View file

@ -61,7 +61,7 @@
[%- BLOCK renderBuildListHeader -%]
<table class="buildList [% IF !unsortable %]tablesorter[% END %] [% IF !showSchedulingInfo %] clean[% END %]">
<table class="buildList table table-striped table-condensed [% IF !unsortable %]tablesorter[% END %] [% IF !showSchedulingInfo %] clean[% END %]">
<thead>
<tr>
[%- IF !hideResultInfo -%]
@ -186,6 +186,9 @@
[% IF uri %]<a [% HTML.attributes(href => uri) %][% IF confirmmsg %]onclick="javascript:return confirm('[% confirmmsg %]')"[% END %]>[% content %]</a>[% ELSE; content; END -%]
[% END -%]
[% BLOCK maybeButton -%]
[% IF uri %]<a class="btn btn-mini" [% HTML.attributes(href => uri) %][% IF confirmmsg %]onclick="javascript:return confirm('[% confirmmsg %]')"[% END %]>[% content %]</a>[% ELSE; content; END -%]
[% END -%]
[% BLOCK renderSelection %]
[% IF edit %]
@ -321,7 +324,7 @@
[% END %]
[% BLOCK renderInputDiff; %]
<table class="tablesorter clean">
<table class="table table-striped table-condensed">
[% IF !nestedDiff %]
<tr><th>Input</th><th>Changes</th></tr>
[% END %]
@ -366,7 +369,7 @@
[% END %]
[% BLOCK hydraStatus %]
<table class="tablesorter">
<table class="tablesorter table table-striped table-condensed">
<thead>
<tr><th>Machine</th><th>Job</th><th>Type</th><th>Build</th><th>Step</th><th>What</th><th>Since</th></tr>
</thead>
@ -388,8 +391,16 @@
[% BLOCK renderPager %]
<ul class="pagination">
<li><a href="[% "$baseUri?page=1" %]">First</a></li>
<li [% IF page == 1 %]class="disabled"[% END %]><a href="[% "$baseUri?page="; (page - 1) %]">Previous</a></li>
<li [% IF page * resultsPerPage < total %]class="disabled"[% END %]><a href="[% "$baseUri?page="; (page + 1) %]">Next</a></li>
<li><a href="[% "$baseUri?page="; (total - 1) div resultsPerPage + 1 %]">Last</a></li>
</ul>
<!--
<p>
[<a href="[% "$baseUri?page=1" %]">First</a>]
[]
[% IF page > 1 %]
[<a href="[% "$baseUri?page="; (page - 1) %]">Prev</a>]
[% END %]
@ -398,6 +409,7 @@
[% END %]
[<a href="[% "$baseUri?page="; (total - 1) div resultsPerPage + 1 %]">Last</a>]
</p>
-->
[% END %]

View file

@ -34,7 +34,7 @@
</tr>
</table>
<table class="tablesorter">
<table class="tablesorter table table-condensed table-striped">
<thead>
<tr>
<th></th>

View file

@ -15,7 +15,7 @@ that dont build.</p>
<h2>Evaluation errors in jobsets</h2>
<table class="buildList tablesorter">
<table class="buildList tablesorter table table-condensed table-striped">
<thead>
<tr>
<th>Name</th>
@ -41,7 +41,7 @@ that dont build.</p>
<h2>Evaluation errors in jobs</h2>
<table class="buildList tablesorter">
<table class="buildList tablesorter table table-condensed table-striped">
<thead>
<tr>
<th>Name</th>

View file

@ -2,22 +2,13 @@
[% PROCESS common.tt %]
[% hideProjectName=1 hideJobsetName=1 hideJobName=1 %]
<h1>Job <tt>[% INCLUDE renderLink
uri = c.uri_for(c.controller('Project').action_for('view'), [project.name])
title = project.name %]:[% INCLUDE renderLink
uri = c.uri_for(c.controller('Jobset').action_for('index'), [project.name, jobset.name])
title = jobset.name %]:[% job.name %]</tt></h1>
<div id="generic-tabs">
<ul>
<li><a href="#tabs-status">Status</a></li>
<li><a href="#tabs-channels">Channels</a></li>
<li><a href="#tabs-latestbuilds">Latest builds</a></li>
<!-- <li><a href="#tabs-statistics">Statistics</a></li> -->
<ul id="tab" class="nav nav-tabs">
<li><a href="#tabs-status" data-toggle="tab">Status</a></li>
<li><a href="#tabs-channels" data-toggle="tab">Channels</a></li>
<li><a href="#tabs-latestbuilds" data-toggle="tab">Latest builds</a></li>
</ul>
<div id="tabs-status">
<div id="generic-tabs" class="tab-content">
<div id="tabs-status" class="tab-pane active">
[% IF currentBuilds.size != 0 %]
<h2>Latest builds (latest evaluation)</h2>
[% INCLUDE renderBuildList builds=currentBuilds showStatusChange=0 %]
@ -31,7 +22,7 @@
[% INCLUDE renderBuildList builds=lastBuilds showStatusChange=0 %]
[% END %]
</div>
<div id="tabs-channels">
<div id="tabs-channels" class="tab-pane">
<p>This job provides the following Nix channel:</p>
<ul>
@ -42,7 +33,7 @@
</li>
</ul>
</div>
<div id="tabs-latestbuilds">
<div id="tabs-latestbuilds" class="tab-pane">
<ul>
<li><a href="[% c.uri_for('/job' project.name jobset.name job.name
'latest') %]">Latest successful build.</a></li>
@ -53,14 +44,11 @@
[% END %]
</ul>
</div>
[%#
<div id="tabs-statistics">
[% INCLUDE showBuildStats % ]
</div>
%]
</div>
<script type="text/javascript">
$("#generic-tabs").tabs();
jQuery(document).ready(function ($) {
$('#tab').tab('show');
});
</script>

View file

@ -12,7 +12,7 @@
[% INCLUDE renderPager %]
<table class="tablesorter">
<table class="tablesorter table table-condensed table-striped">
<thead>
<tr>
<th>#</th>
@ -26,17 +26,18 @@
<td><a href="[% c.uri_for(c.controller('JobsetEval').action_for('view'), [eval.id]) %]">[% eval.id %]</a>&nbsp;</td>
<td>[% INCLUDE renderDateTime timestamp = eval.timestamp %]&nbsp;</td>
<td align='right'>
[% eval.get_column('nrSucceeded') %] / [% eval.get_column('nrBuilds') %]
<span class="label label-success">[% eval.get_column('nrSucceeded') %]</span>
<span class="label label-important">[% eval.get_column('nrBuilds') - eval.get_column('nrSucceeded') - eval.get_column('nrScheduled') %]</span>
[% IF eval.get_column('nrScheduled') > 0 %]
<br />[% eval.get_column('nrScheduled') %] scheduled
<span class="label">[% eval.get_column('nrScheduled') %]</span>
[% END %]
</td>
<td align='right'>
[% diff = eval.get_column('nrSucceeded') - next.get_column('nrSucceeded');
IF diff > 0 %]
<span class='green'><strong>+[% diff %]</strong></span>
<span class='label label-success'><strong>+[% diff %]</strong></span>
[% ELSIF diff < 0 && eval.get_column('nrScheduled') == 0 %]
<span class='red'><strong>[% diff %]</strong></span>
<span class='label label-important'><strong>[% diff %]</strong></span>
[% END %]
</td>
</tr>

View file

@ -8,10 +8,6 @@
[% IF create %]
<h1>New Jobset in Project <tt>[% project.name %]</tt></h1>
[% ELSE %]
<h1>Jobset <tt>[% INCLUDE renderLink
uri = c.uri_for(c.controller('Project').action_for('view'), [project.name])
title = project.name %]:[% jobset.name %]</tt></h1>
[% END %]
@ -56,7 +52,7 @@
[% BLOCK renderInputs %]
<h3>Inputs</h3>
<table class="tablesorter">
<table class="tablesorter table table-striped table-condensed">
<thead>
<tr><th>Input name</th><th>Type</th><th>Values</th></tr>
</thead>
@ -75,20 +71,19 @@
[% END %]
<div id="generic-tabs">
<ul>
<ul id="tab" class="nav nav-tabs">
[% IF !edit -%]
<li><a href="#tabs-information">Jobset</a></li>
[% IF jobset.errormsg -%]<li><a href="#tabs-errors">Evaluation errors</a></li>[% END %]
<li><a href="#tabs-jobs">Jobs ([% activeJobs.size %])</a></li>
<li><a href="#tabs-information" data-toggle="tab">Jobset</a></li>
[% IF jobset.errormsg -%]<li><a href="#tabs-errors" data-toggle="tab">Evaluation errors</a></li>[% END %]
<li><a href="#tabs-jobs" data-toggle="tab">Jobs ([% activeJobs.size %])</a></li>
[% END %]
<li><a href="#tabs-setup">Setup</a></li>
<li><a href="#tabs-setup" data-toggle="tab">Setup</a></li>
[% IF !edit -%]
<li><a href="#tabs-channels">Channels</a></li>
<!-- <li><a href="#tabs-statistics">Statistics</a></li> -->
<li><a href="#tabs-channels" data-toggle="tab">Channels</a></li>
[% END %]
</ul>
<div id="tabs-information">
<div id="generic-tabs" class="tab-content">
<div id="tabs-information" class="tab-pane active">
[% IF jobset.errormsg && !edit -%]
<table class="layoutTable"><tr><td><img src="/static/images/error_16.png" /></td><td><a href="#tabs-errors" id="error-link">There are evaluation errors!</a></td></tr></table>
<script type="text/javascript">
@ -106,7 +101,7 @@
[% IF !edit && activeJobsStatus -%]
<h2>Status</h2>
<table>
<table class="table table-striped table-condensed">
<thead><tr><th>Job</th>[% FOREACH s IN systems %]<th>[% s.system %]</th>[% END %]</tr></thead>
<tbody>
[% odd = 0 %]
@ -140,21 +135,15 @@
[% IF !edit -%]
[% IF jobset.errormsg -%]
<div id="tabs-errors">
<div id="tabs-errors" class="tab-pane">
<h3>Evaluation errors</h3>
<pre class="multiLineMsg error">[% HTML.escape(jobset.errormsg) %]</pre>
</div>
[% END %]
[% END %]
<div id="tabs-setup">
<div id="tabs-setup" class="tab-pane [% IF edit %]active[% END %]">
<h2>Information</h2>
[% IF !edit && c.user_exists %]
<p>
[ <a href="[% c.uri_for('/jobset' project.name jobset.name 'edit') %]">Edit</a> | <a href="[% c.uri_for('/jobset' project.name jobset.name 'clone') %]">Clone</a> ]
</p>
[% END %]
<table class="layoutTable">
[% IF edit %]
<tr>
@ -217,7 +206,7 @@
[% INCLUDE renderInputs %]
</div>
[% IF !edit -%]
<div id="tabs-channels">
<div id="tabs-channels" class="tab-pane">
<h2>Channels</h2>
@ -233,7 +222,7 @@
</div>
<div id="tabs-jobs">
<div id="tabs-jobs" class="tab-pane">
<h2>Jobs</h2>
@ -255,20 +244,14 @@
</p>
</div>
[%#
<div id="tabs-statistics">
<h2>Statistics</h2>
[% INCLUDE showBuildStats % ]
</div>
%]
[% END %]
</div>
<script type="text/javascript">
$("#generic-tabs").tabs();
jQuery(document).ready(function ($) {
$('#tab').tab('show');
});
</script>
[% IF edit %]

View file

@ -10,21 +10,29 @@
<head>
<title>Hydra - [% title %]</title>
<link type="text/css" href="/static/js/jquery/css/smoothness/jquery-ui-1.8.5.custom.css" rel="Stylesheet" />
<link rel="stylesheet" href="/static/css/hydra.css" type="text/css" />
<link rel="stylesheet" href="/static/css/nixos-site.css" type="text/css" />
<link rel="stylesheet" href="/static/css/logfile.css" type="text/css" />
<script type="text/javascript" src="/static/js/jquery/js/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="/static/js/jquery/js/jquery-ui-1.8.5.custom.min.js"></script>
<script type="text/javascript" src="/static/js/tablesorter/jquery.tablesorter.js"></script>
<link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css" type="text/css" />
<style>
body {
padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
padding-bottom: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
}
</style>
<link rel="stylesheet" href="/static/bootstrap/css/bootstrap-responsive.css" type="text/css" />
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min.js"></script>
<script type="text/javascript" src="/static/bootstrap/js/bootstrap-dropdown.js"></script>
<script type="text/javascript" src="/static/bootstrap/js/bootstrap-tab.js"></script>
<script type="text/javascript" src="/static/bootstrap/js/bootstrap-alert.js"></script>
<!--
<script type="text/javascript" src="/static/js/tablesorter/jquery.tablesorter.js"></script>
-->
<script type="text/javascript">
$(document).ready(function() {
/*** Sortable tables. ***/
$("table.tablesorter").tablesorter();
/*** Tree toggles in logfiles. ***/
@ -72,22 +80,31 @@
</head>
<body>
<div id="page">
<div id="header">
<div id="logo"><a class="no-hover" href="/"><img src="[% logo %]" alt="Hydra Logo" /></a></div>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</a>
<a class="brand" href="[% c.uri_for(c.controller('Root').action_for('index')) %]">Hydra</a>
<div class="nav-collapse">
[% PROCESS topbar.tt %]
[% PROCESS contextbar.tt %]
</div><!--/.nav-collapse -->
</div>
</div>
</div>
<div id="main">
[% PROCESS navbar.tt %]
<div id="content">
<div class="container">
[% content %]
</div>
</div>
<div id="footer">
<div class="navbar navbar-fixed-bottom">
<div id="footer" style="text-align: center;">
<div id="last-modified">
<em><a href="http://nixos.org/hydra" target="_new">Hydra</a> [% HTML.escape(version) %] (using [% HTML.escape(nixVersion) %]).</em>
Page generated on [% INCLUDE renderDateTime %].
@ -97,6 +114,7 @@
</div>
</div>
</body>
</html>

View file

@ -3,7 +3,7 @@
<h1>Build machines</h1>
<table>
<table class="table table-condensed table-striped">
<thead>
<tr>
<th>Enabled</th>

View file

@ -1,97 +1 @@
[% BLOCK makeLinkWrapped %]
<li [% IF curUri == uri %]class="active"[% END %]>
<div class="title"><a href="[% uri %]">[% title %]</a></div>
[% content %]
</li>
[% END %]
[% BLOCK menuItem %]
<li [% IF "${root}${curUri}" == uri %]class="active"[% END %]>
<a href="[% uri %]">[% title %]</a>
</li>
[% END %]
[% BLOCK makeLink -%]
[% INCLUDE makeLinkWrapped content="" -%]
[% END %]
[% BLOCK makeSubMenu %]
[% extra = collapsed ? "collapsed" : "" %]
<div id="left-title">[% title %]</div>
<ul id="left-menu">
[% content %]
</ul>
[% END %]
[% IF project || admin %]
<div id="left-bar">
[% IF project %]
[% WRAPPER makeSubMenu title="Project" collapsed=(jobset || job) %]
[% INCLUDE menuItem uri = c.uri_for(c.controller('Project').action_for('view'), [project.name]) title = "Overview" %]
[% INCLUDE menuItem uri = c.uri_for(c.controller('Project').action_for('releases'), [project.name]) title = "Releases" %]
[% INCLUDE menuItem uri = c.uri_for(c.controller('Project').action_for('all'), [project.name]) title = "All builds" %]
[% INCLUDE menuItem uri = c.uri_for(c.controller('Project').action_for('jobstatus'), [project.name]) title = "Job status" %]
[% INCLUDE menuItem uri = c.uri_for(c.controller('Project').action_for('errors'), [project.name]) title = "Errors" %]
[% END %]
[% END %]
[% IF jobset %]
[% WRAPPER makeSubMenu title="Jobset" collapsed=job %]
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Jobset').action_for('index'), [project.name, jobset.name])
title = "Overview" %]
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Jobset').action_for('evals'), [project.name, jobset.name])
title = "Evaluations" %]
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Jobset').action_for('all'), [project.name, jobset.name])
title = "All builds" %]
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Jobset').action_for('jobstatus'), [project.name, jobset.name])
title = "Job status" %]
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Jobset').action_for('errors'), [project.name, jobset.name])
title = "Errors" %]
[% END %]
[% END %]
[% IF job %]
[% WRAPPER makeSubMenu title="Job" %]
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Job').action_for('overview'), [project.name, jobset.name, job.name])
title = "Overview" %]
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Job').action_for('all'), [project.name, jobset.name, job.name])
title = "All builds" %]
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Job').action_for('jobstatus'), [project.name, jobset.name, job.name])
title = "Job status" %]
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Job').action_for('errors'), [project.name, jobset.name, job.name])
title = "Errors" %]
[% END %]
[% END %]
[% IF admin %]
[% WRAPPER makeSubMenu title="Admin" %]
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Admin').action_for('machines'))
title = "Machines" %]
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Admin').action_for('managenews'))
title = "News" %]
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Admin').action_for('users'))
title = "Users" %]
[% END %]
[% END %]
</div>
[% END %]

View file

@ -7,7 +7,7 @@
[% IF newsItems.size == 0 %]
<p>No news items</p>
[% ELSE %]
<table>
<table class="table table-striped table-condensed">
<thead><th>Date</th><th>Contents</th><th></th></thead>
<tbody>
[% FOREACH i IN newsItems %]

View file

@ -2,10 +2,12 @@
[% PROCESS common.tt %]
[% IF newItems.size != 0 %]
<div class="newsbar">
<div class="alert alert-info">
<a class="close" data-dismiss="alert">×</a>
[% FOREACH i IN newsItems %]
[% contents = String.new(i.contents) %]
<p><b>[% INCLUDE renderDateTime timestamp=i.createtime %]</b> <tt>by [% i.author.fullname %]</tt> <br/>[% contents.replace('\n','<br />\n') %]
<h4 class="alert-heading">[% INCLUDE renderDateTime timestamp=i.createtime %] by [% i.author.fullname %]</h4>
[% contents.replace('\n','<br />\n') %]
[% END %]
</div>
[% END %]
@ -14,7 +16,7 @@
<p>The following projects are hosted on this server:</p>
<table class="tablesorter">
<table class="tablesorter table table-condensed table-striped">
<thead>
<tr>
<th>Id</th>
@ -45,12 +47,4 @@
</tbody>
</table>
[% IF c.user_exists %]
<p>
[ [% INCLUDE maybeLink
uri = c.uri_for(c.controller('Project').action_for('create'))
content = "Create project" %] ]
</p>
[% END %]
[% END %]

View file

@ -29,7 +29,7 @@
Failed build produced output. Click here to inspect the output.
</a>
[<a class="productDetailsToggle" href="javascript:">help</a>]
<div class="help productDetails">
<div class="well hide productDetails">
<p>If you have Nix installed on your machine, this failed build output and
all its dependencies can be unpacked into your local Nix store by doing:</p>
@ -51,7 +51,7 @@
One-click install of Nix package <tt>[% build.nixname %]</tt>
</a>
[<a class="productDetailsToggle" href="javascript:">help</a>, <a href="[% contents %]">contents</a>]
<div class="help productDetails">
<div class="well hide productDetails">
<p>If you have Nix installed on your machine, you can
install this package and all its dependencies automatically
by clicking on the link above. This requires that you have
@ -83,7 +83,7 @@
</a>
[<a class="productDetailsToggle" href="javascript:">help</a>]
<div class="help productDetails">
<div class="well hide productDetails">
<p>If you have Nix installed on your machine, this build and
all its dependencies can be unpacked into your local Nix
store by doing:</p>
@ -133,7 +133,7 @@
[% END %]
</a>
[<a class="productDetailsToggle" href="javascript:">details</a>, <a href="[% contents %]">contents</a>]
<div class="productDetails">
<div class="well hide productDetails">
<table>
<tr>
<th>URL:</th>
@ -230,4 +230,3 @@
);
});
</script>

View file

@ -1,33 +1,27 @@
[% WRAPPER layout.tt title=(edit ? (create ? "New Project" : "Editing Project $project.name") : "Project $project.name") %]
[% PROCESS common.tt %]
[% IF create %]
<h1>New Project</h1>
[% ELSE %]
<h1>Project <tt>[% project.name %]</tt></h1>
[% END %]
<div id="generic-tabs">
<ul>
<ul id="tab" class="nav nav-tabs">
[% IF !edit %]
<li><a href="#tabs-project">Project</a></li>
<li><a href="#tabs-project" data-toggle="tab">Project</a></li>
[% END %]
<li><a href="#tabs-settings">Settings</a></li>
<li><a href="#tabs-settings" data-toggle="tab">Settings</a></li>
[% IF !edit %]
<li><a href="#tabs-views">Views</a></li>
<li><a href="#tabs-channels">Channels</a></li>
<!-- <li><a href="#tabs-statistics">Statistics</a></li> -->
<li><a href="#tabs-views" data-toggle="tab">Views</a></li>
<li><a href="#tabs-channels" data-toggle="tab">Channels</a></li>
[% END %]
</ul>
<div class="tab-content">
[% IF !edit %]
<div id="tabs-project">
<div id="tabs-project" class="tab-pane active">
<h2>Jobsets</h2>
[% IF project.jobsets.size > 0 %]
<p>This project has the following jobsets:</p>
<table class="tablesorter">
<table class="tablesorter table table-striped table-condensed">
<thead>
<tr>
<th></th>
@ -60,32 +54,36 @@
[% IF j.get_column('nrtotal') > 0 %]
[% successrate = ( j.get_column('nrsucceeded') / j.get_column('nrtotal') )*100 %]
[% IF j.get_column('nrscheduled') > 0 %]
[% class = '' %]
[% class = 'label' %]
[% ELSIF successrate < 25 %]
[% class = 'red' %]
[% class = 'label label-important' %]
[% ELSIF successrate < 75 %]
[% class = 'orange' %]
[% class = 'label label-warning' %]
[% ELSIF successrate <= 100 %]
[% class = 'green' %]
[% class = 'label label-success' %]
[% END %]
[% END %]
<td align="right">
<div class="[% class %]">[% successrate FILTER format('%d') %]%</div>
[% IF j.get_column('nrscheduled') > 0 %]
[% j.get_column('nrscheduled') %] builds still running
[% END %]
<td>
<span class="[% class %]">[% successrate FILTER format('%d') %]%</span>
</td>
<td align="right">
<div class="[% class %]">[% j.get_column('nrsucceeded') %]/[% j.get_column('nrtotal') %]</div>
<td>
[% IF j.get_column('nrsucceeded') > 0 %]
<span class="label label-success">[% j.get_column('nrsucceeded') %]</span>
[% END %]
[% IF j.get_column('nrfailed') > 0 %]
<span class="label label-important">[% j.get_column('nrfailed') %]</span>
[% END %]
[% IF j.get_column('nrscheduled') > 0 %]
<span class="label label">[% j.get_column('nrscheduled') %]</span>
[% END %]
</td>
[% IF c.check_user_roles('admin') %]
<td>[ [% INCLUDE maybeLink uri = c.uri_for(c.controller('Admin').action_for('force_eval'), project.name, j.name) content = "Evaluate" confirmmsg = ("Are you sure you want to force evaluation of jobset " _ project.name _ ":" _ j.name _ "?") %]
| [% IF j.hidden %]
[% INCLUDE maybeLink uri = c.uri_for('/jobset' project.name j.name 'unhide') content = "Unhide" %]
<td>[% INCLUDE maybeButton uri = c.uri_for(c.controller('Admin').action_for('force_eval'), project.name, j.name) content = "Evaluate" confirmmsg = ("Are you sure you want to force evaluation of jobset " _ project.name _ ":" _ j.name _ "?") %]
[% IF j.hidden %]
[% INCLUDE maybeButton uri = c.uri_for('/jobset' project.name j.name 'unhide') content = "Unhide" %]
[% ELSE %]
[% INCLUDE maybeLink uri = c.uri_for('/jobset' project.name j.name 'hide') content = "Hide" %]
[% INCLUDE maybeButton uri = c.uri_for('/jobset' project.name j.name 'hide') content = "Hide" %]
[% END %]
]
</td>
[% END%]
</tr>
@ -99,17 +97,16 @@
[% END %]
<p><a href="[% c.uri_for(c.controller('Project').action_for('create_jobset'), [project.name]) %]">[Create a new jobset]</a></p>
</div>
[% END %]
<div id="tabs-settings">
<div id="tabs-settings" class="tab-pane [% IF edit %]active[% END %]">
[% IF edit %]
<form action="[% IF create %][% c.uri_for('/create-project/submit') %][% ELSE %][% c.uri_for('/project' project.name 'submit') %][% END %]" method="post">
[% END %]
<h2>Information[% IF !edit %] <a class="smallLink" href="[% c.uri_for('/project' project.name 'edit') %]">[Edit]</a>[% END %]</h2>
<h2>Information</h2>
<table class="layoutTable">
[% IF edit %]
@ -177,7 +174,7 @@
</div>
[% IF !edit %]
<div id="tabs-views">
<div id="tabs-views" class="tab-pane">
<h2>Views</h2>
@ -203,7 +200,7 @@
<p><a href="[% c.uri_for('/project' project.name 'create-view') %]">[Create a new view]</a></p>
</div>
<div id="tabs-channels">
<div id="tabs-channels" class="tab-pane">
<h2>Channels</h2>
@ -217,17 +214,13 @@
</ul>
</div>
[%#
<div id="tabs-statistics">
<h2>Statistics</h2>
[% INCLUDE showBuildStats % ]
</div>
%]
[% END %]
</div>
<script type="text/javascript">
$("#generic-tabs").tabs();
jQuery(document).ready(function ($) {
$('#tab').tab('show');
});
</script>

View file

@ -12,7 +12,7 @@
<p>This project has made the following releases:</p>
<table class="tablesorter">
<table class="tablesorter table table-condensed table-striped">
<thead>
<tr>
<th>Name</th>

View file

@ -1,251 +1,7 @@
body {
font-family: sans-serif;
background: white;
margin: 2em 1em 2em 1em;
}
#logo img {
height: 6em;
}
img {
border-style: none;
}
a { text-decoration: none;}
a:link { color: #0048b3; }
a:visited { color: #002a6a; }
a.no-hover:hover { background: none; }
a:hover, a:visited:hover { text-decoration: underline;}
a.smallLink {
font-size: 60%;
vertical-align: top;
}
a#error-link {
color: red;
.logTreeToggle {
text-decoration: none;
font-family: monospace;
font-size: larger;
}
table {
empty-cells: show;
border-collapse: collapse;
text-align: left;
}
th, td {
border: 1px solid #dddddd;
vertical-align: top;
}
th {
background-color: #E6EEEE;
}
th.subheader {
background-color: #f0f0f8;
font-size: 120%;
text-align: center;
font-weight: normal;
}
table.tablesorter:not(.clean) tr:nth-child(even) {
background-color: #efefef;
}
table.tablesorter:not(.clean) tr:nth-child(odd) {
background-color: #ffffff;
}
tr.clickable:hover {
background-color: #E6EEFF;
cursor: pointer;
}
td.centered {
text-align: center;
}
td.right {
text-align: right;
}
.layoutTable td, .layoutTable th {
border-style: none;
}
table.tablesorter thead tr .header {
background-image: url(/static/js/tablesorter/themes/blue/bg.gif);
background-repeat: no-repeat;
background-position: center right;
cursor: pointer;
}
table.tablesorter thead tr .headerSortUp {
background-image: url(/static/js/tablesorter/themes/blue/asc.gif);
}
table.tablesorter thead tr .headerSortDown {
background-image: url(/static/js/tablesorter/themes/blue/desc.gif);
}
table.tablesorter thead tr .headerSortDown, table.tablesorter thead tr .headerSortUp {
background-color: #D6DDDD;
}
table.tablesorter thead tr th {
padding-right: 1.5em;
}
#generic-tabs li {
height : 30px;
font-size : 90%;
}
#generic-tabs div {
font-size : 90%;
}
h1, h2, h3 {
font-weight: bold;
color: #002a70;
}
ul.productList {
list-style: none;
padding-left: 1em;
}
ul.productList li {
margin-top: 1em;
}
.productDetails {
display: none;
margin-top: 1em;
margin-bottom: 1em;
margin-left: 3em;
}
tr:nth-child(even).runningBuild {
background-color: #d8ffd8 !important;
}
tr:nth-child(odd).runningBuild {
background-color: #e8ffe8 !important;
}
.disabledBuild {
background-color: #ffc0c0 !important;
}
.error-msg {
color: red;
white-space: pre-wrap;
}
.error {
color: red;
font-weight: bold;
}
pre {
margin-left: 1.5em;
margin-right: 1.5em;
}
pre.buildlog {
border: 1px solid black;
padding: 0.3em;
white-space: pre-wrap;
}
pre.taillog {
border: 3px solid darkblue;
padding: 0.3em;
white-space: pre-wrap;
background: black;
color: #ffffff;
}
div.buildlog {
border: 1px solid black;
padding: 0.3em;
}
.multiLineMsg {
padding: 0em;
margin: 0em;
white-space: pre-wrap;
}
.template {
display: none;
}
div.help {
border: solid black 1px;
padding-left: 1em;
padding-right: 1em;
}
div.help pre {
padding-left: 1.5em;
color: #400000;
}
th.releaseSetJobName {
font-size: 60%;
padding: 0 0 0 0;
}
input.string {
font-family: sans-serif;
font-size: 100%;
background-color: #fffff0;
width: 20em;
}
input.shortString {
width: 7em;
}
input.longString {
width: 40em;
}
textarea.longString {
width: 40em;
}
select {
background-color: #fffff0;
}
button {
background-color: #f0f0e0;
}
form.inline {
display: inline;
}
.red {
color: red;
}
.orange {
color: orange;
}
.green {
color: green;
}
.newsbar {
background-color:#D9E3EA;
border:1px solid #999999;
float:right;
font-size:x-small;
margin:0 0 0.5em 0.5em;
overflow:hidden;
padding:0.5em;
width:30em;
}

View file

@ -1,3 +1,24 @@
[% BLOCK menuItem %]
<li class="[% IF "${root}${curUri}" == uri %]active[% END %]">
<a href="[% uri %]">[% title %]</a>
</li>
[% END %]
[% BLOCK makeLink -%]
[% INCLUDE makeLinkWrapped content="" -%]
[% END %]
[% BLOCK makeSubMenu %]
<li class="dropdown">
<a class="dropdown-toggle" href="#" data-toggle="dropdown">[% title %]<b class="caret"></b></a>
<ul id="left-menu" class="dropdown-menu">
[% content %]
</ul>
</li>
[% END %]
[% BLOCK makeLinkWrapped %]
<li [% IF curUri == uri %]class="active"[% END %]>
<a href="[% uri %]">[% title %]</a>
@ -9,30 +30,118 @@
[% INCLUDE makeLinkWrapped content="" -%]
[% END %]
[% BLOCK makeSubMenu %]
<ul class="short-menu" id="top-menu">
[% content %]
</ul>
</li>
[% END %]
[% WRAPPER makeSubMenu title="Hydra" %]
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Root').action_for('index'))
title = "Projects" %]
<ul class="nav" id="top-menu">
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Root').action_for('queue'))
title = "Queue" %] ([% nrRunningBuilds %]/[% nrQueuedBuilds %])
title = "Queue ("_ nrRunningBuilds _"/"_ nrQueuedBuilds _")" %]
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Root').action_for('all'))
title = "All builds" %]
[% IF project || admin %]
[% IF project %]
[% WRAPPER makeSubMenu title=("Project: " _ project.name) collapsed=(jobset || job) %]
[% INCLUDE menuItem uri = c.uri_for(c.controller('Project').action_for('view'), [project.name]) title = "Overview" %]
[% INCLUDE menuItem uri = c.uri_for(c.controller('Project').action_for('releases'), [project.name]) title = "Releases" %]
[% INCLUDE menuItem uri = c.uri_for(c.controller('Project').action_for('all'), [project.name]) title = "All builds" %]
[% INCLUDE menuItem uri = c.uri_for(c.controller('Project').action_for('jobstatus'), [project.name]) title = "Job status" %]
[% INCLUDE menuItem uri = c.uri_for(c.controller('Project').action_for('errors'), [project.name]) title = "Errors" %]
[% IF c.check_user_roles('admin') %]
<li class="divider"></li>
[% INCLUDE menuItem uri = c.uri_for(c.controller('Project').action_for('create_jobset'), [project.name]) title = "Create new jobset" %]
[% INCLUDE menuItem uri = c.uri_for('/project' project.name 'edit') title="Edit project" %]
[% END %]
[% END %]
[% END %]
[% IF jobset %]
[% WRAPPER makeSubMenu title=("Jobset: " _ jobset.name) collapsed=job %]
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Jobset').action_for('index'), [project.name, jobset.name])
title = "Overview" %]
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Jobset').action_for('evals'), [project.name, jobset.name])
title = "Evaluations" %]
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Jobset').action_for('all'), [project.name, jobset.name])
title = "All builds" %]
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Jobset').action_for('jobstatus'), [project.name, jobset.name])
title = "Job status" %]
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Jobset').action_for('errors'), [project.name, jobset.name])
title = "Errors" %]
[% IF c.check_user_roles('admin') %]
<li class="divider"></li>
[% INCLUDE menuItem uri = c.uri_for('/jobset' project.name jobset.name 'edit') title="Edit jobset" %]
[% INCLUDE menuItem uri = c.uri_for('/jobset' project.name jobset.name 'clone') title="Clone jobset" %]
[% END %]
[% END %]
[% END %]
[% IF job %]
[% WRAPPER makeSubMenu title=("Job: " _ job.name) %]
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Job').action_for('overview'), [project.name, jobset.name, job.name])
title = "Overview" %]
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Job').action_for('all'), [project.name, jobset.name, job.name])
title = "All builds" %]
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Job').action_for('jobstatus'), [project.name, jobset.name, job.name])
title = "Job status" %]
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Job').action_for('errors'), [project.name, jobset.name, job.name])
title = "Errors" %]
<li>[% INCLUDE maybeLink uri = c.uri_for(c.controller('Admin').action_for('users')) content = "Manage users" %]</li>
<li>[% INCLUDE maybeLink uri = c.uri_for(c.controller('Admin').action_for('news')) content = "Manage news" %]</li>
[% END %]
[% END %]
[% END %]
[% IF c.user_exists %]
[% IF c.check_user_roles('admin') %]
[% WRAPPER makeSubMenu title="Admin" %]
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Admin').action_for('index'))
title = "Admin" %]
title = "Machine status" %]
<li class="divider"></li>
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Admin').action_for('machines'))
title = "Manage machines" %]
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Admin').action_for('managenews'))
title = "Manage news" %]
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Admin').action_for('users'))
title = "Manage users" %]
<li class="divider"></li>
[% INCLUDE maybeLink
uri = c.uri_for(c.controller('Admin').action_for('clearfailedcache'))
content = "Clear failed builds cache"
confirmmsg = "Are you sure you want to clear the failed builds cache?" %]
[% INCLUDE maybeLink
uri = c.uri_for(c.controller('Admin').action_for('clear_queue_non_current'))
content = "Clear all non-running old builds from queue."
confirmmsg = "Are you sure you want to clear the queue?"
%]
<li class="divider"></li>
[% IF c.check_user_roles('admin') %]
[% INCLUDE menuItem uri = c.uri_for(c.controller('Project').action_for('create')) title = "Create new project" %]
[% END %]
[% END %]
[% END %]
[% END %]
[% IF c.user_exists %]
[% INCLUDE makeLink
uri = c.uri_for(c.controller('Root').action_for('logout'))
title = "Sign out" %]
@ -41,6 +150,5 @@
uri = c.uri_for(c.controller('Root').action_for('login'))
title = "Sign in" %]
[% END %]
[% END %]
</ul>

View file

@ -3,7 +3,7 @@
<h1>Users</h1>
<table>
<table class="table table-striped table-condensed">
<thead>
<tr>
<th>Username</th>

View file

@ -11,7 +11,7 @@
<p>Showing results [% (page - 1) * resultsPerPage + 1 %] - [% (page - 1) * resultsPerPage + results.size %] out of [% totalResults %].</p>
<table class="tablesorter">
<table class="tablesorter table table-condensed table-striped">
<thead>
<tr>
<th></th>
@ -64,6 +64,12 @@
</tbody>
</table>
<ul class="pager">
<li class="previous"><a href="[% "$baseUri?page="; (page - 1) %]">Prev</a></li>
<li class="next"><a href="[% "$baseUri?page="; (page + 1) %]">Next</a></li>
</ul>
<!--
<p>
[<a href="[% "$baseUri?page=1" %]">First</a>]
[% IF page > 1 %]
@ -74,5 +80,6 @@
[% END %]
[<a href="[% "$baseUri?page="; (totalResults - 1) div resultsPerPage + 1 %]">Last</a>]
</p>
-->
[% END %]