Expression Evaluation Time: ms / Round trip: ms
How to Build a Custom MatchPoint Timer Job
10. Aug
2011
by Matthias Weibel Category:
Technical
0

The MatchPoint Application Framework provides an easy way to create custom schedulable timer jobs which can be configured through the MatchPoint Configuration Framework. This allows configuring the custom timer job with the help of the configuration user interface. SharePoint timer jobs on the other hand are hardly configurable and have to be deployed by a custom feature and a feature receiver.

Therefore this blog post will demonstrate how to build a custom MatchPoint TimerJob with a custom configuration.

Goal

In this example we create a timer job that checks the tasks of a SharePoint task list and reminds the assigned users to complete their tasks by sending them an email. To fulfill this, the timer job looks for those tasks, which are not completed yet and are due soon. Then it sends an email to all users that are in charge of completing these tasks to remind them. In addition to the timer job we provide a corresponding configuration, in which the following properties are configurable:

  • the url of the task list, which the timer job is analyzing
  • the number of days before the due date of tasks, when the assigned user of a task should be reminded
  • the scheduling of the timer job

Implementation

In order to build this timer job two classes are needed: one implementing the timer job and one implementing the configuration.

Configuration

The configuration class of a schedulable timer job must extend the Colygon.MatchPoint.Core.TimerJobs.ScheduleTimerJobConfiguration class. Additionally, the configuration class is marked with the System.Serializable attribute, since it is serialized as a XML file, which is stored in the MatchPoint instance configuration library. In the configuration class we provide two field members:

  • TaskListUrl: a string value containing the URL of the task list, which is processed by the timer job.
  • RemindBeforeDays: an int value, defining the number of days before the due date of a task has expired.

In the end the configuration class looks as follow:

// Make it serializable, since it is serialized as a XML file.
[Serializable]
public class TaskReminderConfiguration : ScheduleTimerJobConfiguration
{
    // Specifiy the SPUrlEditor to simplify the specification of the URL in the configuration editor user interface.
    [CustomEditor(typeof(SPUrlEditor))]
    [SPUrlEditorBehavior(SPUrlType.List)]
    // Add a description which is visible in the configuration editor user interface.
    [MemberDescriptor("Specifies the URL of the task list, which is processed by the timer job.")]
    public string TaskListUrl;

    [MemberDescriptor("Specifies the number of days before the due date of a task.", true)]
    public int RemindBeforeDays;

    // Override this abstract method and return an instance of the TaskReminderTimerJob.
    public override ITimerJob GetTimerJob()
    {
        return new TaskReminderTimerJob();
    }
}

Timer Job

The timer job class must implement the Colygon.MatchPoint.Core.TimerJobs.ITimerJob interface in order to be executable. This interface requires just one method to be implemented:

void Run(TimerJobConfiguration configuration, DateTime lastRunTime, List<string> messages)

This Run method is called when the timer job is executed. The configuration parameter is a reference to the assigned configuration, which in our example is an instance of the TaskReminderConfiguration class. In the lastRunTime parameter the date of the last execution time of the timer job instance is stored. You can add success and failure messages to the message parameter, which are displayed in the user interface after the completion of the timer job.

In our implementation of the timer job, we first load the task list, which is specified in the configuration. To load this list, we use the GetList method of the Colygon.MatchPoint.Core.SPHelper class. This method eases the loading of a list and takes care of the disposing of the SharePoint API objects where it is required.

SPList taskList = SPHelper.GetList(reminderJobConfig.TaskListUrl);

After that the timer job executes a CAML query with the Colygon.MatchPoint.Common.Caml.ListCamlQuery on this task list. The query gets two conditions assigned to get the desired tasks. The first condition filters the tasks which are not yet completed. The second condition requests those tasks where the due date is within a specified time range, defined by the current date plus the specified RemindBeforeDays of the configuration.

