Inheritance: WebServiceResponse
Example #1
0
	protected override void OnLoad (EventArgs e)
	{
		base.OnLoad (e);

		try {
			string action = null;

			int id;

			response = Master.WebService.GetViewLaneData2 (Master.WebServiceLogin,
				Utils.TryParseInt32 (Request ["lane_id"]), Request ["lane"],
				Utils.TryParseInt32 (Request ["host_id"]), Request ["host"],
				Utils.TryParseInt32 (Request ["revision_id"]), Request ["revision"], false);

			if (response.Exception != null) {
				if (response.Exception.HttpCode == 403) {
					Master.RequestLogin ();
					return;
				}
				lblMessage.Text = response.Exception.Message;
				return;
			}

			if (Authentication.IsInRole (response, MonkeyWrench.DataClasses.Logic.Roles.Administrator))
				action = Request ["action"];


			DBHost host = response.Host;
			DBLane lane = response.Lane;
			DBRevision revision = response.Revision;

			if (lane == null || host == null || revision == null) {
				Response.Redirect ("index.aspx", false);
				return;
			}

			if (!string.IsNullOrEmpty (action)) {
				switch (action) {
				case "clearrevision":
					Master.WebService.ClearRevision (Master.WebServiceLogin, lane.id, host.id, revision.id);
					break;
				case "deleterevision":
					Master.WebService.RescheduleRevision (Master.WebServiceLogin, lane.id, host.id, revision.id);
					break;
				case "ignorerevision":
					Master.WebService.IgnoreRevision (Master.WebServiceLogin, lane.id, host.id, revision.id);
					break;
				case "abortrevision":
					Master.WebService.AbortRevision (Master.WebServiceLogin, lane.id, host.id, revision.id);
					break;
				case "clearstep":
					if (int.TryParse (Request ["work_id"], out id))
						Master.WebService.ClearWork (Master.WebServiceLogin, id);
					break;
				case "abortstep":
					if (int.TryParse (Request ["work_id"], out id))
						Master.WebService.AbortWork (Master.WebServiceLogin, id);
					break;
				case "pausestep":
					if (int.TryParse (Request ["work_id"], out id))
						Master.WebService.PauseWork (Master.WebServiceLogin, id);
					break;
				case "resumestep":
					if (int.TryParse (Request ["work_id"], out id))
						Master.WebService.ResumeWork (Master.WebServiceLogin, id);
					break;
				}

				Response.Redirect (string.Format ("ViewLane.aspx?lane_id={0}&host_id={1}&revision_id={2}", lane.id, host.id, revision.id), false);
				Page.Visible = false;
				return;
			}

			header.InnerHtml = GenerateHeader (response, lane, host, revision, "Build of");
			buildtable.InnerHtml = GenerateLane (response);
		} catch (Exception ex) {
			Response.Write (ex.ToString ().Replace ("\n", "<br/>"));
		}
	}
		public GetViewLaneDataResponse GetViewLaneData2 (WebServiceLogin login, int? lane_id, string lane, int? host_id, string host, int? revision_id, string revision, bool include_hidden_files)
		{
			GetViewLaneDataResponse response = new GetViewLaneDataResponse ();
			using (DB db = new DB ()) {
				Authenticate (db, login, response);

				response.Now = db.Now;
				response.Lane = FindLane (db, lane_id, lane);
				response.Host = FindHost (db, host_id, host);
				if (response.Lane != null)
					response.Revision = revision_id.HasValue ? FindRevision (db, revision_id.Value) : FindRevision (db, response.Lane, revision);

				if (response.Lane == null || response.Revision == null)
					throw new HttpException (404, "Revision work not found");

				response.RevisionWork = DBRevisionWork_Extensions.Find (db, response.Lane, response.Host, response.Revision);
				if (response.RevisionWork != null && response.RevisionWork.workhost_id.HasValue) {
					response.WorkHost = FindHost (db, response.RevisionWork.workhost_id, null);
				}
				response.WorkViews = db.GetWork (response.RevisionWork);
				response.WorkFileViews = new List<List<DBWorkFileView>> ();
				for (int i = 0; i < response.WorkViews.Count; i++) {
					response.WorkFileViews.Add (DBWork_Extensions.GetFiles (db, response.WorkViews [i].id, include_hidden_files));
				}
				response.Links = DBWork_Extensions.GetLinks (db, response.WorkViews.Select<DBWorkView2, int> ((DBWorkView2 a, int b) => a.id));
			}

			return response;
		}
Example #3
0
	public string GenerateLane (GetViewLaneDataResponse response)
	{
		StringBuilder matrix = new StringBuilder ();
		List<DBWorkView2> steps = response.WorkViews;
		DBRevision dbr = response.Revision;
		DBRevisionWork revisionwork = response.RevisionWork;
		DBLane lane = response.Lane;
		DBHost host = response.Host;
		DBRevision revision = response.Revision;

		StringBuilder header = new StringBuilder ();
		header.AppendFormat ("Revision: <a href='GetRevisionLog.aspx?id={0}'>{1}</a>", dbr.id, dbr.revision);
		header.AppendFormat (" - Status: {0}", revisionwork.State);
		header.AppendFormat (" - Author: {0}", dbr.author);
		header.AppendFormat (" - Commit date: {0}", dbr.date.ToString ("yyyy/MM/dd HH:mm:ss UTC"));

		if (Authentication.IsInRole (response, MonkeyWrench.DataClasses.Logic.Roles.Administrator)) {
			bool isExecuting = response.RevisionWork.State == DBState.Executing || (response.RevisionWork.State == DBState.Issues && !response.RevisionWork.completed) || response.RevisionWork.State == DBState.Aborted;
			if (isExecuting) {
				header.AppendFormat (" - <a href='javascript:confirmViewLaneAction (\"ViewLane.aspx?lane_id={0}&amp;host_id={2}&amp;revision_id={1}&amp;action=clearrevision\", \"clear\");'>reset work</a>", lane.id, dbr.id, host.id);
				header.AppendFormat (" - <a href='javascript:confirmViewLaneAction (\"ViewLane.aspx?lane_id={0}&amp;host_id={2}&amp;revision_id={1}&amp;action=deleterevision\", \"delete\");'>delete work</a>", lane.id, dbr.id, host.id);
				header.AppendFormat (" - <a href='ViewLane.aspx?lane_id={0}&amp;host_id={2}&amp;revision_id={1}&amp;action=abortrevision'>abort work</a>", lane.id, dbr.id, host.id);
			} else if (response.RevisionWork.State == DBState.Ignore) {
				header.AppendFormat (" - <a href='ViewLane.aspx?lane_id={0}&amp;host_id={2}&amp;revision_id={1}&amp;action=clearrevision'>build this revision</a>", lane.id, dbr.id, host.id);
			} else {
				if (response.RevisionWork.State == DBState.NotDone)
					header.AppendFormat (" - <a href='ViewLane.aspx?lane_id={0}&amp;host_id={2}&amp;revision_id={1}&amp;action=ignorerevision'>don't build</a>", lane.id, dbr.id, host.id);
				header.AppendFormat (" - <a href='ViewLane.aspx?lane_id={0}&amp;host_id={2}&amp;revision_id={1}&amp;action=clearrevision'>reset work</a>", lane.id, dbr.id, host.id);
				header.AppendFormat (" - <a href='ViewLane.aspx?lane_id={0}&amp;host_id={2}&amp;revision_id={1}&amp;action=deleterevision'>delete work</a>", lane.id, dbr.id, host.id);
			}
		}

		if (response.WorkHost != null) {
			header.AppendFormat (" - Assigned to <a href='ViewHostHistory.aspx?host_id={1}'>{0}</a>", response.WorkHost.host, response.WorkHost.id);
		} else {
			header.AppendFormat (" - Unassigned.");
		}

		if (!revisionwork.completed && revisionwork.State != DBState.NotDone && revisionwork.State != DBState.Paused && revisionwork.State != DBState.Ignore) {
			header.Insert (0, "<center><table class='executing'><td>");
			header.Append ("</td></table></center>");
		}

		// matrix.AppendFormat ("<div class='buildstatus {0}'>Status: {0}</div>", revisionwork.State.ToString ().ToLowerInvariant ());

		matrix.AppendLine ("<table class='buildstatus'>");

		matrix.AppendFormat ("<tr class='{0}'>", revisionwork.State.ToString ().ToLowerInvariant ());
		matrix.Append ("<th colspan='9'>");
		matrix.Append (header.ToString ());
		matrix.Append ("</th>");
		matrix.AppendLine ("</tr>");

		matrix.AppendLine ("<tr>");
		matrix.AppendLine ("\t<th>Step</th>");
		matrix.AppendLine ("\t<th>Result</th>");
		matrix.AppendLine ("\t<th>Start Time</th>");
		matrix.AppendLine ("\t<th>Duration</th>");
		matrix.AppendLine ("\t<th>Html report</th>");
		matrix.AppendLine ("\t<th>Summary</th>");
		matrix.AppendLine ("\t<th>Files</th>");
		matrix.AppendLine ("\t<th>Host</th>");
		matrix.AppendLine ("\t<th>Misc</th>");
		matrix.AppendLine ("</tr>");

		bool failed = false;
		for (int s = 0; s < steps.Count; s++) {
			DBWorkView2 step = steps [s];
			DBState state = (DBState) step.state;
			DateTime starttime = step.starttime.ToLocalTime ();
			DateTime endtime = step.endtime.ToLocalTime ();
			int duration = (int) (endtime - starttime).TotalSeconds;
			bool nonfatal = step.nonfatal;
			string command = step.command;
			List<DBWorkFileView> files = response.WorkFileViews [s];
			IEnumerable<DBFileLink> links = response.Links.Where<DBFileLink> ((DBFileLink link) => link.work_id == step.id);

			if (step.endtime.Year < DateTime.Now.Year - 1 && step.duration == 0) {// Not ended, endtime defaults to year 2000
				duration = (int) (response.Now - starttime).TotalSeconds;
			} else if (step.endtime == DateTime.MinValue) {
				duration = step.duration;
			}

			if (state == DBState.Failed && !nonfatal)
				failed = true;

			matrix.AppendLine ("<tr>");

			// step
			DBWorkFileView step_log = files.Find ((v) => v.filename == command + ".log");
			matrix.Append ("\t<td>");
			if (step_log != null) {
				matrix.AppendFormat ("<a href='GetFile.aspx?id={0}'>{1}</a> ", step_log.id, Path.GetFileNameWithoutExtension (command));
			} else {
				matrix.Append (Path.GetFileNameWithoutExtension (command));
			}
			matrix.Append ("</td>");

			// result
			string result;
			switch (state) {
			case DBState.NotDone:
				result = failed ? "skipped" : "queued"; break;
			case DBState.Executing:
				result = "running"; break;
			case DBState.Failed:
				result = nonfatal ? "issues" : "failure"; break;
			case DBState.Success:
			case DBState.Aborted:
			case DBState.Timeout:
			case DBState.Paused:
			default:
				result = state.ToString ().ToLowerInvariant ();
				break;
			}

			// result
			matrix.AppendFormat ("\t<td class='{0}'>{0}</td>", result);

			if (state > DBState.NotDone && state != DBState.Paused && state != DBState.Ignore && state != DBState.DependencyNotFulfilled) {
				matrix.AppendFormat ("<td>{0}</td>", step.starttime.ToString ("yyyy/MM/dd HH:mm:ss UTC"));
			} else {
				matrix.AppendLine ("<td>-</td>");
			}
			// duration
			matrix.Append ("\t<td>");
			if (state >= DBState.Executing && state != DBState.Paused && state != DBState.Ignore && state != DBState.DependencyNotFulfilled && state != DBState.Aborted) {
				matrix.Append ("[");
				matrix.Append (TimeSpan.FromSeconds (duration).ToString ());
				matrix.Append ("]");
			} else {
				matrix.Append ("-");
			}
			matrix.AppendLine ("</td>");

			// html report
			matrix.AppendLine ("<td>");
			DBWorkFileView index_html = null;

			foreach (DBWorkFileView file in files) {
				if (file.filename == "index.html") {
					index_html = file;
					break;
				}
			}

			if (index_html != null) {
				matrix.AppendFormat ("<a href='ViewHtmlReportEmbedded.aspx?workfile_id={0}&lane_id={1}&host_id={2}&revision_id={3}'>View html report</a>", index_html.id, lane.id, host.id, revision.id);
			} else {
				matrix.AppendLine ("-");
			}
			matrix.AppendLine ("</td>");

			// summary
			matrix.AppendLine ("<td>");
			matrix.AppendLine (step.summary);
			matrix.AppendLine ("</td>");

			// files
			matrix.AppendLine ("<td style='text-align: left;'>");
			bool did_first = false;
			foreach (DBWorkFileView file in files) {
				if (file.hidden)
					continue;
				if (file.hidden)
					continue;
				if (did_first)
					matrix.Append (", ");
				matrix.AppendFormat ("<a href='GetFile.aspx?id={0}'>{1}</a> ", file.id, file.filename);
				did_first = true;
			}
			foreach (var link in links) {
				if (did_first)
					matrix.Append (", ");
				matrix.Append (link.link);
				did_first = true;
			}
			matrix.AppendLine ("</td>");

			// host
			matrix.AppendFormat ("<td>{0}</td>", step.workhost);

			// misc
			matrix.AppendFormat ("\t<td><a href='ViewWorkTable.aspx?lane_id={1}&amp;host_id={2}&amp;command_id={3}'>History for '{4}'</a></td>", result, lane.id, host.id, step.command_id, command);

			matrix.AppendLine ("</tr>");
		}
		matrix.AppendLine ("</table>");

		return matrix.ToString ();
	}
Example #4
0
	public static string GenerateHeader (GetViewLaneDataResponse response, DBLane lane, DBHost host, DBRevision revision, string description)
	{
		if (!Authentication.IsInRole (response, MonkeyWrench.DataClasses.Logic.Roles.Administrator)) {
			return string.Format (@"
<h2>{4} revision <a href='ViewLane.aspx?lane_id={0}&host_id={1}&revision_id={6}'>{5}</a> on lane '{2}' on '<a href='ViewHostHistory.aspx?host_id={1}'>{3}</a>' 
(<a href='ViewTable.aspx?lane_id={0}&amp;host_id={1}'>table</a>)</h2><br/>", lane.id, host.id, lane.lane, host.host, description, revision.revision, revision.id);
		} else {
			return string.Format (@"
<h2>{4} revision <a href='ViewLane.aspx?lane_id={0}&host_id={1}&revision_id={6}'>{5}</a> on lane '<a href='EditLane.aspx?lane_id={0}'>{2}</a>' on '<a href='ViewHostHistory.aspx?host_id={1}'>{3}</a>' 
(<a href='ViewTable.aspx?lane_id={0}&amp;host_id={1}'>table</a>)</h2><br/>", lane.id, host.id, lane.lane, host.host, description, revision.revision, revision.id);
		}
	}
Example #5
0
	public string GenerateLane (GetViewLaneDataResponse response)
	{
		StringBuilder matrix = new StringBuilder ();
		List<DBWorkView2> steps = response.WorkViews;
		DateTime beginning = new DateTime (2001, 1, 1, 0, 0, 0);
		DBRevision dbr = response.Revision;
		DBRevisionWork revisionwork = response.RevisionWork;
		DBLane lane = response.Lane;
		DBHost host = response.Host;
		DBRevision revision = response.Revision;

		StringBuilder header = new StringBuilder ();
		header.AppendFormat ("Revision: <a href='GetRevisionLog.aspx?id={0}'>{1}</a>", dbr.id, dbr.revision);
		header.AppendFormat (" - Status: {0}", revisionwork.State);
		header.AppendFormat (" - Author: {0}", dbr.author);
		header.AppendFormat (" - Commit date: {0}", dbr.date.ToString ("yyyy/MM/dd HH:mm:ss UTC"));
	
		if (Utils.IsInRole (MonkeyWrench.DataClasses.Logic.Roles.Administrator)) {
			header.AppendFormat (" - <a href='ViewLane.aspx?lane_id={0}&amp;host_id={2}&amp;revision_id={1}&amp;action=clearrevision'>reset work</a>", lane.id, dbr.id, host.id);
			header.AppendFormat (" - <a href='ViewLane.aspx?lane_id={0}&amp;host_id={2}&amp;revision_id={1}&amp;action=deleterevision'>delete work</a>", lane.id, dbr.id, host.id);
			header.AppendFormat (" - <a href='ViewLane.aspx?lane_id={0}&amp;host_id={2}&amp;revision_id={1}&amp;action=abortrevision'>abort work</a>", lane.id, dbr.id, host.id);
		}

		if (response.WorkHost != null) {
			header.AppendFormat (" - Assigned to {0}", response.WorkHost.host);
		} else {
			header.AppendFormat (" - Unassigned.");
		}

		if (!revisionwork.completed && revisionwork.State != DBState.NotDone && revisionwork.State != DBState.Paused) {
			header.Insert (0, "<center><table class='executing'><td>");
			header.Append ("</td></table></center>");
		}

		// matrix.AppendFormat ("<div class='buildstatus {0}'>Status: {0}</div>", revisionwork.State.ToString ().ToLowerInvariant ());

		matrix.AppendLine ("<table class='buildstatus'>");

		matrix.AppendFormat ("<tr class='{0}'>", revisionwork.State.ToString ().ToLowerInvariant ());
		matrix.Append ("<th colspan='9'>");
		matrix.Append (header.ToString ());
		matrix.Append ("</th>");
		matrix.AppendLine ("</tr>");

		matrix.AppendLine ("<tr>");
		matrix.AppendLine ("\t<th>Step</th>");
		matrix.AppendLine ("\t<th>Result</th>");
		matrix.AppendLine ("\t<th>Start Time</th>");
		matrix.AppendLine ("\t<th>Duration</th>");
		matrix.AppendLine ("\t<th>Html report</th>");
		matrix.AppendLine ("\t<th>Summary</th>");
		matrix.AppendLine ("\t<th>Files</th>");
		matrix.AppendLine ("\t<th>Host</th>");
		matrix.AppendLine ("</tr>");

		bool failed = false;
		for (int s = 0; s < steps.Count; s++) {
			DBWorkView2 step = steps [s];
			DBState state = (DBState) step.state;
			bool done = state > DBState.Executing;
			DateTime starttime = step.starttime.ToLocalTime ();
			DateTime endtime = step.endtime.ToLocalTime ();
			int duration = (int) (endtime - starttime).TotalSeconds;
			bool nonfatal = step.nonfatal;
			bool alwaysexecute = step.alwaysexecute;
			string command = step.command;
			List<DBWorkFileView> files = response.WorkFileViews [s];

			if (step.endtime.Year < DateTime.Now.Year - 1 && step.duration == 0) {// Not ended, endtime defaults to year 2000
				duration = (int) (response.Now - starttime).TotalSeconds;
			} else if (step.endtime == DateTime.MinValue) {
				duration = step.duration;
			}

			if (state == DBState.Failed && !nonfatal)
				failed = true;

			matrix.AppendLine ("<tr>");

			// step
			matrix.AppendFormat ("\t<td><a href='ViewWorkTable.aspx?lane_id={1}&amp;host_id={2}&amp;command_id={3}'>{0}</a></td>", command.Replace (".sh", ""), lane.id, host.id, step.command_id);

			// result
			string result;
			switch (state) {
			case DBState.NotDone:
				result = failed ? "skipped" : "queued"; break;
			case DBState.Executing:
				result = "running"; break;
			case DBState.Failed:
				result = nonfatal ? "issues" : "failure"; break;
			case DBState.Success:
			case DBState.Aborted:
			case DBState.Timeout:
			case DBState.Paused:
			default:
				result = state.ToString ().ToLowerInvariant ();
				break;
			}

			// result
			matrix.AppendFormat ("\t<td class='{0}'>{1}</td>", result, result);

			if (state > DBState.NotDone && state != DBState.Paused) {
				matrix.AppendFormat ("<td>{0}</td>", step.starttime.ToString ("yyyy/MM/dd HH:mm:ss UTC"));
			} else {
				matrix.AppendLine ("<td>-</td>");
			}
			// duration
			matrix.Append ("\t<td>");
			if (state >= DBState.Executing && state != DBState.Paused) {
				matrix.Append ("[");
				matrix.Append (TimeSpan.FromSeconds (duration).ToString ());
				matrix.Append ("]");
			} else {
				matrix.Append ("-");
			}
			matrix.AppendLine ("</td>");

			// html report
			matrix.AppendLine ("<td>");
			DBWorkFileView index_html = null;

			foreach (DBWorkFileView file in files) {
				if (file.filename == "index.html") {
					index_html = file;
					break;
				}
			}

			if (index_html != null) {
				matrix.AppendFormat ("<a href='ViewHtmlReportEmbedded.aspx?workfile_id={0}&lane_id={1}&host_id={2}&revision_id={3}'>View html report</a>", index_html.id, lane.id, host.id, revision.id);
			} else {
				matrix.AppendLine ("-");
			}
			matrix.AppendLine ("</td>");

			// summary
			matrix.AppendLine ("<td>");
			matrix.AppendLine (step.summary);
			matrix.AppendLine ("</td>");

			// files
			matrix.AppendLine ("<td>");
			bool did_first = false;
			foreach (DBWorkFileView file in files) {
				if (file.hidden)
					continue;
				if (file.hidden)
					continue;
				if (did_first)
					matrix.Append (", ");
				matrix.AppendFormat ("<a href='GetFile.aspx?id={0}'>{1}</a> ", file.id, file.filename);
				did_first = true;
			}
			matrix.AppendLine ("</td>");

			// host
			matrix.AppendFormat ("<td>{0}</td>", step.workhost);

			matrix.AppendLine ("</tr>");
		}
		matrix.AppendLine ("</table>");

		return matrix.ToString ();
	}
Example #6
0
		public GetViewLaneDataResponse GetViewLaneData2 (WebServiceLogin login, int? lane_id, string lane, int? host_id, string host, int? revision_id, string revision, bool include_hidden_files)
		{
			GetViewLaneDataResponse response = new GetViewLaneDataResponse ();

			using (DB db = new DB ()) {
				Authenticate (db, login, response);

				response.Now = db.Now;
				response.Lane = FindLane (db, lane_id, lane);
				response.Host = FindHost (db, host_id, host);
				response.Revision = FindRevision (db, revision_id, revision);
				response.RevisionWork = DBRevisionWork_Extensions.Find (db, response.Lane, response.Host, response.Revision);
				if (response.RevisionWork != null && response.RevisionWork.workhost_id.HasValue) {
					response.WorkHost = FindHost (db, response.RevisionWork.workhost_id, null);
				}
				response.WorkViews = db.GetWork (response.RevisionWork);
				response.WorkFileViews = new List<List<DBWorkFileView>> ();
				for (int i = 0; i < response.WorkViews.Count; i++) {
					response.WorkFileViews.Add (DBWork_Extensions.GetFiles (db, response.WorkViews [i].id, include_hidden_files));
				}
			}

			return response;
		}
Example #7
0
	public string GenerateLane (GetViewLaneDataResponse response)
	{
		StringBuilder matrix = new StringBuilder ();
		List<DBWorkView2> steps = response.WorkViews;
		DBRevision dbr = response.Revision;
		DBRevisionWork revisionwork = response.RevisionWork;
		DBLane lane = response.Lane;
		DBHost host = response.Host;
		DBRevision revision = response.Revision;

		StringBuilder header = new StringBuilder ();
		header.AppendFormat ("Revision: {0}", CalculateDiffLink (lane.repository, dbr.revision));
		header.AppendFormat (" - Status: {0}", revisionwork.State);
		header.AppendFormat (" - Author: {0}", dbr.author);
		header.AppendFormat (" - Commit date: {0}", dbr.date.ToString ("yyyy/MM/dd HH:mm:ss UTC"));

		if (Authentication.IsInRole (response, Roles.Administrator)) {
			bool hidden = IsBranchProtected(lane);
			bool isExecuting = revisionwork.State == DBState.Executing || (revisionwork.State == DBState.Issues && !revisionwork.completed) || revisionwork.State == DBState.Aborted;
			if (isExecuting) {
				GenerateActionLink(header, lane, host, dbr, "clearrevision", "clear", "reset work");
				GenerateActionLink(header, lane, host, dbr, "deleterevision", "delete", "delete work");
				GenerateActionLink(header, lane, host, dbr, "abortrevision", "abort", "abort work");
			} else if (response.RevisionWork.State == DBState.Ignore) {
				header.AppendFormat (" - <a href='ViewLane.aspx?lane_id={0}&amp;host_id={2}&amp;revision_id={1}&amp;action=clearrevision'>build this revision</a>", lane.id, dbr.id, host.id);
			} else {
				if (response.RevisionWork.State == DBState.NotDone)
					header.AppendFormat (" - <a href='ViewLane.aspx?lane_id={0}&amp;host_id={2}&amp;revision_id={1}&amp;action=ignorerevision'>don't build</a>", lane.id, dbr.id, host.id);
				if (response.RevisionWork.State != DBState.NoWorkYet) {
					GenerateActionLink(header, lane, host, dbr, "clearrevision", "clear", "reset work", hidden);
					GenerateActionLink(header, lane, host, dbr, "deleterevision", "delete", "delete work", hidden);
				}
			}
		}

		if (response.WorkHost != null) {
			header.AppendFormat (" - Assigned to <a href='ViewHostHistory.aspx?host_id={1}'>{0}</a>", response.WorkHost.host, response.WorkHost.id);
		} else {
			header.AppendFormat (" - Unassigned.");
		}

		if (!revisionwork.completed && revisionwork.State != DBState.NotDone && revisionwork.State != DBState.Paused && revisionwork.State != DBState.Ignore) {
			header.Insert (0, "<center><table class='executing'><td>");
			header.Append ("</td></table></center>");
		}

		// matrix.AppendFormat ("<div class='buildstatus {0}'>Status: {0}</div>", revisionwork.State.ToString ().ToLowerInvariant ());

		matrix.AppendLine ("<table class='buildstatus'>");

		matrix.AppendFormat ("<tr class='{0}'>", revisionwork.State.ToString ().ToLowerInvariant ());
		matrix.Append ("<th colspan='9'>");
		matrix.Append (header.ToString ());
		matrix.Append ("</th>");
		matrix.AppendLine ("</tr>");

		matrix.AppendLine ("<tr>");
		matrix.AppendLine ("\t<th>Step</th>");
		matrix.AppendLine ("\t<th>Result</th>");
		matrix.AppendLine ("\t<th>Start Time</th>");
		matrix.AppendLine ("\t<th>Duration</th>");
		matrix.AppendLine ("\t<th>Html report</th>");
		matrix.AppendLine ("\t<th>Summary</th>");
		matrix.AppendLine ("\t<th>Files</th>");
		matrix.AppendLine ("\t<th>Host</th>");
		matrix.AppendLine ("\t<th>Misc</th>");
		matrix.AppendLine ("</tr>");

		bool failed = false;
		for (int s = 0; s < steps.Count; s++) {
			DBWorkView2 step = steps [s];
			DBState state = (DBState) step.state;
			bool nonfatal = step.nonfatal;
			string command = step.command;
			List<DBWorkFileView> files = response.WorkFileViews [s];
			IEnumerable<DBFileLink> links = response.Links.Where<DBFileLink> ((DBFileLink link) => link.work_id == step.id);

			if (state == DBState.Failed && !nonfatal)
				failed = true;

			matrix.AppendLine ("<tr>");

			// step
			DBWorkFileView step_log = files.Find ((v) => v.filename == command + ".log");
			matrix.Append ("\t<td>");
			if (step_log != null) {
				matrix.AppendFormat ("<a href='GetFile.aspx?id={0}'>{1}</a> ", step_log.id, command);
			} else {
				matrix.Append (command);
			}
			matrix.Append ("</td>");

			// result
			string result;
			switch (state) {
			case DBState.NotDone:
				result = failed ? "skipped" : "queued"; break;
			case DBState.Executing:
				result = "running"; break;
			case DBState.Failed:
				result = nonfatal ? "issues" : "failure"; break;
			case DBState.Success:
			case DBState.Aborted:
			case DBState.Timeout:
			case DBState.Paused:
			default:
				result = state.ToString ().ToLowerInvariant ();
				break;
			}

			// result
			matrix.AppendFormat ("\t<td class='{0}'>{0}</td>", result);

			if (state > DBState.NotDone && state != DBState.Paused && state != DBState.Ignore && state != DBState.DependencyNotFulfilled) {
				matrix.AppendFormat ("<td>{0}</td>", step.starttime.ToString ("yyyy/MM/dd HH:mm:ss UTC"));
			} else {
				matrix.AppendLine ("<td>-</td>");
			}
			// duration
			matrix.Append ("\t<td>");
			if (state >= DBState.Executing && state != DBState.Paused && state != DBState.Ignore && state != DBState.DependencyNotFulfilled && state != DBState.Aborted) {
				matrix.Append ("[");
				matrix.Append (MonkeyWrench.Utilities.GetDurationFromWorkView (step).ToString ());
				matrix.Append ("]");
			} else {
				matrix.Append ("-");
			}
			matrix.AppendLine ("</td>");

			// html report
			matrix.AppendLine ("<td>");
			DBWorkFileView index_html = null;

			foreach (DBWorkFileView file in files) {
				if (file.filename == "index.html") {
					index_html = file;
					break;
				}
			}

			if (index_html != null) {
				matrix.AppendFormat ("<a href='ViewHtmlReportEmbedded.aspx?workfile_id={0}&lane_id={1}&host_id={2}&revision_id={3}'>View html report</a>", index_html.id, lane.id, host.id, revision.id);
			} else {
				matrix.AppendLine ("-");
			}
			matrix.AppendLine ("</td>");

			// summary
			matrix.AppendLine ("<td>");
			matrix.AppendLine (step.summary);
			matrix.AppendLine ("</td>");

			// files
			matrix.AppendLine ("<td style='text-align: left;'>");
			var sb = new StringBuilder ();
			var file_count = 0;
			foreach (DBWorkFileView file in files.Where ((v) => !v.hidden).OrderBy ((v) => v.filename)) {
				if (file.hidden)
					continue;
				file_count++;
				sb.AppendFormat ("<a href='GetFile.aspx?id={0}'>{1}</a> <br /> ", file.id, file.filename);
			}

			foreach (var link in links.OrderBy ((v) => v.link)) {
				file_count++;
				sb.Append (link.link).Append ("<br />");
			}

			if (file_count > 3) {
				matrix.AppendFormat (
				"<a href='#' id='showFiles_{0}' onclick='javascript:document.getElementById (\"files_{0}\").style.display = \"block\"; document.getElementById (\"showFiles_{0}\").style.display = \"none\"; document.getElementById (\"hideFiles_{0}\").style.display = \"block\";'>Show {2} files</a>" +
				"<a href='#' id='hideFiles_{0}' onclick='javascript:document.getElementById (\"files_{0}\").style.display = \"none\"; document.getElementById (\"showFiles_{0}\").style.display = \"block\"; document.getElementById (\"hideFiles_{0}\").style.display = \"none\";' style='display: none'>Hide {2} files</a>" +
				"<span id='files_{0}' style='display: none'>{1}</span>",
					step.id, sb.ToString (), file_count);
			} else {
				matrix.Append (sb.ToString ());
			}
			matrix.AppendLine ("</td>");

			// host
			matrix.AppendFormat ("<td>{0}</td>", step.workhost);

			// misc
			matrix.AppendFormat ("\t<td><a href='ViewWorkTable.aspx?lane_id={1}&amp;host_id={2}&amp;command_id={3}'>History for '{4}'</a></td>", result, lane.id, host.id, step.command_id, command);

			matrix.AppendLine ("</tr>");
		}
		matrix.AppendLine ("</table>");

		return matrix.ToString ();
	}