The Blog

When I was a kid, I remember my doctor really taking care of me. There were just things that doctors did that were expected of them, such as:

  • If a wound was dirty, the doctor would clean it
  • If medicine needed to be prescribed, and the doctor’s office had some, they would administer the first dose in the office
  • If a sibling looked to be suffering from the same ailment, they wouldn’t have to be setup with a separate formal visit
  • Multiple prescriptions might have been given at the same visit (e.g. “try this first, if it doesn’t work then try this”), saving multiple office visits

After taking my son in to be looked at for Pink Eye, I was amazed at how much things have changed.

We didn’t clean his eyes before the visit, to make sure that the doctor saw the discharge. She looked at his eyes which were all goopy, and didn’t even clean them out when she was done.

I guess they didn’t have any eye drops on hand to offer the first dose onsite, but even if they had, they wouldn’t have done it anyway.

His sister’s eyes were also starting to get goopy, and it was a good bet at the time that she got what her brother had. Of course, the doctor couldn’t offer any suggestions for her, she needed a separate visit because “she was not in the system yet”. Along those lines, she told us that his sister couldn’t use his drops because she was under a year old, and they typically use an ointment instead of drops. I guess I just wondered why she couldn’t have prescribed both meds for him, and let us use the ointment on her, since she was clearly progressing along the same lines as her brother.

I don’t know what the reasons are, maybe it’s fear of lawsuits, maybe it’s financially motivated to increase office visits, maybe it’s just a shift in expectations that’s occurred as a result of limited time and budgets. In any case, it ain’t what it used to be.

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.