ListCamlQuery query = new ListCamlQuery();
query.ViewFields = new HashSet<ViewField>();
query.ViewFields.Add(new ViewField("Title"));
query.ViewFields.Add(new ViewField("AssignedTo"));
query.ViewFields.Add(new ViewField("DueDate"));

query.Criteria = new CamlCriteriaGroup(LogicalOperator.And,
    new CamlCriteria("DueDate", SPFieldType.DateTime, 
        DateTime.Now.AddDays(reminderJobConfig.RemindBeforeDays), CamlComparisonOperator.Leq),
    new CamlCriteria("Status", SPFieldType.Choice, 
        "Completed", CamlComparisonOperator.Neq));

Executing the query will return the desired task list items. Then these list items are grouped by the user specified in the AssignedTo field of the list items.

Dictionary<string, List<SPListItem>> itemsPerUser = new Dictionary<string, List<SPListItem>>();
foreach (SPListItem item in query.Execute(taskList))
{
    string assignedTo = item["AssignedTo"] as string;
    if (assignedTo != null)
    {
        List<SPListItem> items;
        if (!itemsPerUser.TryGetValue(assignedTo, out items))
        {
            itemsPerUser[assignedTo] = items = new List<SPListItem>();
        }
        items.Add(item);
    }
}

Finally, after collecting the list items, an email to every user is sent to remind him about the outstanding tasks he has to complete by calling the following method with each entry of the created dictionary:

private static void SendEmail(string assignedTo, IEnumerable<SPListItem> items, SPWeb web)
{
    StringBuilder emailBody = new StringBuilder();
    emailBody.AppendLine("Hello!").AppendLine("Please complete the following tasks:");

    foreach (SPListItem item in items)
    {
        emailBody.Append("- ").Append(item.Title).Append(" until: ").AppendLine(Convert.ToString(item["DueDate"]));
    }

    StringDictionary headers = new StringDictionary();
    headers.Add("to", new SPFieldUserValue(web, assignedTo).User.Email);
    headers.Add("content-type", "text/plain");
    SPUtility.SendEmail(web, headers, emailBody.ToString());
}

Make sure that an email server is present in your SharePoint farm, to be able to successfully execute the SPUtility.SendEmail method.

Deployment

The assembly containing the two classes has to be signed and deployed to the global assembly cache on the servers in the farm. In the MatchPoint Configuration file of the MatchPoint instance, where the timer job needs to be executed, register the assembly in the ExternalAssemblies property by entering the fully qualified assembly name.

After the deployment, navigate to the "Manage Timer Jobs Form" of the MatchPoint instance site. The TaskReminderConfiguration appears in the drop down menu of the available timer job configurations. Select it and click on the "Add" button to create a new timer job configuration instance of the TaskReminderConfiguration. Configure the configuration at your needs and save it.

Now, the created TaskReminderTimerJob is listed in the "Manage Timer Jobs Form". Click on start in the corresponding line to run the timer job. Of course the timer job does not find any task items, unless you create some in the specified task list.

And that's it. I hope you've got an impression on how easy it is to create a fully customizable and configurable timer job with the MatchPoint Application Framework.

Download Source Code

Tags: Timer Job
Comments
ABOUT

This blog is about technical and non-technical aspects of the product MatchPoint and other SharePoint topics.

If you would like to post an article or if you have an idea for a post, please contact us.

ARCHIVE
COMMENTS
Matthias Weibel
09.04.2018 01:12
Link is updated and works now. | Goto Post
Dhanabalan
09.04.2018 12:21
Link doesn't work. Could anyone explain what does... | Goto Post
matthiaszbrun
14.03.2018 02:05
Hi Markus
We I use the config for SiteCollectionSe... | Goto Post
Reto Jeger
04.10.2017 09:15
Hello Reiner,
Thanks for pointing out the missing ... | Goto Post
rganser
29.09.2017 09:56
Hi, I downloaded the ZIP-file for MatchPoint Versi... | Goto Post