Up until the release of Sitecore 7.2 Update-6 Sitecore was missing an out-of-the-box Jobs Viewer tool - which resulted in community developers rolling their own and sharing to the rest of us.
Here are links to some of the community-driven implementations:
https://www.geekhive.com/buzz/post/2015/04/sitecore-job-viewer/
https://briancaos.wordpress.com/2014/11/11/sitecore-job-viewer-see-what-sitecore-is-doing-behind-your-back/
https://marketplace.sitecore.net/en/Modules/V/View_Sitecore_Jobs.aspx
https://sitecoreblog.marklowe.ch/2014/06/view-running-sitecore-background-jobs/
One feature that separated Sitecore's Jobs.aspx page from the community-driven implementations was its ability to auto-refresh and essentially view jobs running in real-time without the need for a refresh button.
It also looked very nice! :)
I wanted to use THIS Jobs.aspx page on older versions of Sitecore I still maintained, so I started decompiling the Sitecore.Kernel, SitecoreClient, and Sitecore.ExperienceContentManagement.Administration binaries from v8.1 in an attempt to consolidate all the code into one Jobs.aspx page - no additional dll required.
Here's the result:
<%@ Page language="c#" EnableEventValidation="false" AutoEventWireup="true" Inherits="Sitecore.sitecore.admin.AdminPage" %>
<script runat="server">
protected override void OnInit(EventArgs e)
{
base.CheckSecurity(true); //Required!
base.OnInit(e);
}
void Page_Load(object sender, System.EventArgs e)
{
Sitecore.Jobs.JobManager.GetJobs();
StringBuilder stringBuilder = new StringBuilder();
Type type = typeof(Sitecore.Jobs.JobManager);
this.ShowRefreshStatus(stringBuilder);
System.Reflection.FieldInfo field = type.GetField("_runningJobs", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
if (field != null)
{
this.ShowJobs(stringBuilder, "Running jobs", ((Sitecore.Collections.SafeDictionary<Sitecore.Handle, Sitecore.Jobs.Job>)field.GetValue(null)).Values.ToArray<Sitecore.Jobs.Job>());
}
System.Reflection.FieldInfo fieldInfo = type.GetField("_queuedJobs", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
if (fieldInfo != null)
{
this.ShowJobs(stringBuilder, "Queued jobs", ((Sitecore.Collections.JobCollection)fieldInfo.GetValue(null)).ToArray<Sitecore.Jobs.Job>());
}
System.Reflection.FieldInfo field1 = type.GetField("_finishedJobs", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
if (field1 != null)
{
this.ShowJobs(stringBuilder, "Finished jobs", ((Sitecore.Collections.JobCollection)field1.GetValue(null)).Reverse<Sitecore.Jobs.Job>().ToArray<Sitecore.Jobs.Job>());
}
this.lt.Text = stringBuilder.ToString();
}
protected virtual void ShowJobs(StringBuilder stringBuilder, string name, ICollection<Sitecore.Jobs.Job> enumerable)
{
stringBuilder.AppendLine(string.Concat("<h1>", name, ":</h1><br />"));
if (enumerable.Count <= 0)
{
stringBuilder.AppendLine("<b>No jobs</b><br />");
}
else
{
stringBuilder.AppendLine("<table class='jobs-table'>");
stringBuilder.AppendLine("<thead><tr><td class='counter'>No</td><td class='add-time'>Added</td><td class='title'>Title</td><td class='progress'>Progress</td><td class='priority'>Priority</td></tr></thead>");
int num = 1;
foreach (Sitecore.Jobs.Job job in enumerable)
{
long total = job.Status.Total;
TimeSpan localTime = TimeSpan.Zero;
string str = (localTime.Hours == 0 ? string.Empty : string.Concat(localTime.Hours, "h "));
string str1 = (localTime.Minutes == 0 ? string.Empty : string.Concat(localTime.Minutes, "m "));
stringBuilder.AppendLine("<tr>");
stringBuilder.AppendLine(string.Concat("<td class='counter'>", num, "</td>"));
object[] longTimeString = new object[] { "<td class='add-time'>", null, null, null, null, null, null };
DateTime dateTime = job.QueueTime.ToLocalTime();
longTimeString[1] = dateTime.ToLongTimeString();
longTimeString[2] = " (";
longTimeString[3] = str;
longTimeString[4] = str1;
longTimeString[5] = localTime.Seconds;
longTimeString[6] = "s ago)</td>";
stringBuilder.AppendLine(string.Concat(longTimeString));
stringBuilder.AppendLine(string.Concat("<td class='title'>", job.Name, "</td>"));
StringBuilder stringBuilder1 = stringBuilder;
object[] processed = new object[] { "<td class='progress'>", job.Status.Processed
, null, null };
processed[2] = (total > (long)0 ? string.Concat(" of ", total) : string.Empty);
processed[3] = "</td>";
stringBuilder1.AppendLine(string.Concat(processed));
stringBuilder.AppendLine(string.Concat("<td class='priority'>", job.Options.Priority, "</td>"));
stringBuilder.AppendLine("</tr>");
num++;
}
stringBuilder.AppendLine("</table>");
}
stringBuilder.AppendLine("<br /><hr />");
}
protected virtual void ShowRefreshStatus(StringBuilder stringBuilder)
{
int num;
string str;
string item = base.Request.QueryString["refresh"];
int.TryParse(item, out num);
object[] objArray = new object[1];
DateTime now = DateTime.Now;
objArray[0] = now.ToString(System.Globalization.CultureInfo.InvariantCulture);
stringBuilder.Append(Sitecore.StringExtensions.StringExtensions.FormatWith("Last updated: {0}. ", objArray));
int[] numArray = new int[] { 1, 2, 5, 10, 20, 30, 60 };
stringBuilder.Append(string.Format("Refresh each <a href='jobs.aspx' class='refresh-link {0}'>No Refresh</a>", (num == 0 ? "refresh-selected" : string.Empty)));
int[] numArray1 = numArray;
for (int i = 0; i < (int)numArray1.Length; i++)
{
int num1 = numArray1[i];
str = (num == num1 ? "refresh-selected" : string.Empty);
string str1 = string.Format(", <a href='jobs.aspx?refresh={0}' class='refresh-link {1}'>{0} sec</a>", num1, str);
stringBuilder.Append(str1);
}
stringBuilder.Append("<br /><br />");
}
</script>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Jobs Viewer</title>
<link rel="Stylesheet" type="text/css" href="/sitecore/shell/themes/standard/default/WebFramework.css" />
<link rel="Stylesheet" type="text/css" href="./default.css" />
<style type="text/css">
.jobs-table {
border: solid 1px grey;
border-spacing: 2px;
border-collapse: separate;
width: 100%;
}
.jobs-table td {
padding: 2px;
}
.jobs-table thead {
font-weight: bold;
}
.jobs-table .counter {
width: 25px;
text-align: right;
}
.jobs-table .add-time {
width: 150px;
}
.jobs-table .title {
word-break: break-all;
}
.jobs-table .progress {
width: 50px;
text-align: center;
}
.jobs-table .priority {
width: 80px;
}
</style>
</head>
<body>
<form id="Form1" runat="server" class="wf-container">
<div class="wf-content">
<h1>
<a href="/sitecore/admin/">Administration Tools</a> - Jobs Viewer
</h1>
<br />
<asp:Literal runat="server" ID="lt"></asp:Literal>
<script type="text/javascript">
function getQueryString() {
var result = {}, queryString = location.search.substring(1), re = /([^&=]+)=([^&]*)/g, m;
while (m = re.exec(queryString)) {
result[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
}
return result;
}
var str = getQueryString()["refresh"];
if (str != undefined) {
c = parseInt(str) * 1000;
setTimeout("document.location.href = document.location.href;", c);
}
</script>
</div>
</form>
</body>
</html>
Here is the Sitecore's Jobs Viewer admin tool working on a clean Sitecore 7.0 site:
Simply copy the code above into a new Jobs.aspx file and drop it into your site's /sitecore/admin folder.
I've confirmed this working on versions as far back as Sitecore versions 6.6.
Enjoy!