diff --git a/lib/WebGUI/Asset/Wobject/ProjectManager.pm b/lib/WebGUI/Asset/Wobject/ProjectManager.pm
new file mode 100644
index 000000000..120707aec
--- /dev/null
+++ b/lib/WebGUI/Asset/Wobject/ProjectManager.pm
@@ -0,0 +1,1186 @@
+package WebGUI::Asset::Wobject::ProjectManager;
+
+$VERSION = "1.0.0";
+
+#-------------------------------------------------------------------
+# WebGUI is Copyright 2001-2006 Plain Black Corporation.
+#-------------------------------------------------------------------
+# Please read the legal notices (docs/legal.txt) and the license
+# (docs/license.txt) that came with this distribution before using
+# this software.
+#-------------------------------------------------------------------
+# http://www.plainblack.com info@plainblack.com
+#-------------------------------------------------------------------
+
+use strict;
+use DateTime;
+use Tie::IxHash;
+use WebGUI::International;
+use WebGUI::Utility;
+use POSIX qw(ceil floor);
+use base 'WebGUI::Asset::Wobject';
+
+#-------------------------------------------------------------------
+sub _addDaysForMonth {
+ my $self = shift;
+ my $dt = $self->session->datetime;
+ my $eh = $self->session->errorHandler;
+
+ my $days_loop = $_[0];
+ my $hash = $_[1];
+ my $month = $_[2];
+
+ my ($monthStart,$monthEnd) = $dt->monthStartEnd($month);
+ my $dayOfWeek = $dt->getDayOfWeek($monthStart);
+ my $mondayAdjust = (7 - ($dayOfWeek-1)) % 7;
+
+ my $firstMonday = $dt->addToDateTime($monthStart,0,0,$mondayAdjust,1);
+
+ #This line of code just makes things easier to read
+ my $colCount = 0;
+ my $monday = $firstMonday;
+ while ($monday < $monthEnd) {
+ my $hash = {};
+ $hash->{'day.number'} = $dt->epochToHuman($monday,"%d");
+ #$eh->warn($hash->{'day.number'});
+ $monday += 604800; # Add one week to the first monday of the month
+ push(@{$days_loop},$hash);
+ $colCount++;
+ }
+ $hash->{'month.colspan'} = $colCount;
+ #$eh->warn($dt->epochToHuman($firstMonday));
+}
+
+#-------------------------------------------------------------------
+sub _getDurationUnitHash {
+ my $self = shift;
+ my ($session,$privilege,$form,$db,$dt,$i18n,$user) = $self->setSessionVars;
+
+ tie my %hash, "Tie::IxHash";
+ %hash = ( "hours"=>$i18n->get("hours label"), "days"=>$i18n->get("days label") );
+
+ return \%hash;
+}
+
+#-------------------------------------------------------------------
+sub _getDurationUnitHashAbbrev {
+ my $self = shift;
+ my ($session,$privilege,$form,$db,$dt,$i18n,$user) = $self->setSessionVars;
+
+ tie my %hash, "Tie::IxHash";
+ %hash = ( "hours"=>$i18n->get("hours label abbrev"), "days"=>$i18n->get("days label abbrev") );
+
+ return \%hash;
+}
+
+#-------------------------------------------------------------------
+sub definition {
+ my $class = shift;
+ my $session = shift;
+ my $definition = shift;
+ my $i18n = WebGUI::International->new($session,'ProjectManager');
+ my %properties;
+ tie %properties, 'Tie::IxHash';
+ %properties = (
+ projectDashboardTemplateId =>{
+ fieldType=>"template",
+ defaultValue=>'ProjectManagerTMPL0001',
+ tab=>"display",
+ namespace=>"ProjectManager_dashboard",
+ hoverHelp=>$i18n->get('projectDashboardTemplate hoverhelp'),
+ label=>$i18n->get('projectDashboardTemplate label')
+ },
+ projectDisplayTemplateId => {
+ fieldType=>"template",
+ defaultValue=>'ProjectManagerTMPL0002',
+ tab=>"display",
+ namespace=>"ProjectManager_project",
+ hoverHelp=>$i18n->get('projectDisplayTemplate hoverhelp'),
+ label=>$i18n->get('projectDisplayTemplate label')
+ },
+ ganttChartTemplateId => {
+ fieldType=>"template",
+ defaultValue=>'ProjectManagerTMPL0003',
+ tab=>"display",
+ namespace=>"ProjectManager_gantt",
+ hoverHelp=>$i18n->get('ganttChartTemplate hoverhelp'),
+ label=>$i18n->get('ganttChartTemplate label')
+ },
+ editTaskTemplateId =>{
+ fieldType=>"template",
+ defaultValue=>'ProjectManagerTMPL0004',
+ tab=>"display",
+ namespace=>"ProjectManager_editTask",
+ hoverHelp=>$i18n->get('editTaskTemplate hoverhelp'),
+ label=>$i18n->get('editTaskTemplate label')
+ },
+ groupToAdd => {
+ fieldType=>"group",
+ defaultValue=>3,
+ tab=>"security",
+ hoverHelp=>$i18n->get('groupToAdd hoverhelp'),
+ label=>$i18n->get('groupToAdd label')
+ }
+ );
+ push(@{$definition}, {
+ assetName=>$i18n->get('assetName'),
+ icon=>'projManagement.gif',
+ autoGenerateForms=>1,
+ tableName=>'PM_wobject',
+ className=>'WebGUI::Asset::Wobject::ProjectManager',
+ properties=>\%properties
+ });
+ return $class->SUPER::definition($session, $definition);
+}
+
+
+#-------------------------------------------------------------------
+sub duplicate {
+ my $self = shift;
+ my $newAsset = $self->SUPER::duplicate(shift);
+ return $newAsset;
+}
+
+#-------------------------------------------------------------------
+sub prepareView {
+ my $self = shift;
+ $self->SUPER::prepareView();
+ my $template = WebGUI::Asset::Template->new($self->session, $self->get("projectDashboardTemplateId"));
+ $template->prepare;
+ $self->{_viewTemplate} = $template;
+}
+
+#-------------------------------------------------------------------
+sub processErrors {
+ my $self = shift;
+ my $errors = "";
+ if($_[0]) {
+ $errors = "
";
+ foreach (@{$_[0]}) {
+ $errors .= "- $_
";
+ }
+ $errors .= "
";
+ }
+ return $errors;
+}
+
+
+#-------------------------------------------------------------------
+sub purge {
+ my $self = shift;
+ #purge your wobject-specific data here. This does not include fields
+ # you create for your NewWobject asset/wobject table.
+ return $self->SUPER::purge;
+}
+
+#-------------------------------------------------------------------
+sub setSessionVars {
+ my $self = shift;
+ my $session = $self->session;
+ my $i18n = WebGUI::International->new($session,'ProjectManager');
+
+ return ($session,$session->privilege,$session->form,$session->db,$session->datetime,$i18n,$session->user);
+}
+
+#-------------------------------------------------------------------
+sub view {
+ my $self = shift;
+ my $var = $self->get;
+
+ my ($session,$privilege,$form,$db,$datetime,$i18n,$user) = $self->setSessionVars;
+ my $config = $session->config;
+ my $eh = $session->errorHandler;
+
+ $var->{'extras'} = $config->get("extrasURL")."/wobject/ProjectManager";
+ $var->{'project.create'} = $self->getUrl("func=editProject;projectId=new");
+ $var->{'project.create.label'} = $i18n->get("project new label");
+
+
+ #Project Table Headers
+ $var->{'project.name.label'} = $i18n->get("project name label");
+ $var->{'project.startDate.label'} = $i18n->get("project start date label");
+ $var->{'project.endDate.label'} = $i18n->get("project end date label");
+ $var->{'project.cost.label'} = $i18n->get("project cost label");
+ $var->{'project.complete.label'} = $i18n->get("project complete label");
+ $var->{'project.actions.label'} = $i18n->get("project action label");
+
+ $var->{'empty.colspan'} = 5;
+ if($user->isInGroup($self->get("groupToAdd"))) {
+ $var->{'canEditProjects'} = "true";
+ $var->{'empty.colspan'} = 6;
+ }
+
+ #Project Data
+ my @projects = ();
+ my $sth = $db->read("select * from PM_project where assetId=".$db->quote($self->get("assetId")));
+ while (my $project = $sth->hashRef) {
+ my $hash = {};
+ my $projectId = $project->{projectId};
+ $hash->{'project.view.url'} = $self->getUrl("func=viewProject;projectId=".$projectId);
+ $hash->{'project.name.data'} = $project->{name};
+ $hash->{'project.description.data'} = $project->{description};
+ $hash->{'project.startDate.data'} = $project->{startDate}?$datetime->epochToSet($project->{startDate}):"N/A";
+ $hash->{'project.endDate.data'} = $project->{endDate}?$datetime->epochToSet($project->{endDate}):"N/A";
+ $hash->{'project.cost.data.int'} = WebGUI::Utility::commify(int($project->{targetBudget}));
+ $hash->{'project.cost.data.float'} = WebGUI::Utility::commify($project->{targetBudget});
+ $hash->{'project.complete.data.int'} = int($project->{percentComplete});
+ $hash->{'project.complete.data.float'} = sprintf("%2.2f",$project->{percentComplete});
+ if($var->{'canEditProjects'}) {
+ $hash->{'project.edit.url'} = $self->getUrl("func=editProject;projectId=".$projectId);
+ $hash->{'project.edit.title'} = $i18n->get("project edit title");
+ $hash->{'project.delete.url'} = $self->getUrl("func=deleteProject;projectId=".$projectId);
+ $hash->{'project.delete.title'} = $i18n->get("project delete title");
+ }
+ push(@projects, $hash);
+ }
+ $sth->finish;
+
+ my $warning = $i18n->get("project delete warning");
+ $warning =~ s/'/\\'/g;
+ $var->{'project.delete.warning'} = $warning;
+ $var->{'noProjects'} = $i18n->get("no projects") if(scalar(@projects) == 0);
+ $var->{'project.loop'} = \@projects;
+
+ return $self->processTemplate($var, undef, $self->{_viewTemplate});
+}
+
+#-------------------------------------------------------------------
+sub www_deleteProject {
+ my $self = shift;
+ #Set Method Helpers
+ my ($session,$privilege,$form,$db,$datetime,$i18n,$user) = $self->setSessionVars;
+
+ #Check Privileges
+ return $privilege->insufficient unless ($user->isInGroup($self->get("groupToAdd")));
+
+ my $projectId = $form->get("projectId");
+
+ #Delete Project
+ $db->write("delete from PM_project where projectId=?",[$projectId]);
+ #Delete Associated Tasks
+ $db->write("delete from PM_task where projectId=?",[$projectId]);
+
+ return "";
+}
+
+#-------------------------------------------------------------------
+sub www_editProject {
+ my $self = shift;
+ #Set Method Helpers
+ my ($session,$privilege,$form,$db,$datetime,$i18n,$user) = $self->setSessionVars;
+
+ #Check Privileges
+ return $privilege->insufficient unless ($user->isInGroup($self->get("groupToAdd")));
+
+ #Set Local Vars
+ my $projectId = $form->get("projectId");
+ my $project = $db->quickHashRef("select * from PM_project where projectId=".$db->quote($projectId));
+ my $addEditText = ($projectId eq "new")?$i18n->get("create project"):$i18n->get("edit project");
+
+ #Build Form
+ my $f = WebGUI::HTMLForm->new($self->session,-action=>$self->getUrl, -extras=>q|onsubmit="return checkform(this);"|);
+ $f->hidden(
+ -name=>"func",
+ -value=>"editProjectSave"
+ );
+ $f->hidden(
+ -name=>"projectId",
+ -value=>$projectId
+ );
+ $f->readOnly(
+ -label=>$i18n->get("project id"),
+ -value=>$projectId
+ );
+ $f->text(
+ -name => "name",
+ -value => $form->get("name") || $project->{name},
+ -hoverHelp => $i18n->get('project name hoverhelp'),
+ -label => $i18n->get('project name label')
+ );
+ $f->HTMLArea(
+ -name => "description",
+ -value => $form->get("description") || $project->{description},
+ -hoverHelp => $i18n->get('project description hoverhelp'),
+ -label => $i18n->get('project description label')
+ );
+ $f->group(
+ -name=> "projectManager",
+ -value=> $form->get("projectManager") || $project->{projectManager} || $self->get("groupToAdd"),
+ -hoverHelp=> $i18n->get('project manager hoverhelp'),
+ -label => $i18n->get('project manager label')
+ );
+
+ my $dunitValue = $form->get("durationUnits") || $project->{durationUnits} || "hours";
+ $f->selectBox(
+ -name=>"durationUnits",
+ -value=> $dunitValue,
+ -options=>$self->_getDurationUnitHash,
+ -hoverHelp => $i18n->get('duration units hoverhelp'),
+ -label => $i18n->get('duration units label'),
+ -extras=> q|onchange="if(this.value == 'hours'){ document.getElementById('hoursper').style.display='' } else { document.getElementById('hoursper').style.display='none' }"|
+ );
+
+
+
+ my $hpdLabel = $i18n->get('hours per day label');
+ my $hpdHoverHelp = $i18n->get('hours per day hoverhelp');
+ $hpdHoverHelp =~ s/'/\\'/g;
+ my $hpdValue = $form->get("hoursPerDay") || $project->{hoursPerDay} || "8.0";
+ my $hpdStyle = ($dunitValue eq "days"?"display:none":"");
+
+ my $html = qq|
+
+ |
+
+ |
+
+
+ |
+
|;
+ $f->raw($html);
+
+ $f->float (
+ -name=>"targetBudget",
+ -value=> $form->get("targetBudget") || $project->{targetBudget} || "0.00",
+ -hoverHelp => $i18n->get('target budget hoverhelp'),
+ -label=> $i18n->get('target budget label')
+ );
+ $f->submit(
+ -extras=>"name='subbutton'",
+ -value=>$addEditText
+ );
+
+ my $jscript = qq|
+
+ |;
+
+ my $errors = $self->processErrors($_[0]);
+
+ my $output = $jscript."\n".$errors.$f->print;
+ return $self->getAdminConsole->render($output,$addEditText);
+}
+
+#-------------------------------------------------------------------
+sub www_editProjectSave {
+ my $self = shift;
+ #Set Method Helpers
+ my ($session,$privilege,$form,$db,$dt,$i18n,$user) = $self->setSessionVars;
+
+ #Check Privileges
+ return $privilege->insufficient unless ($user->isInGroup($self->get("groupToAdd")));
+
+ my $now = $dt->time();
+ my $uid = $user->userId;
+ my $projectId = $form->process("projectId","hidden");
+
+ #Set Properties
+ my $props = {};
+ $props->{projectId} = $projectId;
+ $props->{name} = $form->process("name","text");
+ $props->{description} = $form->process("description","HTMLArea");
+ $props->{projectManager} = $form->process("projectManager","group");
+ $props->{durationUnits} = $form->process("durationUnits","selectBox");
+ $props->{hoursPerDay} = $form->process("hoursPerDay","float") || 8.0;
+ $props->{targetBudget} = $form->process("targetBudget","float");
+ if($projectId eq "new") {
+ $props->{creationDate} = $now;
+ $props->{createdBy} = $uid;
+ }
+ $props->{lastUpdateDate} = $now;
+ $props->{lastUpdatedBy} = $uid;
+ #Process Errors
+ my @errors = ();
+ push(@errors,"You must enter a project name") unless ($props->{name});
+ if(scalar(@errors) > 0) {
+ return $self->www_editProject(\@errors);
+ }
+
+ #Save the extended project data
+ my $projectId = $self->setCollateral("PM_project","projectId",$props,0,1);
+
+ if($projectId eq "new") {
+ #Create Project Start Milestone Task for new projects
+ $props = {};
+ $props->{taskId} = "new";
+ $props->{projectId} = $projectId;
+ $props->{taskName} = $i18n->get("project start task label");
+ $props->{duration} = 0;
+ $props->{startDate} = $dt->time();
+ $props->{endDate} = $dt->time();
+ $props->{isMilestone} = 1;
+ $props->{creationDate} = $now;
+ $props->{createdBy} = $uid;
+ $props->{lastUpdateDate} = $now;
+ $props->{lastUpdatedBy} = $uid;
+
+ #Save the extended task data
+ my $taskId = $self->setCollateral("PM_task","taskId",$props,1,0,"projectId",$projectId);
+ }
+
+ return $self->www_viewProject($projectId);
+}
+
+#-------------------------------------------------------------------
+sub www_editTask {
+ my $self = shift;
+ my $var = {};
+ #Set Method Helpers
+ my ($session,$privilege,$form,$db,$dt,$i18n,$user) = $self->setSessionVars;
+ my $config = $session->config;
+
+ my $projectId = $form->get("projectId");
+ my $taskId = $form->get("taskId") || "new";
+
+ my $project = $db->quickHashRef("select * from PM_project where projectId=".$db->quote($projectId));
+ my $task = $db->quickHashRef("select * from PM_task where taskId=".$db->quote($taskId));
+
+ #Check Privileges
+ return $privilege->insufficient unless ($user->isInGroup($project->{projectManager}));
+
+ my $isMilestone = $task->{isMilestone};
+ my $seq = $task->{sequenceNumber};
+ my $extras = ($isMilestone)?" disabled":"";
+ $var->{'form.header'} = WebGUI::Form::formHeader($session,{
+ action=>$self->getUrl,
+ extras=>q|name="editTaskForm"|
+ });
+ $var->{'form.header'} .= WebGUI::Form::hidden($session, {
+ -name=>"func",
+ -value=>"editTaskSave"
+ });
+ $var->{'form.header'} .= WebGUI::Form::hidden($session, {
+ -name=>"projectId",
+ -value=>$projectId
+ });
+ $var->{'form.header'} .= WebGUI::Form::hidden($session, {
+ -name=>"taskId",
+ -value=>$taskId
+ });
+
+ #Set some hidden variables to make it easy to reset data in javascript
+ my $duration = $task->{duration};
+ my $start = $dt->epochToSet($task->{startDate});
+ my $end = $dt->epochToSet($task->{endDate});
+ my $dependant = $task->{dependants};
+
+ $var->{'form.header'} .= WebGUI::Form::hidden($session, {
+ -name=>"orig_duration",
+ -value=>$duration
+ });
+ $var->{'form.header'} .= WebGUI::Form::hidden($session, {
+ -name=>"orig_start",
+ -value=>$start
+ });
+ $var->{'form.header'} .= WebGUI::Form::hidden($session, {
+ -name=>"orig_end",
+ -value=>$end
+ });
+ $var->{'form.header'} .= WebGUI::Form::hidden($session, {
+ -name=>"orig_dependant",
+ -value=>$dependant
+ });
+ $var->{'form.name'} = WebGUI::Form::text($session,{
+ -name=>"name",
+ -value=>$task->{taskName},
+ -extras=>q|style="width:95%;"|
+ });
+
+
+ $var->{'form.duration'} = WebGUI::Form::float($session,{
+ -name=>"duration",
+ -value=>$task->{duration},
+ -extras=>qq|style="width:70%;" onchange="adjustTaskTimeFromDuration(this.form.start,this.form.end,this,true,this.form.dependants,this.form.orig_start,this.form.orig_end,$seq)" onblur="if(this.value == 0){ adjustTaskTimeFromDuration(this.form.start,this.form.end,this,true) }" $extras|
+ });
+ $var->{'form.duration.units'} = $self->_getDurationUnitHashAbbrev->{$project->{durationUnits}};
+ $var->{'form.start'} = WebGUI::Form::text($session,{
+ -name=>"start",
+ -value=>$start,
+ -size=>"10",
+ -maxlength=>"10",
+ -extras=>qq|onfocus="doCalendar(this.id);" onblur="adjustTaskTimeFromDate(this.form.start,this.form.end,this.form.duration,this,true,this.form.dependants,this.form.orig_start,this.form.orig_end,$seq);" onblur="if(this.form.milestone.checked==true){ this.form.end.value=this.value; }" style="width:88%;"|
+ });
+
+ $var->{'form.end'} = WebGUI::Form::text($session,{
+ -name=>"end",
+ -value=>$end,
+ -size=>"10",
+ -maxlength=>"10",
+ -extras=>qq|onfocus="doCalendar(this.id);" style="width:88%;" onblur="adjustTaskTimeFromDate(this.form.start,this.form.end,this.form.duration,this,true,this.form.dependants,this.form.orig_start,this.form.orig_end,$seq);" $extras|
+ });
+
+ $var->{'form.dependants'} = WebGUI::Form::integer($session,{
+ -name=>"dependants",
+ -value=>$dependant || "",
+ -defaultValue=>"",
+ -size=>4,
+ -maxlength=>10,
+ -extras=>qq|style="width:50%;" onchange="validateDependant(this,this.form.orig_dependant,'$seq',this.form.start,this.form.end,this.form.duration,true,true,this.form.orig_start,this.form.orig_end);"|
+ });
+ tie my %users, "Tie::IxHash";
+ %users = $db->buildHash("select userId,username from users where userId not in (1,3)");
+ %users = (""=>$i18n->get("resource none"),%users);
+ $var->{'form.resource'} = WebGUI::Form::selectBox($session, {
+ -name=>"resource",
+ -options=>\%users,
+ -value=>[$task->{resourceId}],
+ -extras=>$extras
+ });
+ $var->{'form.milestone'} = WebGUI::Form::checkbox($session, {
+ -name=>"milestone",
+ -value=>1,
+ -checked=>$task->{isMilestone},
+ -extras=>q|onclick="configureMilestone(this)"|
+ });
+ $var->{'form.percentComplete'} = WebGUI::Form::float($session, {
+ -name=>"percentComplete",
+ -value=>$task->{percentComplete},
+ -extras=>$extras
+
+ });
+ $var->{'form.save'} = WebGUI::Form::submit($session, {
+ -value=>"Save",
+ -extras=>q|name="subbutton"|
+ });
+ $var->{'form.footer'} = WebGUI::Form::formFooter($session);
+
+ $var->{'extras'} = $config->get("extrasURL");
+
+ return $self->processTemplate($var,$self->getValue("editTaskTemplateId"))
+}
+
+#-------------------------------------------------------------------
+sub www_editTaskSave {
+ my $self = shift;
+ my $var = {};
+ #Set Method Helpers
+ my ($session,$privilege,$form,$db,$dt,$i18n,$user) = $self->setSessionVars;
+ my $config = $session->config;
+
+ my $projectId = $form->get("projectId");
+ my $project = $db->quickHashRef("select * from PM_project where projectId=".$db->quote($projectId));
+
+ #Check Privileges
+ return $privilege->insufficient unless ($user->isInGroup($project->{projectManager}));
+
+ my $isMilestone = $form->process("milestone","checkbox");
+
+ my $props = {};
+ $props->{taskId} = $form->process("taskId","hidden");
+ $props->{projectId} = $projectId;
+ $props->{taskName} = $form->process("name","text");
+ $props->{duration} = $isMilestone? 0 : $form->process("duration","text");
+ $props->{startDate} = $form->process("start","date");
+ $props->{endDate} = ($isMilestone ? $props->{startDate} : $form->process("end","date"));
+ $props->{dependants} = $form->process("dependants","selectBox") unless $isMilestone;
+ $props->{isMilestone} = $isMilestone || 0;
+ $props->{resourceId} = $form->process("resource","selectBox");
+ $props->{percentComplete} = $isMilestone? 0 : $form->process("percentComplete","float");
+
+ my $now = $dt->time();
+ if($props->{taskId} eq "new") {
+ $props->{creationDate} = $now;
+ $props->{createdBy} = $user->userId;
+ }
+ $props->{lastUpdateDate} = $now;
+ $props->{lastUpdatedBy} = $user->userId;
+
+ #Save the extended task data
+ my $taskId = $self->setCollateral("PM_task","taskId",$props,1,0,"projectId",$projectId);
+
+ $self->_updateProject($projectId);
+ $self->_arrangePredecessors($project,$taskId);
+
+ return $self->www_viewProject($projectId,$taskId);
+}
+
+#-------------------------------------------------------------------
+sub _updateProject {
+ my $self = shift;
+ my ($session,$privilege,$form,$db,$dt,$i18n,$user) = $self->setSessionVars;
+ my $eh = $session->errorHandler;
+ my $projectId= $_[0];
+
+ my ($minStart) = $db->quickArray("select min(startDate) from PM_task where projectId=".$db->quote($projectId));
+ my ($maxEnd) = $db->quickArray("select max(endDate) from PM_task where projectId=".$db->quote($projectId));
+
+ my ($projectTotal) = $db->quickArray("select sum(duration) from PM_task where projectId=".$db->quote($projectId));
+
+ my $complete = 0;
+
+ my $tasks = $db->buildArrayRefOfHashRefs("select * from PM_task where projectId=".$db->quote($projectId)." order by sequenceNumber asc");
+ foreach my $task (@{$tasks}) {
+ $complete += ($task->{duration} * ($task->{percentComplete}/100));
+ }
+
+ my $projectComplete = (($complete / $projectTotal) * 100);
+
+ $db->write("update PM_project set startDate=?, endDate=?, percentComplete=? where projectId=?",[$minStart,$maxEnd,$projectComplete,$projectId]);
+}
+
+
+
+#-------------------------------------------------------------------
+sub _arrangePredecessors {
+ my $self = shift;
+ my ($session,$privilege,$form,$db,$dt,$i18n,$user) = $self->setSessionVars;
+ my $eh = $session->errorHandler;
+ my ($project,$taskId) = @_;
+ my $projectId = $project->{projectId};
+
+ my $seq = 0;
+ if($taskId) {
+ $seq = "(select sequenceNumber from PM_task where taskId=".$db->quote($taskId).")";
+ }
+
+ my $tasks = $db->buildArrayRefOfHashRefs("select * from PM_task where projectId=".$db->quote($projectId));
+
+ my $taskHash = {};
+ foreach my $task (@{$tasks}) {
+ my $seqNum = $task->{sequenceNumber};
+ #$eh->warn("Seq Num = $seqNum");
+ #Calculate initial duration in days and duration floor
+ my $durationInDays = $task->{duration};
+ $durationInDays = $durationInDays / $project->{hoursPerDay} if( $project->{durationUnits} == "hours" );
+ #$eh->warn("Duration in Days: ".$durationInDays);
+ my $durationFloor = floor($durationInDays);
+ #$eh->warn("Duration Floor: ".$durationFloor);
+
+ #Skip the first record as it has no predecessors
+ #next if (scalar(keys %{$taskHash})) == 1;
+ if( scalar(keys %{$taskHash} ) > 0 ) {
+ #If the task has a predecessor, ensure that it starts are the right time.
+ my $predecessor = $task->{dependants};
+ #$eh->warn("Predecessor is: ".$predecessor);
+ if($predecessor) {
+ #Get the predecessor task data - since predecessors come before this task, it should be in the taskHash
+ my $pred = $taskHash->{$predecessor};
+ my $predEndDate = $pred->{endDate};
+ #$eh->warn("Task Start Date: ".$dt->epochToSet($task->{startDate}));
+ #$eh->warn("Pred End Date: ".$dt->epochToSet($predEndDate));
+ #Get the day part of the predecessor
+ my $predDayPart = $pred->{dayPart};
+ #$eh->warn("Pred Day Part: ".$predDayPart);
+ #Make sure start date of this task is greater than the end date of the predecessor
+ if($task->{startDate} <= $predEndDate) {
+ #Change the start and end dates of the task
+ if($predDayPart > 0) {
+ #The previous task took up a part of a day. Add the additional day part to get the correct duration
+ $durationInDays += $predDayPart;
+ $durationFloor = floor($durationInDays);
+ }
+
+ #$eh->warn("Duration in Days is now: ".$durationInDays);
+ #$eh->warn("Duration Floor is now: ".$durationFloor);
+
+ #$eh->warn("Pred End Date is now: ".$dt->epochToSet($predEndDate));
+
+ #Set the start date of this task to the end date of the predecessor and update the hash
+ $task->{startDate} = $predEndDate;
+ #$eh->warn("Start Date is now: ".$dt->epochToSet($task->{startDate}));
+ #Adjust end date for change in start date and update the hash
+ $task->{endDate} = $dt->addToDateTime($task->{startDate}, 0, 0, $durationFloor);
+ #$eh->warn("End Date is now: ".$dt->epochToSet($task->{endDate})."\n\n");
+ #Update the database
+ $self->setCollateral("PM_task","taskId",$task,1,0,"projectId",$projectId);
+ }
+ }
+ }
+
+ #Adjust duration of days to only include the part of the day used
+ $durationInDays = $durationInDays - floor($durationInDays);
+ #$eh->warn("Day Part left over is: $durationInDays \n\n");
+
+
+ #add this task to the taskHash
+ $taskHash->{$seqNum} = {
+ 'startDate'=>$task->{startDate},
+ 'endDate'=>$task->{endDate},
+ 'duration'=>$task->{duration},
+ 'dayPart'=>$durationInDays
+ };
+ }
+
+ return;
+}
+
+#-------------------------------------------------------------------
+sub www_saveExistingTasks {
+ my $self = shift;
+ my $var = {};
+ #Set Method Helpers
+ my ($session,$privilege,$form,$db,$dt,$i18n,$user) = $self->setSessionVars;
+ my $config = $session->config;
+
+ my $projectId = $form->get("projectId");
+ my $project = $db->quickHashRef("select * from PM_project where projectId=".$db->quote($projectId));
+
+ #Check Privileges
+ return $privilege->insufficient unless ($user->isInGroup($project->{projectManager}));
+
+ my $tasks = $db->buildArrayRefOfHashRefs("select * from PM_task where projectId=".$db->quote($projectId)." order by sequenceNumber asc");
+
+ #Save each row
+ foreach my $task (@{$tasks}) {
+ my $isMilestone = $task->{isMilestone};
+ my $props = {};
+ my $taskId = $task->{taskId};
+ $props->{taskId} = $taskId;
+ $props->{projectId} = $projectId;
+ $props->{startDate} = $form->process("start_$taskId","date");
+ $props->{endDate} = $form->process("end_$taskId","date");
+ $props->{dependants} = $form->process("dependants_$taskId","selectBox");
+ unless($isMilestone) {
+ $props->{duration} = $form->process("duration_$taskId","float");
+ }
+ $props->{lastUpdateDate} = $dt->time();
+ $props->{lastUpdatedBy} = $user->userId;
+
+ $self->setCollateral("PM_task","taskId",$props,1,0,"projectId",$projectId);
+ }
+
+ #Rearrange predecessors
+ #$self->_arrangePredecessors($project);
+
+ return $self->www_drawGanttChart();
+
+}
+
+#-------------------------------------------------------------------
+sub www_viewProject {
+ my $self = shift;
+ my $var = {};
+ #Set Method Helpers
+ my ($session,$privilege,$form,$db,$dt,$i18n,$user) = $self->setSessionVars;
+ my $config = $session->config;
+ my $style = $session->style;
+ my $eh = $session->errorHandler;
+ my $projectId = $_[0] || $form->get("projectId");
+
+ #Check Privileges
+ return $privilege->insufficient unless ($user->isInGroup($self->canView));
+
+ $var->{'extras'} = $config->get("extrasURL")."/wobject/ProjectManager";
+ $var->{'extras.base'} = $config->get("extrasURL");
+
+ #Set Some Style stuff
+ $style->setLink($var->{'extras'}."/subModal.css",{rel=>"stylesheet",type=>"text/css"});
+ $style->setLink($var->{'extras.base'}."/calendar/calendar-win2k-1.css",{rel=>"stylesheet",type=>"text/css"});
+
+ #Get Project Data
+ my $project = $db->quickHashRef("select * from PM_project where projectId=".$db->quote($projectId));
+ my $canEdit = $user->isInGroup($self->get("groupToAdd"));
+ my $canAddTask = $user->isInGroup($project->{projectManager}) || $canEdit;
+ my $dunits = $self->_getDurationUnitHashAbbrev->{$project->{durationUnits}};
+
+ #Set some JavaScript stuff
+ $var->{'project.durationUnits'} = $dunits;
+ $var->{'project.hoursPerDay'} = $project->{hoursPerDay} || "0";
+
+ #Get Tasks
+ my $data = $db->buildArrayRefOfHashRefs("select * from PM_task where projectId=".$db->quote($projectId)." order by sequenceNumber asc");
+
+ $var->{'task.name.label'} = $i18n->get("task name label");
+ $var->{'task.duration.label'} = $i18n->get("task duration label");
+ $var->{'task.start.label'} = $i18n->get("task start label");
+ $var->{'task.end.label'} = $i18n->get("task end label");
+ $var->{'task.dependants.label'} = $i18n->get("task dependant label");
+
+ #JavaScript Alert Errors for Tasks
+ $var->{'form.name.error'} = $i18n->get("task name error");
+ $var->{'form.start.error'} = $i18n->get("task start error");
+ $var->{'form.end.error'} = $i18n->get("task end error");
+ $var->{'form.greaterthan.error'} = $i18n->get("task greaterthan error");
+ $var->{'form.previousPredecessor.error'} = $i18n->get("task previousPredecessor error");
+ $var->{'form.samePredecessor.error'} = $i18n->get("task samePredecessor error");
+ $var->{'form.noPredecessor.error'} = $i18n->get("task noPredecessor error");
+ $var->{'form.invalidMove.error'} = $i18n->get("task invalidMove error");
+
+ my @taskList = ();
+ my $count = 0;
+ foreach my $row (@{$data}) {
+ my $hash = {};
+ my $seq = $row->{sequenceNumber};
+ my $id = $row->{taskId};
+ my $isMilestone = $row->{isMilestone} || 0;
+ my $startDate = $dt->epochToSet($row->{startDate});
+ my $endDate = $dt->epochToSet($row->{endDate});
+ my $duration = $row->{duration};
+
+ $hash->{'task.number'} = $seq;
+ $hash->{'task.number.id'} = "task~~".$id."~~rowId";
+ $hash->{'task.name'} = $row->{taskName};
+
+ if($canEdit) {
+ my $startId = "start_".$id."_formId";
+ my $endId = "end_".$id."_formId";
+ my $durId = "duration_".$id."_formId";
+ my $predId = "dependants_".$id."_formId";
+ my $origStartField = "orig_start_$id";
+ my $origStartFieldId = $origStartField."_formId";
+ my $origDepField = "orig_dependants_$id";
+ my $origDepFieldId = $origDepField."_formId";
+ my $origEndField = "orig_end_$id";
+ my $origEndFieldId = $origEndField."_formId";
+
+ $hash->{'task.start'} = WebGUI::Form::text($session,{
+ -name=>"start_$id",
+ -value=>$startDate,
+ -size=>"10",
+ -maxlength=>"10",
+ -extras=>qq|onfocus="doCalendar(this.id);" class="taskdate" onblur="adjustTaskTimeFromDate(this,document.getElementById('$endId'),document.getElementById('$durId'),this,false,document.getElementById('$predId'),document.getElementById('$origStartFieldId'),document.getElementById('$origEndFieldId'),$seq);"|
+ });
+
+ $hash->{'task.start'} .= WebGUI::Form::hidden($session,{
+ -name=>$origStartField,
+ -value=>$startDate,
+ -extras=>qq|id="$origStartFieldId"|
+ });
+
+
+ $hash->{'task.dependants'} = WebGUI::Form::integer($session,{
+ -name=>"dependants_$id",
+ -value=>$row->{dependants} || "",
+ -defaultValue=>"",
+ -extras=>qq|class="taskdependant" onchange="validateDependant(this,document.getElementById('$origDepFieldId'),'$seq',document.getElementById('$startId'),document.getElementById('$endId'),document.getElementById('$durId'),false,document.getElementById('$origStartFieldId'),document.getElementById('$origEndFieldId'));"|
+ });
+ $hash->{'task.dependants'} .= WebGUI::Form::hidden($session,{
+ -name=>$origDepField,
+ -value=>$row->{dependants},
+ -extras=>qq|id="$origDepFieldId"|
+ });
+
+ $hash->{'task.end'} = WebGUI::Form::text($session,{
+ -name=>"end_$id",
+ -value=>$endDate,
+ -size=>"10",
+ -maxlength=>"10",
+ -extras=>qq|class="taskdate" onfocus="doCalendar(this.id);" onblur="adjustTaskTimeFromDate(document.getElementById('$startId'),this,document.getElementById('$durId'),this,false,document.getElementById('$predId'),document.getElementById('$origStartFieldId'),document.getElementById('$origEndFieldId'),$seq);"|
+ });
+
+ $hash->{'task.end'} .= WebGUI::Form::hidden($session,{
+ -name=>$origEndField,
+ -value=>$endDate,
+ -extras=>qq|id="$origEndFieldId"|
+ });
+ #Don't display uneditable fields if the task is a milestone.
+ if($isMilestone) {
+ $hash->{'task.duration'} = $row->{duration};
+ $hash->{'task.duration'} .= WebGUI::Form::hidden($session,{
+ -name=>"duration_$id",
+ -value=>$duration,
+ -extras=>qq|id="$durId"|
+ });
+ } else {
+ $hash->{'task.duration'} = WebGUI::Form::float($session,{
+ -name=>"duration_$id",
+ -value=>$duration,
+ -extras=>qq|class="taskduration" onchange="adjustTaskTimeFromDuration(document.getElementById('$startId'),document.getElementById('$endId'),this,false,document.getElementById('$predId'),document.getElementById('$origStartFieldId'),document.getElementById('$origEndFieldId'),$seq);" |
+ });
+
+ }
+ } else {
+ $hash->{'task.duration'} = $duration;
+ $hash->{'task.start'} = $startDate;
+ $hash->{'task.end'} = $endDate;
+ $hash->{'task.dependants'} = $row->{dependants} || " ";
+ }
+ $hash->{'task.duration.units'} = $dunits;
+ $hash->{'task.isMilestone'} = "true" if($isMilestone);
+ if($canAddTask) {
+ $hash->{'task.edit.url'} = $self->getUrl("func=editTask;projectId=$projectId;taskId=".$row->{taskId});
+ $hash->{'task.edit.label'} = $i18n->get("edit task label");
+ }
+ push(@taskList, $hash);
+ }
+ $var->{'task.loop'} = \@taskList;
+
+ #Set some javascript stuff;
+ my $taskLength = scalar(@taskList);
+ $var->{'project.task.length'} = $taskLength;
+
+ if($canEdit) {
+ #Build Form for submitting on the fly updates
+ $var->{'form.header'} = WebGUI::Form::formHeader($session,{
+ action=>$self->getUrl,
+ extras=>q|name="editAll"|
+ });
+ $var->{'form.header'} .= WebGUI::Form::hidden($session, {
+ -name=>"func",
+ -value=>"saveExistingTasks"
+ });
+ $var->{'form.header'} .= WebGUI::Form::hidden($session, {
+ -name=>"projectId",
+ -value=>$projectId
+ });
+ $var->{'form.footer'} = WebGUI::Form::formFooter($session);
+ $var->{'project.canEdit'} = "true";
+ $var->{'task.resources.label'} = $i18n->get("task resources label");
+ $var->{'task.resources.url'} = $self->getUrl("func=manageResources");
+
+ }
+
+ if($canAddTask) {
+ $var->{'task.add.label'} = $i18n->get("add task label");
+ $var->{'task.add.url'} = $self->getUrl("func=editTask;projectId=$projectId;taskId=new");
+ $var->{'task.canAdd'} = "true";
+ }
+
+
+ #Rowspan of gaant chart is 4 plus number of tasks
+ $var->{'project.gaant.rowspan'} = 4 + $taskLength;
+
+ $var->{'project.ganttChart'} = $self->www_drawGanttChart($projectId, $data, $var);
+
+ $var->{'task.back.label'} = $i18n->get("task back label");
+ $var->{'task.back.url'} = $self->getUrl;
+
+ return $style->process($self->processTemplate($var,$self->getValue("projectDisplayTemplateId")),$self->getValue("styleTemplateId"));
+}
+
+#-------------------------------------------------------------------
+sub www_drawGanttChart {
+ my $self = shift;
+ my $var = {};
+ #Set Method Helpers
+ my ($session,$privilege,$form,$db,$dt,$i18n,$user) = $self->setSessionVars;
+ my $config = $session->config;
+ my $style = $session->style;
+ my $eh = $session->errorHandler;
+
+ #Check Privileges
+ return $privilege->insufficient unless ($user->isInGroup($self->canView));
+
+ #Set up some the task data
+ my $projectId = $_[0];
+ my $taskList = $_[1] || [];
+ my $projVar = $_[2] || {};
+ my $calledByAjax = 0;
+
+ unless($projectId) {
+ $projectId = $form->get("projectId");
+ $taskList = $db->buildArrayRefOfHashRefs("select * from PM_task where projectId=".$db->quote($projectId)." order by sequenceNumber asc");
+ $calledByAjax = 1;
+ }
+
+ my ($dunits,$hoursPerDay) = $db->quickArray("select durationUnits,hoursPerDay from PM_project where projectId=".$db->quote($projectId));
+
+ $var->{'extras'} = $config->get("extrasURL")."/wobject/ProjectManager";
+
+ #Initialize display settings
+ my $projectDisplay = "weeks";
+ my $monthInterval = 5;
+ my $dayLimit = 150;
+ if($projectDisplay eq "weeks") {
+ $monthInterval = 3;
+ $dayLimit = 60;
+ }
+
+ #Find start and end months: Set start and end months to always display at least 4 months if display in weeks, 6 months if display in months
+ my ($startMonth,$endMonth);
+ if(scalar(@{$taskList}) > 0) {
+ ($startMonth) = $db->quickArray("select min(startDate) from PM_task where projectId=".$db->quote($projectId));
+ ($endMonth) = $db->quickArray("select max(endDate) from PM_task where projectId=".$db->quote($projectId));
+
+ #$eh->warn("Interval is: ".$dt->getDaysInInterval($startMonth,$endMonth));
+ if($dt->getDaysInInterval($startMonth,$endMonth) < 60) {
+ $endMonth = $dt->addToDate($startMonth,0,3);
+ }
+ } else {
+ $startMonth = $dt->time;
+ $endMonth = $dt->addToDate($startMonth,0,3);
+ }
+
+ #$eh->warn($dt->epochToSet($startMonth));
+ #$eh->warn($dt->epochToSet($endMonth));
+ #Build the loops of weeks and days
+ my @monthsLoop = ();
+ my @daysLoop = ();
+
+ if($projectDisplay eq "weeks") {
+ #Week View Below
+ my $dayOfWeek = $dt->getDayOfWeek($startMonth);
+ my $sundayAdjust = (0 - ($dayOfWeek % 7));
+
+ $startMonth = $dt->addToDateTime($startMonth,0,0,$sundayAdjust,1);
+
+ my @days = (
+ $i18n->get("sunday label"),
+ $i18n->get("monday label"),
+ $i18n->get("tuesday label"),
+ $i18n->get("wednesday label"),
+ $i18n->get("thursday label"),
+ $i18n->get("friday label"),
+ $i18n->get("saturday label"),
+ );
+ #$eh->warn($dt->epochToSet($sundayOfFirstWeek));
+ #$eh->warn($dt->epochToSet($endMonth));
+ my $datecounter = $startMonth;
+ my $counter = 0;
+ while($datecounter <= $endMonth || $counter++ == 1000) {
+ my $hash = {};
+ $hash->{'month.name'} = $dt->epochToHuman($datecounter,"%C %d, %Y");
+ $hash->{'month.colspan'} = 7;
+ push(@monthsLoop,$hash);
+ #Add 7 days for this week
+ foreach (@days) {
+ push(@daysLoop,{'day.number' => $_ });
+ }
+ #$eh->warn($dt->epochToSet($datecounter));
+ $datecounter = $dt->addToDateTime($datecounter,0,0,7);
+ }
+
+ } else {
+ #Months View Below
+ my $junk;
+ ($startMonth,$junk) = $dt->monthStartEnd($startMonth);
+ ($junk,$endMonth) = $dt->monthStartEnd($endMonth);
+ my $numMonths = ($dt->monthCount($startMonth,$endMonth) + 1);
+
+ for(my $i = 0; $i < $numMonths; $i++) {
+ my $hash = {};
+ my $month = $dt->addToDateTime($startMonth,0,$i,0,1);
+
+ $hash->{'month.name'} = $dt->epochToHuman($month,"%C %Y");
+ $self->_addDaysForMonth(\@daysLoop,$hash,$month);
+ push(@monthsLoop,$hash);
+ }
+
+ }
+
+ $var->{'months.loop'} = \@monthsLoop;
+ $var->{'days.loop'} = \@daysLoop;
+
+ #Build tasks and divs
+ my @taskCount = ();
+ my @taskDiv = ();
+ my $taskCount = 0;
+
+ my $pixelSize = ($projectDisplay eq "weeks")?23:3.2857;
+
+ $var->{'project.task.array'} = "{\n";
+
+ my $taskHash = {};
+ foreach my $task (@{$taskList}) {
+ my $hash = {};
+ my $id = $task->{taskId};
+ my $seq = $task->{sequenceNumber};
+ my $startDate = $task->{startDate};
+ my $endDate = $task->{endDate};
+ my $duration = $task->{duration};
+ my $predecessor = $task->{dependants};
+ my $resource = $task->{resourceId};
+
+ if($resource) {
+ $hash->{'task.hasResource'} = "true";
+ my $u = WebGUI::User->new($session,$resource);
+ my $username = $u->username;
+
+ my $firstName = $u->profileField('firstName');
+ my $lastName = $u->profileField('lastName');
+ if($firstName && $lastName) {
+ $username = $firstName." ".$lastName;
+ }
+ $hash->{'task.resource.name'} = $username;
+ }
+
+ my $durationFloor = floor($duration);
+ $duration = $duration / $hoursPerDay if( $dunits == "hours" );
+ #Set duration to 1 day if it's a milestone
+ #$duration = 1 unless ($duration);
+
+ #Each day is 23 pixels so calculate the days and round
+ unless ($duration) {
+ $hash->{'task.div.width'} = $pixelSize;
+ } else {
+ $hash->{'task.div.width'} = int(($duration * $pixelSize)) || 3;
+ }
+
+ $hash->{'task.div.label.left'} = $hash->{'task.div.width'} + 3;
+
+ my $predDayPart = 0;
+ my $predEndDate = "";
+ if($predecessor) {
+ my $pred = $taskHash->{$predecessor};
+ $predEndDate = $pred->{endDate};
+ #Get the day part of the predecessor
+ $predDayPart = $pred->{dayPart};
+ if($startDate eq $predEndDate && $predDayPart > 0) {
+ #The previous task took up a part of the same day. Add the additional day part to get the correct duration
+ $duration += $predDayPart;
+ $durationFloor = floor($duration);
+ }
+
+ }
+
+ #Adjust top for MSIE
+ my $divTop = ($session->env->get("HTTP_USER_AGENT") =~ /msie/i) ? 45 : 43;
+ #Start at 45 px and add 20px as the start of the new task
+ $hash->{'task.div.top'} = $divTop + (22*$taskCount);
+
+ #Interval includes current day so add 1
+ my $daysFromStart = ($dt->getDaysInInterval($startMonth,$startDate) + 1);
+ #Add day part of predecessor if necessary
+ #$eh->warn("Task $seq is currently $daysFromStart days from the first day on ".$dt->epochToHuman($startDate));
+ my $daysLeft = $daysFromStart;
+ #Only adjust for predecessor if the start date of the task falls on the same day as it's predecessors end date
+ if($startDate eq $predEndDate) {
+ $daysLeft += $predDayPart;
+ #$eh->warn("Adjusting this by $predDayPart days");
+ }
+ $hash->{'task.div.left'} = int(($daysLeft * $pixelSize)); #Each day is 23 pixels so calculate the days and round
+ #$eh->warn("Starts at: $daysLeft * $pixelSize :".$hash->{'task.div.left'});
+
+ $hash->{'task.isMilestone'} = $task->{isMilestone};
+
+ push(@taskDiv, $hash);
+ push(@taskCount, { 'task.counter' => $task->{sequenceNumber} } );
+ $var->{'project.task.array'} .= ",\n" if($taskCount > 0);
+ $startDate = $dt->epochToSet($startDate);
+ $endDate = $dt->epochToSet($endDate);
+ my $rduration = $task->{duration};
+
+ #Adjust duration of days to only include the part of the day used
+ #$eh->warn("day part is being set to: $duration - ".floor($duration)." : ".($duration-floor($duration)));
+ $duration = $duration - floor($duration);
+
+ $hash->{'task.div.percentComplete'} = $task->{percentComplete} || 0;
+ $var->{'project.task.array'} .= qq|"$seq": { "id":"$id" ,"start":"$startDate" ,"end":"$endDate", "duration":"$rduration", "dayPart":"$duration", "predecessor":"$predecessor" }|;
+ $taskCount++;
+
+ $taskHash->{$seq} = {
+ 'startDate'=>$task->{startDate},
+ 'endDate'=>$task->{endDate},
+ 'duration'=>$task->{duration},
+ 'dayPart'=>$duration
+ };
+ }
+
+ $var->{'task.count.loop'} = \@taskCount;
+ $var->{'task.div.loop'} = \@taskDiv;
+ $var->{'project.task.array'} .= "\n}";
+
+ #Set Gantt Chart template vars
+ my $cols = scalar(@daysLoop);
+ $var->{'total.colspan'} = $cols;
+ $var->{'scrollWidth'} = $cols * 23;
+
+ #Set project template vars
+ $var->{'project.table.width'} = $projVar->{'project.table.width'} = 560 + $var->{'scrollWidth'};
+ my $scrollWidth = (1- (560 / $projVar->{'project.table.width'})) * 100;
+ $var->{'project.scroll.percentWidth'} = $projVar->{'project.scroll.percentWidth'} = sprintf("%2.2f",$scrollWidth);
+
+ return $self->processTemplate($var,$self->getValue("ganttChartTemplateId"));
+}
+
+
+
+1;