Skip to content

Instantly share code, notes, and snippets.

@nul800sebastiaan
Last active September 18, 2021 18:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save nul800sebastiaan/83c3da828a72c15a89259938402f2449 to your computer and use it in GitHub Desktop.
Save nul800sebastiaan/83c3da828a72c15a89259938402f2449 to your computer and use it in GitHub Desktop.
using System;
using Hangfire;
using Hangfire.Console;
using Hangfire.SqlServer;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Web.BackOffice.Authorization;
using Umbraco.Cms.Web.Common.ApplicationBuilder;
namespace Cultiv.Hangfire.Composers
{
public class HangfireComposer : IComposer
{
public void Compose(IUmbracoBuilder builder)
{
// Configure Hangfire to use our current database and add the option to write console messages
var connectionString = builder.Config.GetConnectionString(Umbraco.Cms.Core.Constants.System.UmbracoConnectionName);
builder.Services.AddHangfire(configuration =>
{
configuration
.SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
.UseSimpleAssemblyNameTypeSerializer()
.UseRecommendedSerializerSettings()
.UseConsole()
.UseSqlServerStorage(connectionString, new SqlServerStorageOptions
{
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
QueuePollInterval = TimeSpan.Zero,
UseRecommendedIsolationLevel = true,
DisableGlobalLocks = true,
});
});
// Run the required server so your queued jobs will get executed
builder.Services.AddHangfireServer();
// Add a named policy to authorize requests to the dashboard
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("HangfireDashboard", policy =>
{
// We require a logged in backoffice user who has access to the settings section
policy.AuthenticationSchemes.Add(Umbraco.Cms.Core.Constants.Security.BackOfficeAuthenticationType);
policy.Requirements.Add(new SectionRequirement(Umbraco.Cms.Core.Constants.Applications.Settings));
});
});
// Add the dashboard and make sure it's authorized with the named policy above
builder.Services.Configure<UmbracoPipelineOptions>(options =>
{
options.AddFilter(new UmbracoPipelineFilter("HangfireDashboard")
{
Endpoints = app => app.UseEndpoints(endpoints =>
{
endpoints.MapHangfireDashboard(
pattern: "/umbraco/backoffice/hangfire",
options: new DashboardOptions()).RequireAuthorization("HangfireDashboard");
}).UseHangfireDashboard()
});
});
// For some reason we need to give it the connection string again, else we get this error:
// https://discuss.hangfire.io/t/jobstorage-current-property-value-has-not-been-initialized/884
JobStorage.Current = new SqlServerStorage(connectionString);
// Now we can queue our jobs
var jobsQueue = new JobsQueue();
jobsQueue.ScheduleJobs();
}
}
}
@nul800sebastiaan
Copy link
Author

nul800sebastiaan commented Sep 18, 2021

The App_Plugin code is as follows, there's a directory in App_Plugins named Cultiv.Hangfire, containing a package.manifest:

{
    "dashboards":  [
        {
            "alias": "cultiv.Hangfire",
            "view":  "/App_Plugins/Cultiv.Hangfire/dashboard.html",
            "sections":  [ "settings" ]
        }
    ]
}

A dashboard.html with the iframe in it:

<style type="text/css">
    .hangfireWrapper {
        margin: -30px -20px;
    }

    .hangfireContent {
        position: absolute;
        width: 100%;
        height: 100%;
    }
</style>

<div class="hangfireWrapper">
    <iframe name="hangfireIframe" class="hangfireContent" id="Hangfire" frameborder="0" scrolling="yes" marginheight="0" marginwidth="0"
            src="/umbraco/backoffice/hangfire/" allowfullscreen="true" style="width:100%!important"
            webkitallowfullscreen="true" mozallowfullscreen="true"
            oallowfullscreen msallowfullscreen="true"></iframe>
</div>    

And in an extra directory called lang there's a en-US.xml with a simple translation in it:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<language>
    <area alias="dashboardTabs">
        <key alias="cultiv.Hangfire">Hangfire</key>
    </area>
</language>

@nul800sebastiaan
Copy link
Author

In case you're interested in the silly job as well, here's JobsQueue.cs

using System.Threading;
using Cultiv.Hangfire.Interfaces;
using Hangfire;
using Hangfire.Console;
using Hangfire.Server;

namespace Cultiv.Hangfire
{
    public class JobsQueue : IJobsQueue
    {
        public void ScheduleJobs()
        {
            RecurringJob.AddOrUpdate(() => DoIt(null), Cron.Hourly());
        }
        
        public void DoIt(PerformContext context)
        {
            // Do something!
            var progressBar =  context.WriteProgressBar();
            var items = new int[10]{ 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 };

            foreach (var item in items.WithProgress(progressBar, items.Length))
            {
                context.WriteLine($"Number: {item}");
                Thread.Sleep(1000);
            }
        }
    }
}

and IJobsQueue.cs

namespace Cultiv.Hangfire.Interfaces
{
    public interface IJobsQueue
    {
        void ScheduleJobs();
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment