Take the community feedback survey now.

Praful Jangid
Dec 7, 2019
  3905
(4 votes)

Skip jobs to execute on specific day(s) of the week

Hi All,

Recently came across a requirement of skipping jobs execution on weekends. So, I thought why not allow editors to control all/any jobs to stop execute on specific days of week.

So, I took inspiration from @Paul's reply on this thread.

First, I created SelectionFactory for all existing jobs on StartPage (Home page), you can create this property on your setting item (as per your wish). Along with another property to select days of week to disable the jobs.

[Display(
    Name = "Scheduled jobs",
    Description = "Select jobs to stop execution on specific day",
    GroupName = Global.GroupNames.SiteSettings,
    Order = 1000)]
[SelectMany(SelectionFactoryType = typeof(ScheduledJobsSelectionFactory))]
public virtual string ScheduledJobs { get; set; }

[Display(
    Name = "Skip job execution on day(s)",
    Description = "Select days on which you don't want to execute jobs",
    GroupName = Global.GroupNames.SiteSettings,
    Order = 1010)]
[SelectMany(SelectionFactoryType = typeof(WeekDaysSelectionFactory))]
public virtual string SkipJobExecutionOnDays { get; set; }

Which something will look like this in our CMS (in below image).

[I selected some jobs and days to disable].

*I will add code of selection factories at the end

Next, our scheduled job that will scan all our jobs and check if their next execution is fallin on the skip day, then just updated next execution field and saved back. Here this code contains function to get HomePage.

[ScheduledPlugIn(DisplayName = "Skip JobExecution Scheduled Job")]
public class SkipJobExecutionScheduledJob : ScheduledJobBase
{
    private bool _stopSignaled;
    private readonly IContentLoader _contentLoader;
    private readonly IScheduledJobRepository _scheduledJobRepository;

    public SkipJobExecutionScheduledJob(
        IContentLoader contentLoader,
        IScheduledJobRepository scheduledJobRepository)
    {
        _contentLoader = contentLoader ?? throw new ArgumentNullException(nameof(contentLoader));
        _scheduledJobRepository = scheduledJobRepository ?? throw new ArgumentNullException(nameof(scheduledJobRepository));
        IsStoppable = true;
    }

    /// <summary>
    /// Called when a user clicks on Stop for a manually started job, or when ASP.NET shuts down.
    /// </summary>
    public override void Stop()
    {
        _stopSignaled = true;
    }

    /// <summary>
    /// Called when a scheduled job executes
    /// </summary>
    /// <returns>A status message to be stored in the database log and visible from admin mode</returns>
    public override string Execute()
    {
        //Call OnStatusChanged to periodically notify progress of job for manually started jobs
        OnStatusChanged($"Starting execution of {this.GetType()}");

        var sb = new StringBuilder();
        var homePage = this.GetHomePage();
        if (homePage == null) return $"Could not retrieve site settings";

        var jobs = homePage.ScheduledJobs?.Split(',').ToList();
        var jobToDisable = jobs?.Select(x => this._scheduledJobRepository.Get(Guid.Parse(x))).ToList()
               ?? new List<ScheduledJob>();
        var skipJobExecutionOnDays = homePage.SkipJobExecutionOnDays?.Split(',').ToList();
        if (jobToDisable.Count > 0 && skipJobExecutionOnDays != null && skipJobExecutionOnDays.Count > 0)
        {
            foreach (var job in jobToDisable)
            {
                if (skipJobExecutionOnDays.Contains(job.NextExecution.DayOfWeek.ToString()))
                {
                    job.NextExecution = job.NextExecution.AddDays(1);
                    this._scheduledJobRepository.Save(job);
                    OnStatusChanged($"Updated \"{job.Name}\" to run on \"{job.NextExecution}\"");
                    sb.AppendLine($"Updated \"{job.Name}\" to run on \"{job.NextExecution}\"");
                }

                //For long running jobs periodically check if stop is signaled and if so stop execution
                if (_stopSignaled)
                {
                    return "Stop of job was called";
                }
            }
        }

        //For long running jobs periodically check if stop is signaled and if so stop execution
        if (_stopSignaled)
        {
            return "Stop of job was called";
        }
        var rtn = sb.ToString();
        if (rtn == string.Empty)
        {
            rtn = "Job completed successfully. No updates required.";
        }
        return rtn;
    }

    private StartPage GetHomePage()
    {
        var pageLink = SiteDefinition.Current.StartPage;
        if (pageLink == null || pageLink == ContentReference.EmptyReference)
            return null;

        this._contentLoader.TryGet(pageLink, out PageData page);

        if (page == null)
            return null;

        return this.GetHomePage(page);
    }

