prod@blog:~$

Adding a Second Button: Extending the Task Runner with Data Cleanup

After deploying the SARC Task Runner, I thought to myself: "Can I add a button to run the data purge task?" Looking at the Windows Task Scheduler, I could see there was indeed another scheduled task in the \SARC-Auto folder called Purge-Data that cleans up old uploaded files to maintain storage space.

This seemed like a simple addition - I already had working code for one button, so adding a second should be straightforward. However, since this task deletes files (even if they're just old uploads that have already been processed), I figured it would be good practice to add a confirmation dialog.

Visual Results 

This is the updated interface with the new button for Purge Data: 


This is then the confirmation button before the task runs:


Checking the Available Tasks

First, I verified what scheduled tasks were available:

schtasks /Query /FO LIST | findstr "SARC"

This showed me:

  • \SARC-Auto\SARC-Task - The existing monitor task
  • \SARC-Auto\Purge-Data - The cleanup task I needed to add

Both tasks were in the same folder and had similar configurations, which made integration simpler.

Refactoring for Multiple Tasks

My original code was written for a single task. Rather than duplicate all the execution logic, I decided to refactor it into a reusable method:

private void ExecuteTask(string taskPath, string taskDisplayName, string taskDescription)
{
    try
    {
        ProcessStartInfo psi = new ProcessStartInfo();
        psi.FileName = "schtasks.exe";
        psi.Arguments = "/run /tn \"" + taskPath + "\"";
        psi.RedirectStandardOutput = true;
        psi.RedirectStandardError = true;
        psi.UseShellExecute = false;
        psi.CreateNoWindow = true;
        
        Process process = Process.Start(psi);
        string output = process.StandardOutput.ReadToEnd();
        string errors = process.StandardError.ReadToEnd();
        process.WaitForExit();
        
        if (process.ExitCode == 0)
        {
            Result.InnerHtml = "<div id='successMsg' class='success-message'>" +
                              "<strong>✓ " + taskDisplayName + " Running</strong><br/>" +
                              taskDescription + " has been started successfully." +
                              "</div>";
            
            LogTaskRun(User.Identity.Name, taskPath, true, taskDisplayName + " started successfully");
        }
    }
}

This meant each button's click handler became a simple one-liner:

protected void RunTaskButton_Click(object sender, EventArgs e)
{
    ExecuteTask(@"\SARC-Auto\SARC-Task", "SARC Monitor", "SARC monitoring task");
}

protected void PurgeDataButton_Click(object sender, EventArgs e)
{
    ExecuteTask(@"\SARC-Auto\Purge-Data", "Purge Data", "Data cleanup task");
}

Adding a Confirmation Dialog

Since the purge task removes files, I added a simple JavaScript confirmation. It's not that the operation is particularly risky (these are old files that have already been processed), but it's good practice to confirm any deletion operation:

<asp:Button ID="PurgeDataButton" runat="server" 
            Text="Purge Data" 
            CssClass="run-button purge-button"
            OnClick="PurgeDataButton_Click"
            OnClientClick="return confirm('Are you sure you want to purge old data?');" />

The OnClientClick runs first. If the user clicks Cancel, the server-side code never executes. Simple and effective.

Creating Visual Distinction

I wanted users to be able to quickly distinguish between the two operations. The monitor task is routine maintenance that runs regularly, while the purge task is more of a cleanup operation. I used different colors to make this clear:

/* Green for the regular monitor task */
.run-button {
    background: #28a745;
    color: white;
}

/* Red/orange for the cleanup task */
.purge-button {
    background: #dc3545;
}

.purge-button:hover:not(:disabled) {
    background: #c82333;
}

Implementing Independent Cooldowns

Both buttons needed their 120-second cooldown timers, but they had to work independently. If someone runs the monitor task, they might still need to run the purge task without waiting:

var sarcTimer;
var purgeTimer;
var sarcRemaining = 0;
var purgeRemaining = 0;

function startSarcCooldown() {
    var btn = document.getElementById('RunTaskButton');
    btn.disabled = true;
    sarcRemaining = 120;
    updateSarcCountdown();
    sarcTimer = setInterval(updateSarcCountdown, 1000);
}

function startPurgeCooldown() {
    var btn = document.getElementById('PurgeDataButton');
    btn.disabled = true;
    purgeRemaining = 120;
    updatePurgeCountdown();
    purgeTimer = setInterval(updatePurgeCountdown, 1000);
}

After a postback, I needed to determine which button was clicked to start the right cooldown:

window.onload = function() {
    var successMsg = document.getElementById('successMsg');
    if (successMsg) {
        // Check which task ran based on the success message
        if (successMsg.innerHTML.indexOf('SARC Monitor') > -1) {
            startSarcCooldown();
        } else if (successMsg.innerHTML.indexOf('Purge Data') > -1) {
            startPurgeCooldown();
        }
    }
}

Updating the Logging

I updated the logging to include which task was executed, making the audit trail clearer:

private void LogTaskRun(string user, string taskName, bool success, string message)
{
    string logEntry = string.Format("{0} | {1} | {2} | {3} | {4}",
        DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
        user,
        taskName,  // Now includes the specific task
        success ? "SUCCESS" : "FAILED",
        message);
        
    File.AppendAllText(logFile, logEntry);
}

The log now clearly shows which operation was performed:

2024-01-06 14:23:15 | STWATER\lcrouc2 | \SARC-Auto\SARC-Task | SUCCESS | SARC Monitor started successfully
2024-01-06 14:25:43 | STWATER\lcrouc2 | \SARC-Auto\Purge-Data | SUCCESS | Purge Data started successfully

The Updated Interface

The final interface presents both tasks in a clean card layout:

<div class="button-group">
    <div class="task-button">
        <h3>📁 SARC Monitor</h3>
        <p>Process new SARC requests and upload to OneDrive</p>
        <asp:Button ID="RunTaskButton" runat="server" 
                    Text="Start SARC Monitor" 
                    CssClass="run-button"
                    OnClick="RunTaskButton_Click" />
        <div id="sarcCountdown" class="countdown"></div>
    </div>
    
    <div class="task-button">
        <h3>🗑️ Purge Data</h3>
        <p>Clean up old processed files to free up storage</p>
        <asp:Button ID="PurgeDataButton" runat="server" 
                    Text="Purge Data" 
                    CssClass="run-button purge-button"
                    OnClick="PurgeDataButton_Click"
                    OnClientClick="return confirm('Are you sure you want to purge old data?');" />
        <div id="purgeCountdown" class="countdown"></div>
    </div>
</div>

Testing the Implementation

Before deploying, I tested a few scenarios:

  1. The confirmation dialog - Clicking the purge button shows the confirm dialog
  2. Canceling the operation - Clicking "No" prevents the task from running
  3. Independent cooldowns - Each button's timer works independently
  4. Logging accuracy - The log file correctly records which task was executed

Conclusion

What started as adding "just another button" became a nice exercise in making the interface more flexible and maintainable. The purge button works exactly like the monitor button technically - both just trigger scheduled tasks. But by adding a confirmation dialog and visual distinction, users get appropriate context for what each operation does.

The confirmation dialog for the purge operation isn't because it's particularly risky - it's just good UX practice. When an operation removes files (even old ones that are no longer needed), it's worth confirming the user actually meant to click that button. It takes half a second to click "OK" but saves potential confusion if someone clicked the wrong button.