/*
* The MIT License
*
* Copyright (c) 2012, Jesse Farinacci
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package my_plugin.jobwalldisplay;
import hudson.model.AbstractProject;
import hudson.model.Action;
import hudson.model.Hudson;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
/**
* A very simple {@link hudson.model.Action} which provides a <code>poll</code>
* URL target to force polling for the specified
* {@link hudson.model.AbstractProject}.
*
* @author <a href="mailto:jieryn@gmail.com">Jesse Farinacci</a>
* @since 1.0
*/
public final class JobWallDisplayAction implements Action {
@SuppressWarnings({ "rawtypes" })
private final AbstractProject project;
private static final String ENCODING = "UTF-8";
public JobWallDisplayAction(
@SuppressWarnings("rawtypes") final AbstractProject project) {
this.project = project;
}
public AbstractProject<?,?> getOwner() {
return project;
}
public String getIconFileName() {
return "search.png";
}
public String getUrlName() {
String encodedUrl = null;
String encodedJobName = null;
String rootUrl = getRootUrl();
try {
encodedUrl = URLEncoder.encode(rootUrl, ENCODING);
encodedJobName = URLEncoder.encode(getJobName(), ENCODING);
} catch (UnsupportedEncodingException e) {
encodedUrl = rootUrl;
encodedJobName = getJobName();
}
return String
.format("/plugin/jobwalldisplay/jobShow.htm?jobName=%s&jenkinsUrl=%s",
encodedJobName, encodedUrl);
}
protected String getRootUrl() {
return Hudson.getInstance().getRootUrl();
}
public String getDisplayName() {
return Messages.ActionLabel();
}
public String getJobNumber()
{
return String.format("%d", project.getLastBuild().getNumber());
}
public String getJobStatusImage()
{
return project.getLastBuild().getBuildStatusUrl();
}
public String getJobStatusColor()
{
return project.getLastBuild().getIconColor().getHtmlBaseColor();
}
public String getJobBuildTime()
{
return project.getLastBuild().getTime().toString();
}
public String getJobName()
{
return project.getDisplayName();
}
}
======================
<!DOCTYPE html>
<html>
<head>
<title>Wall Notice</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta http-equiv=Content-Type content="text/html; charset=utf-8">
<style type="text/css">
body
{
margin: 0;
padding: 0px;
background-color: #000000;
overflow: hidden;
}
#TextDimensionDiv
{
position: absolute;
visibility: hidden;
height: auto;
width: auto;
}
.jobName {
border-radius: 20px;
-moz-border-radius: 20px;
text-shadow: 0px -2px black;
display: table-cell;
vertical-align: middle;
text-align: center;
font-family: Calibri, 黑体;
color: #FFFFFF;
}
.jobContext {
border-radius: 20px;
-moz-border-radius: 20px;
text-shadow: 0px -2px black;
margin: 10;
padding: 10px;
font-family: Calibri, 黑体;
color: #FFFFFF;
}
.jobBlock {
position: absolute;
border-radius: 20px;
-moz-border-radius: 20px;
text-shadow: 0px -2px black;
}
.jobStatus{
position: absolute;
border-radius: 20px;
-moz-border-radius: 20px;
display: table-cell;
vertical-align: middle;
}
</style>
<script src="jquery.js"></script>
<script type="text/javascript">
function getParameterByName(name, defaultValue)
{
name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
var regexS = "[\\?&]" + name + "=([^&#]*)";
var regex = new RegExp(regexS);
var results = regex.exec(window.location.href);
if(results == null)
return defaultValue;
else
return decodeURIComponent(results[1].replace(/\+/g, " "));
}
function getJobInfo(name)
{
$.ajax({
url: jenkinsUrl + "/job/" + jobName + "/api/json",
dataType: "json",
data: {
"tree": "property[wallDisplayName],name,color,lastStableBuild[timestamp],lastBuild[number,timestamp,duration,actions[parameters[name,value],claimed,claimedBy,failCount,skipCount,totalCount],culprits[fullName,property[address]]],lastSuccessfulBuild[duration]"
},
success: function(job, textStatus, jqXHR) {
var add = true;
$.each(jobsToDisplay, function(index, oldJob) {
if (oldJob.name == job.name)
{
job.visited = true;
jobsToDisplay[index] = job;
add = false;
}
});
var jobFilteredOut = false;
if(!showDisabledBuilds && job.color === 'disabled') {
add = false;
jobFilteredOut = true;
}
// show all failed builds, but only show passing builds
// in the date range the user specifies
//
// The times are approximate - we're not being totally precise,
// so a day is considered the last 24 hours, a week the last
// 7 days, and a month the last 31 days. It's just so the
// build screen doesn't fill up with builds.
if(job.color === "blue") {
var timestamp = new Date().getTime();
var ONE_DAY_MS = 86400000;
var minTimestamp = 0;
switch(buildRange) {
case 'active today': // last 24 hours
minTimestamp = timestamp - ONE_DAY_MS;
break;
case 'active this week':
minTimestamp = timestamp - ONE_DAY_MS * 7;
break;
case 'active this month':
minTimestamp = timestamp - ONE_DAY_MS * 31;
break;
}
if(job.lastBuild && job.lastBuild.timestamp < minTimestamp) {
add = false;
jobFilteredOut = true;
}
}
if (add)
{
jobsToDisplay[jobsToDisplay.length] = job;
} else {
if(jobFilteredOut) {
jobsToDisplay.remove(job);
}
}
jobsToDisplay.sort(function(job1, job2) {
var sort = 0;
if (sortOrder == "job status")
{
sort = jobStatusOrder[job1.color] - jobStatusOrder[job2.color];
}
if (sort == 0)
{
sort = getJobText(job1, showBuildNumber, showLastStableTimeAgo, showDetails).localeCompare(getJobText(job2, showBuildNumber, showLastStableTimeAgo, showDetails));
}
return sort;
});
updateRunning[jobName] = false;
},
error: function(e, xhr) {
debug("error getting api for job '" + jobName + "': '" + e.statusText + "'");
updateRunning[jobName] = false;
},
timeout: jenkinsTimeOut
});
}
var jobName = getParameterByName("jobName", "");
var jenkinsUrl = getParameterByName("jenkinsUrl", window.location.protocol + "://" + window.location.host + "/" + window.location.pathname.replace("plugin/jobwalldisplay/jobShow.htm", ""));
var jobColor = "#D4190E";
var cachedTextDimensions = new Array();
function getTextDimensions(text, fontSize) {
var cacheKey = text + fontSize;
if (cachedTextDimensions[cacheKey] == null)
{
$("#TextDimensionDiv").html(text);
$("#TextDimensionDiv").css("font-size", fontSize + "px");
var dimension = {};
dimension.width = $("#TextDimensionDiv")[0].clientWidth;
dimension.height = $("#TextDimensionDiv")[0].clientHeight;
cachedTextDimensions[cacheKey] = dimension;
}
return cachedTextDimensions[cacheKey];
}
function fontSizeJobName(text, height) {
var maxFontSize = 0;
for (var fontSize = 10; fontSize <= 302; fontSize++) {
var textDimensions = getTextDimensions(text, fontSize);
if (textDimensions.height <= height)
maxFontSize = fontSize;
else
break;
}
return maxFontSize;
}
function repaint() {
text = ".";
clientHeight = $(window).height();
clientWidth = $(window).width();
divJobBlock = $('<div />');
divJobName = $('<div />');
divJobContext = $('<div />');
divJobStatus = $('<div />');
var padding = 10;
jobStatusHeight = 56;
jobBlockHeight = clientHeight - padding - jobStatusHeight;
jobNameHeight = jobBlockHeight * 0.25;
jobContextHeight = jobBlockHeight * 0.75;
fontSize = fontSizeJobName(jobName, clientHeight * 0.2)
divJobName.css({
"height": jobNameHeight + "px",
"width": clientWidth + "px",
"left": "0",
"top": "0",
"font-size": fontSize + "px"
});
divJobContext.css({
"height": jobContextHeight + "px",
"width": clientWidth + "px",
"left": "0",
"top": "0",
"font-size": (fontSize * 0.8) + "px"
});
divJobBlock.css({
"height": jobBlockHeight + "px",
"width": clientWidth + "px",
"left": "0",
"top": "0",
"background-color": jobColor
});
divJobStatus.css({
"height": jobStatusHeight + "px",
"width": clientWidth + "px",
"left": "0",
"top": (clientHeight - jobStatusHeight) + "px",
"background-color": "#F0F0F0"
});
divJobName.addClass("jobName");
divJobBlock.addClass("jobBlock");
divJobContext.addClass("jobContext");
divJobStatus.addClass("jobStatus");
divJobName.html(jobName);
divJobContext.html("Failed<br />r123456<br />2013/9/14");
divJobStatus.html("<div style=\"padding: 5px\"><img src=\"http://127.0.0.1/static/336ce931/images/48x48/red.gif\" /><img src=\"http://127.0.0.1/static/336ce931/images/48x48/red.gif\" /><img src=\"http://127.0.0.1/static/336ce931/images/48x48/red_anime.gif\" /></div>");
divJobBlock.prepend(divJobContext);
divJobBlock.prepend(divJobName);
$("body").prepend(divJobStatus);
$("body").prepend(divJobBlock);
}
function init() {
$(document).ready(function() {
repaint();
});
}
defaultContext = "loading..."
context = defaultContext
init(context);
</script>
</head>
<body>
<div id="TextDimensionDiv">
</div>
</body>
</html>