    private StartPage GetHomePage(PageData page)
    {
        if (page == null)
            return null;

        if (page is StartPage homePage)
            return homePage;

        homePage = this._contentLoader
                .GetAncestors(page.ContentLink)
                .FirstOrDefault(x => x is StartPage)
            as StartPage;

        return homePage;
    }
}

In addition, I am adding selection factory code here.

public class ScheduledJobsSelectionFactory: ISelectionFactory
{
    public Injected<IScheduledJobRepository> ScheduledJobRepository { get; set; }

    public IEnumerable<ISelectItem> GetSelections(ExtendedMetadata metadata)
    {
        var scheduledJobs = this.ScheduledJobRepository.Service.List();

        var selections =
            scheduledJobs
                .Select(x => new SelectItem
                {
                    Text = x.Name,
                    Value = x.ID.ToString()
                })
                .ToList();

        return selections;
    }
}
public class WeekDaysSelectionFactory : ISelectionFactory
{
    public IEnumerable<ISelectItem> GetSelections(ExtendedMetadata metadata)
    {
        return Enum
            .GetNames(typeof(DayOfWeek))
            .Select(x => new SelectItem { Text = x, Value = x })
            .ToList();
    }
}

On a special note, you can add fields for time range to skip in that duration (Stop Execution Time and Start Execution Time).

Special Note: This implementation is more focused with single site implementation. If you have jobs that are site specific you can follow comment by @Antti (below in comments).

And to restrict the access you can follow this post.

Reach-out to me if you have any question or concern :)

Thanks & Regards

Praful Jangid

Happy Coding!

Dec 07, 2019

Comments

Antti Alasvuo
Antti Alasvuo Dec 7, 2019 07:38 PM

Hi Praful, nice proof of concept post.

Couple of improvements that come to my mind:

  • you should use constructor injection for the services instead of using the injected<T> properties, constructor injection is the preferred way
  • performance wise List<T>.Count property is better than using .Any() ;-)
  • the GetHomePage() and GetHomePage(PageData) should be private, these most likely should not be called from outside
  • if this code is used on a multisite, then the HomePage is fetched for the site having '*' host
    • refactor the code to use SiteDefinitionRepository to fetch all sites and then loop the sites to get the site specific settings
    • and I guess in that case the "skipping" should somehow contain more logic what should be done when more than one site has a setting to skip / not to skip as the jobs usually are not site specific, unless it is your custom job, and then wouldn't the better option be having the logic in the job
    • or just skip this and say, it is for single site setups :D
  • I wonder was the requirement that editors without Admin view access should be able to modify these settings?

Praful Jangid
Praful Jangid Dec 9, 2019 06:38 AM

Thanks Antti, for reading full code and giving your valuable feedback. I updated my code, used the constructor injection and made functions private with other suggestions too.

Initially I only thought, to provide the solution in the form of Scheduled job to skip the other jobs on specific day. So, I would suggest whoever implement/use this code into their solution should consider the suggetions provided by you.

Thanks and Regards

Praful :)

Please login to comment.
Latest blogs
Optimizely Opal — working notes from me

A short, practical orientation I use when introducing Opal. Lately I’ve been helping customers and developers understand Opal, and I kept finding...

Pär Wissmark | Nov 26, 2025 |

How to setup Entra ID (formerly Azure AD) for CMS 12 site

Recently, I created a doc about configuring AAD with CMS 12, but it has been posted internally only So today, I would like to introduce it again in...

Tuan Anh Hoang | Nov 25, 2025

Running Optimizely CMS 12 on .NET 10 in Azure

Upgrade your Optimizely CMS website to .NET 10!

Tomas Hensrud Gulla | Nov 21, 2025 |

Experimentation Evolution with AI (Masterclass Recap)

If you think you are not using AI in your experimentation program you are probably wrong. Ever asked an AI to rephrase a hypothesis or brainstorm a...

Polly Walton | Nov 21, 2025

Effortlessly Configurable Custom Fields for Scheduled Jobs in Optimizely with PlugInProperty

Optimizely CMS lets developers add job parameters—such as textboxes and checkboxes—directly to the scheduled job admin UI using PlugInProperty. Wit...

Ravindra S. Rathore | Nov 21, 2025 |

Optimizely Package Explorer: Now With Extra Superpowers

If you’ve ever opened a .episerverdata file and asked “What is in here?” (guilty as charged) — then this is your moment. We’ve given our open-sourc...

Allan Thraen | Nov 20, 2025 |