This C# ASP.NET application demonstrates one way to implement automatic date adjustment. Both the code below and its associated “AdjustTaskDates.aspx” file should be installed and configured to run under the URL: http://[your SharePoint server] /_layouts/ custom/ AdjustTaskDates. Note that the parent .aspx file will not contain any web controls, as the end-user will never interact with this application. The AdjustTaskDates application will simply execute the following code, then immediately redirect the end-user’s browser back to the task list’s default page.
// These are the required .NET library
// references + Microsoft.SharePoint +
// Microsoft.SharePoint.WebControls. You
// will need to create a reference to the
// Windows.SharePoint.Services .NET library
// to include the last two
using
System;
using
System.Configuration;
using
System.Data;
using
System.Web;
using
System.Web.Security;
using
System.Web.UI;
using
System.Web.UI.HtmlControls;
using
System.Web.UI.WebControls;
using
System.Web.UI.WebControls.WebParts;
using
Microsoft.SharePoint;
using
Microsoft.SharePoint.WebControls;
public
partial class _Default : System.Web.UI.Page
{
// LAG_DAYS ensures that there will be
// at least one day delay between a
// predecessor and its successor task
const int LAG_DAYS = 1;
// This page assumes that the following querystring
// parameters have been passed:
//
// l = Guid (unique ID) of the task list
// id (optional) = integer uniqu ID of task to process
//
protected void Page_Load(object sender, EventArgs e)
{
// Get handle to web site and enable updates
SPWeb web = SPControl.GetContextWeb(Context);
web.AllowUnsafeUpdates = true;
// Get handle to task list to be processed
SPList tasks = web.Lists[new Guid(Request.QueryString["l"])];
SPQuery query = new SPQuery();
SPListItemCollection taskItems = tasks.Items;
// If this routine has been called from the
// item-level menu, get the ID of the task from
// which is was called
int taskId = 0;
try
{
if (Request.QueryString["id"] != null)
{
// Instead of processing all tasks, only process
// the specific task calling this routine
taskId = int.Parse(Request.QueryString["id"].ToString());
query.Query = “<Where><Eq><FieldRef Name=’ID’/>” +
“<Value Type=’Number’>” + taskId + “</Value></Eq></Where>”;
taskItems = tasks.GetItems(query);
}
}
catch
{}
// Start by clearing out the processing notes field, so
// that the only notes to be displayed after processing
// is complete will be for the current run
foreach (SPListItem task in taskItems)
{
if (task["Processing Notes"] != null && task["Processing Notes"].ToString() != “”)
{
task["Processing Notes"] = “”;
task.Update();
}
}
// Initialize working variables
bool datesChanged = true;
int overlap;
DateTime startDate;
DateTime dueDate;
// Loop until no more dates need to be changed
while (datesChanged)
{
// Clear flag that indicates whether
// any date changes were made
datesChanged = false;
// Loop through the task(s) list, changing any dates as necessary
foreach (SPListItem task in taskItems)
{
// The dependency behavior will control how dates
// are adjusted for the current task. Values are:
//
// Ignore Dependencies … no change
// No Earlier Than ……. only move if predecessor overlaps
// As Early as Possible .. compress dates if possible
string dependencyBehavior = “”;
try
{
dependencyBehavior = task["Dependency Behavior"].ToString();
}
catch { }
// Get the current task’s LAG DAYS, if it has not been set,
// use the default.
int lagDays = LAG_DAYS;
try
{
if (task["Lag Days"] != null)
{
lagDays = int.Parse(task["Lag Days"].ToString());
}
}
catch { }
// Skip current task if its dependency behavior is set
// to “Ignore Dependencies”
if (dependencyBehavior != “Ignore Dependencies”)
{
// Get any perdecessor tasks for the current task,
// note that this field can have multiple values
SPFieldLookupValueCollection predecessors =
(SPFieldLookupValueCollection)task["Predecessors"];
// Get dates of current task
startDate = (DateTime)task["Start Date"];
try
{
dueDate = (DateTime)task["Due Date"];
}
catch
{
// If due date has not been filled in, assume
// it’s the same as the start date
dueDate = startDate;
}
// If one or more predecessors exist, parse and check their end dates
DateTime maxPredecessorDueDate = DateTime.MinValue;
int maxPredecessorId = 0;
string maxPredecessorTitle = “”;
for (int i = 0; i < predecessors.Count; i++)
{
// Get handle to predecessor task
int id = predecessors[i].LookupId;
SPListItem predecessor = tasks.GetItemById(id);
// Get the due (end) date of the predecessor
DateTime predecessorDueDate = (DateTime)predecessor["Due Date"];
// Store the latest predecessor due date for use
// later if current task’s dependency behavior is
// set to “As Early as Possible”
if (predecessorDueDate > maxPredecessorDueDate)
{
maxPredecessorDueDate = predecessorDueDate;
maxPredecessorId = predecessor.ID;
maxPredecessorTitle = predecessor.Title;
}
// Calculate the current overlap of the predecessor due
// date and current task start date. Add in the lag days.
overlap = (predecessorDueDate – startDate).Days + lagDays;
// If predecessor overlaps with current task, move the
// current task out by the amount of the overlap
if (overlap > 0)
{
// Move the dependent task and set the processing
// comment
startDate = startDate.AddDays(overlap);
dueDate = dueDate.AddDays(overlap);
task["Start Date"] = startDate;
task["Due Date"] = dueDate;
task["Processing Notes"]
= string.Format(“Task moved to start no earlier than end of task
# {0}: \”{1}\”.”
,
predecessor.ID.ToString(),
predecessor.Title);
// Because dates were changed, will need to loop through
// tasks again, in case this change will impact other
// tasks dependent on the task that was just moved.
datesChanged = true;
}
}
// If dependency behavior is set to “As Early as Possible”, set
// start date of current task to be no later than latest predecessor
// plus lag days
overlap = (maxPredecessorDueDate – startDate).Days + lagDays;
if (dependencyBehavior == “As Early as Possible”
&& maxPredecessorDueDate != DateTime.MinValue && overlap < 0)
{
startDate = startDate.AddDays(overlap);
dueDate = dueDate.AddDays(overlap);
task["Start Date"] = startDate;
task["Due Date"] = dueDate;
task["Processing Notes"]
= string.Format(“Task moved to start no later than end of task # {0}:
\”{1}\”.”
,
maxPredecessorId.ToString(),
maxPredecessorTitle);
datesChanged = true;
}
}
// Save changes
task.Update();
}
}
// Return to the task list after processing has been completed
Response.Redirect(tasks.DefaultViewUrl);
}
}