The Blog

While working on a Timer Job project, I needed a flexible way to store one-time configuration settings. I was using a SharePoint Feature and FeatureReceiver to install and activate the Timer Job, so I decided to use the Feature.XML file as a repository for the initial configuration settings Job.

I used the Properties node of the feature.xml file to store the settings:

<Feature xmlns="http://schemas.microsoft.com/sharepoint/"...>
  <Properties xmlns="http://schemas.microsoft.com/sharepoint/">
    <!-- The name of the timer job (will appear in Central Admin). -->
    <Property Key="JobTitle" Value="Your Timer Job Name Here"/>
    <!-- Connection String that the TimerJob will use -->
    <Property Key="ConnString" Value="ConnectionString..."/>
    <!-- The schedule to run the job in (24 hour format) -->
    <Property Key="Schedule" Value="daily at 02:00:00"/>
  </Properties>
</Feature>

Then, during the FeatureActivated event, I passed those values into the Properties bag on the Timer Job class:

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{

    // Get all the properties from the feature.xml file
    string jobTitle = properties.Feature.Properties["JobTitle"].Value;
    string connString = properties.Feature.Properties["ConnString"].Value;
    string dailySchedule = properties.Feature.Properties["Schedule"].Value;

    // Create the job.
    CustomTimerJob customTimerJob = new CustomTimerJob((SPWebApplication)properties.Feature.Parent, jobTitle);

    // Set the properties for the job to run properly
    customTimerJob.Properties.Add("ConnString", connString);
    customTimerJob.Properties.Add("Schedule", dailySchedule);

    // Set the schedule
    SPSchedule mainSchedule = SPSchedule.FromString(dailySchedule);
    customTimerJob.Schedule = mainSchedule;

    // Activate the schedule
    customTimerJob.Update();

}

Then, in the Timer Job’s Execute method, grabbed those settings and used them:

public override void Execute(Guid targetInstanceId)
{
    // Set up configuration values
    string connString = this.Properties["ConnString"] as string;

    ...
}

I didn’t have a need to make run-time changes to the configuration settings, so this worked out really well. To change the defaults and apply new settings, the process was as simple as:

  • Deactivate the feature (uninstalls the Timer Job)
  • Change the feature.xml file
  • Re-activate the feature

I enjoy working with the ASP.NET Wizard control – it’s one of the more useful controls available, and there always seems to be a place for a wizard in the custom apps I’ve written. Over time I’ve developed a helper method that makes it easier to do non-linear jumps from step to step.

GetIndexFromStep(WizardStepBase step)

Often, in a NextButtonClick event handler for a Wizard, you need to figure out what the current step is that you are on, based on the WizardNavigationEventArgs e.CurrentStepIndex property. Here is a helper method to enable you to do that:

/// <summary>
/// Returns the index of a particular wizard step in the WizardSteps collection
/// </summary>
/// <param name="step">The step whose index number you want</param>
/// <returns></returns>
private int GetIndexFromStep(WizardStepBase step)
{
    return step.Wizard.WizardSteps.IndexOf(step);
}

You can also package it up as an extension method (.NET 3.5) off of the Wizard or WizardStepBase object:

using System.Web.UI.WebControls;
 
/// <summary>
/// Extensions for ASP.NET Wizard Control Objects
/// </summary>
public static class WizardExtensions
{
    
    /// <summary>
    /// Gets the index of a particular wizard step.
    /// </summary>
    /// <param name="wizard">The current Wizard</param>
    /// <param name="step">The step whose index number you want</param>
    /// <returns></returns>
    public static int GetIndexFromStep(this Wizard wizard, WizardStepBase step)
    {
        return wizard.WizardSteps.IndexOf(step);
    }
    /// <summary>
    /// Gets the index of this wizard step.
    /// </summary>
    /// <param name="step">This step</param>
    /// <returns></returns>
    public static int GetIndex(this WizardStepBase step)
    {
        return step.Wizard.WizardSteps.IndexOf(step);
    }
}

If you’ve given your wizard steps specific IDs, then you can use them strongly typed in a NextButtonClick event:

protected void Wizard1_NextButtonClick(object sender, WizardNavigationEventArgs e)
{
    if (e.CurrentStepIndex == GetIndexFromStep(step1))
    {
        // We are on step #1
 
        // Validate...
 
        // Skip to step 3
        Wizard1.ActiveStepIndex = GetIndexFromStep(step3);
 
    }
}

Recently I saw an access denied error when a user tried to edit a SharePoint Designer workflow task. The error did not occur if the user was a site collection administrator, but did occur even if they had full control to the site, list, and task list.

Running through the Request Access pages, it appeared that SharePoint did not think the user had rights to the task list, even though they did after thoroughly checking permissions.

I figured that it was having trouble with permissions to the ASPX pages from the Designer-generated tasks forms, so I solved it by doing the following:

  • Open the site in SharePoint designer
  • Locate the “Workflows” node in the tree view.
  • Right-click “Workflows” and select Properties.
  • Click the Security tab.
  • Choose the option to manage permissions from the browser.

It turns out that this “Workflows” node is actually a SharePoint folder object, and it turned out that this folder did not inherit permissions from the parent. The web page for managing permissions that came up showed me this, and enabled me to reinherit permissions and fix the problem.

I’m not sure why that folder didn’t inherit permissions though, I’d never seen that before.

After activating a custom SharePoint feature I created, the web application I activated it on stopped responding, and gave me a 403 forbidden error.

This link from MSDN (see the Notes section) helped me to identify that the NTFS permissions were screwed up on the feature folder and files (since I created them through windows explorer). Re-inheriting permissions from the parent “Features” folder solved the problem.