Overall refactoring of projectDisplay.js from the Project Management asset.

Hopefully this will make it much easier to deal with later, and hopefully this
will help to flush out the remaining duration-calculation-related bugs by
avoiding repetition of the calculations.  Various minor fixes in the process.

I'm not convinced this is completely bug-free yet, of course, but it does
seem at least to be slightly less broken than before, so.
This commit is contained in:
Drake 2006-10-05 23:07:13 +00:00
parent 01bfe673ae
commit 6516062d4d
5 changed files with 621 additions and 284 deletions

View file

@ -3,7 +3,8 @@
- fix: adding Matrix listings committing the current version tag - fix: adding Matrix listings committing the current version tag
- fix: user searches in task resource additions in PM not displaying right without both last name and first name present - fix: user searches in task resource additions in PM not displaying right without both last name and first name present
- fix: task editor in PM not actually receiving start/end date information at first - fix: task editor in PM not actually receiving start/end date information at first
- fix: Error Displaying Multiple TimeTracking Wobjects (ekennedy) - fix: Error Displaying Multiple TimeTracking Wobjects (ekennedy)
- refactoring of PM JavaScript stuff
7.0.9 7.0.9
- Removed the need for DateTime::Cron::Simple, which also added the ability - Removed the need for DateTime::Cron::Simple, which also added the ability

View file

@ -0,0 +1,91 @@
#ProjectManagerTMPL0004
<tmpl_var form.header><tmpl_var form.seqNum>
<table class="popUp" cellspacing="0" cellpadding="3" border="0">
<tbody>
<tr>
<td align="right" style="width:25%;" class="header">Task Name&nbsp;</td>
<td style="width:75%;"><tmpl_var form.name></td>
</tr>
<tr>
<td colspan="2">
<table width="100%" cellpadding="3" cellspacing="0">
<tbody>
<tr class="clear">
<td style="width:25%;" align="right" class="header">Start&nbsp;</td>
<td style="width:25%;"><tmpl_var form.start></td>
<td style="width:25%;" align="right" class="header">Finish&nbsp;</td>
<td style="width:25%;"><tmpl_var form.end></td>
</tr><tr class="clear">
<td style="width:100%; text-align: center" colspan="4" class="header"><tmpl_var form.taskType></td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td colspan="2">
<table width="100%" cellpadding="3" cellspacing="0">
<tbody>
<tr class="clear">
<td style="width:25%; text-align:right" class="header">Duration&nbsp;</td>
<td style="width:25%;"><tmpl_var form.duration>&nbsp;<tmpl_var form.duration.units></td>
<td style="width:25%; text-align:right" class="header">Lag Time&nbsp;</td>
<td style="width:25%;"><tmpl_var form.lagTime>&nbsp;<tmpl_var form.lagTime.units></td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td colspan="2">
<table width="100%" cellpadding="3" cellspacing="0">
<tbody>
<tr class="clear">
<td style="width:25%;" align="right" class="header">Predecessor&nbsp;</td>
<td style="width:25%;"><tmpl_var form.dependants></td>
<td style="width:25%;" align="right" class="header">% Complete&nbsp;</td>
<td style="width:25%;"><tmpl_var form.percentComplete></td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td colspan="2">
<table width="100%" cellpadding="3" cellspacing="0">
<tbody>
<tr class="clearNoBG" style="text-align:center;">
<td style="text-align:left;">
<span class="header">Resources:</span><br /><br />
<span style="margin-bottom:2px;padding-bottom:2px;padding-left:5px;-moz-box-sizing:border-box;display:block;">
<a style="text-decoration:none;" id="<tmpl_var form.addUser.id>" href="<tmpl_var form.addUser.link>" target="_new" onclick="taskEdit_searchPopup(this.href); return false;"><img style="border-style:none;vertical-align:middle;" title="<tmpl_var form.addUser.text>" alt="<tmpl_var form.addUser.text>" src="<tmpl_var assetExtras>/users.gif" /><span style="text-decoration:none;font-size:8pt;"> Add User To Task</span></a>&nbsp;&nbsp;&nbsp;&nbsp;
<a style="text-decoration:none;" id="<tmpl_var form.addGroup.id>" href="<tmpl_var form.addGroup.link>" target="_new" onclick="taskEdit_searchPopup(this.href); return false;"><img style="border-style:none;vertical-align:middle;" title="<tmpl_var form.addGroup.text>" alt="<tmpl_var form.addGroup.text>" src="<tmpl_var assetExtras>/groups.gif" /><span style="text-decoration:none;font-size:8pt;"> Add Group to Task</span></a>
</span><tmpl_var form.resourceDiv>
</td>
</tr>
<tr>
<td style="text-align:right;" class="header"><a href="javascript:void(checkEditTaskForm(document.editTaskForm))" class="saveBtn">Save</a></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<tmpl_var form.footer>
~~~
<style type="text/css">
.PM_small {
font-size:8pt;
}
tr.clearNoBG a {
text-decoration:none;
color:navy;
}
</style>

View file

@ -0,0 +1,275 @@
#ProjectManagerTMPL0002
<script type="text/javascript">
//<![CDATA[
dunits = "<tmpl_var project.durationUnits>";
hoursPerDay = <tmpl_var project.hoursPerDay>;
taskLength = <tmpl_var project.task.length>;
extrasPath = '<tmpl_var extras>';
errorMsgs = {
'name' : "<tmpl_var form.name.error>",
'start' : "<tmpl_var form.start.error>",
'end' : "<tmpl_var form.end.error>",
'greaterthan' : "<tmpl_var form.greaterthan.error>",
'invalidMove' : "<tmpl_var form.invalidMove.error>",
'noPredecessor' : "<tmpl_var form.noPredecessor.error>",
'samePredecessor' : "<tmpl_var form.samePredecessor.error>",
'previousPredecessor' : "<tmpl_var form.previousPredecessor.error>",
'untimedPredecessor' : "<tmpl_var form.untimedPredecessor.error>"
};
addEvent(window, "load", initPopUp);
//]]>
</script>
<tmpl_var form.header>
<table cellpadding="0" cellspacing="0" border="0" class="project" id="mastertable" style="width:<tmpl_var project.table.width>px;">
<tbody>
<tr>
<td style="width:20px;height:20px;">&nbsp;</td>
<td style="width:300px;" align="center">Task Name</td>
<td style="width:70px;" align="center">Duration</td>
<td style="width:70px;" align="center">Start</td>
<td style="width:70px;" align="center">Finish</td>
<td style="width:30px;" align="center">Pred</td>
<td rowspan="<tmpl_var project.gantt.rowspan>" valign="top" id="scrolltd" style="border-style:none;width:<tmpl_var project.scroll.percentWidth>%;">
<div class="scroller" id="gantt">
<tmpl_var project.ganttChart>
</div>
</td>
</tr>
<tr>
<td colspan="6">&nbsp;</td>
</tr>
<tmpl_loop task.loop>
<tr id="<tmpl_var task.row.id>">
<td align="center" style="height:20px"><tmpl_var task.number></td>
<td align="left" style="height:20px">
<tmpl_if task.canAdd>
<script type="text/javascript">
//<![CDATA[
var cMenu = new cMenu_createWithLink("id_<tmpl_var task.number>","<tmpl_var task.name>");
cMenu.addLink("<tmpl_var task.edit.url>","<tmpl_var task.edit.label>");
cMenu.addLink("<tmpl_var task.insertAbove.url>","<tmpl_var task.insertAbove.label>");
cMenu.addLink("<tmpl_var task.insertBelow.url>","<tmpl_var task.insertBelow.label>");
cMenu.addLink("<tmpl_var task.delete.url>","<tmpl_var task.delete.label>");
cMenu.print();
//]]>
</script>
<!-- <a href="<tmpl_var task.edit.url>" class="submodal-400-350"><tmpl_var task.name></a> -->
<tmpl_else>
<tmpl_var task.name>
</tmpl_if>
</td>
<td align="center" style="height:20px"><tmpl_var task.duration> <tmpl_var task.duration.units></td>
<td align="center" style="height:20px"><tmpl_var task.start></td>
<td align="center" style="height:20px"><tmpl_var task.end></td>
<td align="center" style="height:20px"><tmpl_var task.dependants></td>
<tmpl_var task.lagTime>
<tmpl_var task.taskType><tmpl_var task.seqNum>
</tr>
</tmpl_loop>
<tr><td colspan="6" style="border-style:none;">&nbsp;</td></tr>
<tr><td colspan="6" style="border-style:none;">&nbsp;</td></tr>
</tbody>
</table>
<tmpl_var form.footer>
<div id="links">
<!-- <tmpl_if project.canEdit><a href="<tmpl_var task.resources.url>"><tmpl_var task.resources.label></a>&nbsp;|&nbsp;</tmpl_if> -->
<tmpl_if task.canAdd><a href="<tmpl_var task.add.url>" class="submodal-400-300"><tmpl_var task.add.label></a>&nbsp;|&nbsp;</tmpl_if>
<a href="<tmpl_var task.back.url>"><tmpl_var task.back.label></a>
</div>
~~~
<style type="text/css">
body, html {
margin:0px;
padding:0px;
}
.project {
position:relative;
margin-top:5px;
margin-left:5px;
}
.project td {
border:solid silver 1px;
border-bottom:solid gray 1px;
font-size:9pt;
font-family:arial;
}
.project a {
color:#000000;
font-weight: normal;
font-size: 9pt;
text-decoration: none;
}
.project a:hover {
color:#7AB7E9;
font-weight: normal;
font-size: 9pt;
text-decoration: none;
}
.taskname {
font-family: verdana;
font-size: 10px;
font-weight: normal;
color: black;
width: 295px;
}
.taskduration {
font-family: verdana;
font-size: 10px;
font-weight: normal;
color: black;
width: 25px;
}
.taskdate {
font-family: verdana;
font-size: 10px;
font-weight: normal;
color: black;
width: 68px;
}
.taskdependant {
font-family: verdana;
font-size: 10px;
font-weight: normal;
color: black;
width: 20px;
}
tr.monthNames td {
text-align:center;
}
tr.dates td {
width:16px;
}
div.scroller {
overflow:scroll;
position:relative;
width:400px;
}
* html div.scroller {
overflow-x:scroll;
}
td.empty {
background-color:transparent;
border-style:none;
height:20px;
}
div.barPositions {
position:relative;
}
div.projectBar {
position:absolute;
height:10px;
z-index:2;
padding:0px;
margin:0px;
font-size:1pt;
}
div.statusBar {
background-color:#000;
position:absolute;
height:5px;
z-index:3;
padding:0px;
top:2px;
margin:0px;
font-size:1pt;
}
div.projectLineH {
background: url('images/dot.gif') top repeat-x;
font-size:1pt;
text-align:right;
position:absolute;
}
div.projectLineV {
width:1px;
background-color:black;
position:relative;
font-size:1pt;
height:100%;
float:right;
}
div.projectLabel {
position:absolute;
margin-top:-3px;
white-space:nowrap;
font-family: verdana;
font-size:8px;
}
div.milestone {
color:#000000;
position:absolute;
z-index:4;
margin:0px;
padding:0px;
margin-top:-3px;
font-size: 10px;
font-family: times;
}
#links {
margin-top:7px;
margin-left:5px;
font-family:arial;
font-size:9pt;
}
#links a {
color:#7AB7E9;
}
</style>
<style>
.popUp {
width:100%;
border:solid black 0px;
background-color:#F5F5F5;
}
.popUp td {
font-size:9pt;
font-family:arial;
border-top:solid #E0E0E0 1px;
}
.popUp tr.clear td {
border-style:none;
background-color:#F0F0F0;
}
.popUp tr.clearNoBG td {
border-style:none;
}
.popUp td.header {
font-weight:bold;
}
.popUp td span.header {
font-weight:bold;
}
a.saveBtn {
width:40px;
height:15px;
background-color:silver;
padding:1px;
padding-left:5px;
padding-right:5px;
color:white;
border:solid gray 1px;
text-decoration:none;
font-weight:bold;
display:block;
float:right;
text-align:center;
}
a.saveBtn:hover {
background-color:#F0F0F0;
color:gray;
}
</style>

View file

@ -537,7 +537,7 @@ sub www_editProject {
<label for="hoursPerDay_formId">$hpdLabel</label> <label for="hoursPerDay_formId">$hpdLabel</label>
</td> </td>
<td valign="top" class="tableData" style="width: *;"> <td valign="top" class="tableData" style="width: *;">
<input id="hoursPerDay_formId" type="text" name="hoursPerDay" value="$hpdValue" size="11" maxlength="14" onkeyup="doInputCheck(this.form.hoursPerDay,'0123456789-.')" /> <input id="hoursPerDay_formId" type="text" name="hoursPerDay" value="$hpdValue" size="11" maxlength="14" />
</td> </td>
</tr>|; </tr>|;
$f->raw($html); $f->raw($html);
@ -855,12 +855,15 @@ sub www_editTask {
-value=>$form->get("insertAt") -value=>$form->get("insertAt")
}); });
my $duration = $task->{duration};
my ($startEpoch, $endEpoch) = $db->quickArray('SELECT startDate, startDate FROM PM_task WHERE projectId = ? ORDER BY sequenceNumber LIMIT 1', [$projectId]); my ($startEpoch, $endEpoch) = $db->quickArray('SELECT startDate, startDate FROM PM_task WHERE projectId = ? ORDER BY sequenceNumber LIMIT 1', [$projectId]);
my $dependant = $task->{dependants};
my $duration = $task->{duration} || ($taskType eq 'timed')? (($project->{durationUnits} eq 'hours')? $project->{hoursPerDay} : 1) : 0;
$startEpoch = $endEpoch = time unless defined $startEpoch and defined $endEpoch;
$startEpoch = $task->{startDate} if $task->{startDate}; $startEpoch = $task->{startDate} if $task->{startDate};
$endEpoch = $task->{endDate} if $task->{endDate}; $endEpoch = $task->{endDate} if $task->{endDate};
$endEpoch += 86400 if $taskType eq 'timed' and !$task->{duration} and !$task->{endDate};
my ($start, $end) = ($dt->epochToSet($startEpoch), $dt->epochToSet($endEpoch)); my ($start, $end) = ($dt->epochToSet($startEpoch), $dt->epochToSet($endEpoch));
my $dependant = $task->{dependants};
# Set some hidden variables to make it easy to reset data in javascript # Set some hidden variables to make it easy to reset data in javascript
$var->{'form.header'} .= WebGUI::Form::hidden($session, { $var->{'form.header'} .= WebGUI::Form::hidden($session, {
@ -884,19 +887,27 @@ sub www_editTask {
-value=>$task->{taskName}, -value=>$task->{taskName},
-extras=>q|style="width:95%;"| -extras=>q|style="width:95%;"|
}); });
$var->{'form.seqNum'} = WebGUI::Form::hidden($session, {
-name => "seqNum",
-value => $seq,
});
my $durationEvents = qq|onchange="durationChanged(this.form, '', true)" onblur="if (this.value == 0) durationChanged(this.form, '', true)"|;
my $startDateEvents = qq|onfocus="doCalendar(this.id)" onblur="startDateChanged(this.form, '', true)"|;
my $endDateEvents = qq|onfocus="doCalendar(this.id)" onblur="endDateChanged(this.form, '', true)"|;
$var->{'form.duration'} = WebGUI::Form::float($session,{ $var->{'form.duration'} = WebGUI::Form::float($session,{
-name => "duration", -name => "duration",
-value => (($taskType ne 'milestone')? $task->{duration} : 'N/A'), -value => $duration,
-extras => qq|style="width:70%;" onchange="adjustTaskTimeFromDuration(this.form.start,this.form.end,this,this.form.lagTime,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,this.form.lagTime,true,this.form.dependants,this.form.orig_start,this.form.orig_end,'$seq') }" $disabledIfMilestone| -extras => qq|style="width:70%;" $durationEvents $disabledIfMilestone|
}); });
$var->{'form.duration.units'} = $self->_getDurationUnitHashAbbrev->{$project->{durationUnits}}; $var->{'form.duration.units'} = $self->_getDurationUnitHashAbbrev->{$project->{durationUnits}};
$var->{'form.lagTime'} = WebGUI::Form::float($session,{ $var->{'form.lagTime'} = WebGUI::Form::float($session,{
-name => "lagTime", -name => "lagTime",
-value => (($taskType eq 'timed')? $task->{lagTime} : 'N/A'), -value => (($taskType eq 'timed')? $task->{lagTime} : 'N/A'),
-extras => qq|style="width:70%;" onchange="adjustTaskTimeFromDuration(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.value == 0){ adjustTaskTimeFromDuration(this.form.start,this.form.end,this.form.duration,this,true,this.form.dependants,this.form.orig_start,this.form.orig_end,'$seq') }" $disabledIfUntimed| -extras => qq|style="width:70%;" $durationEvents $disabledIfUntimed|
}); });
$var->{'form.lagTime.units'} = $self->_getDurationUnitHashAbbrev->{$project->{durationUnits}}; $var->{'form.lagTime.units'} = $self->_getDurationUnitHashAbbrev->{$project->{durationUnits}};
@ -905,15 +916,15 @@ sub www_editTask {
-value=>$start, -value=>$start,
-size=>"10", -size=>"10",
-maxlength=>"10", -maxlength=>"10",
-extras=>qq|onfocus="doCalendar(this.id);" onblur="if(getTaskType(this.form) == 'milestone') this.form.end.value=this.value; adjustTaskTimeFromDate(this.form.start,this.form.end,this.form.duration,this.form.lagTime,this,true,this.form.dependants,this.form.orig_start,this.form.orig_end,'$seq');" style="width:88%;"| -extras=>qq|style="width:88%;" $startDateEvents|
}); });
$var->{'form.end'} = WebGUI::Form::text($session,{ $var->{'form.end'} = WebGUI::Form::text($session,{
-name=>"end", -name=>"end",
-value=>$end, -value=>$end,
-size=>"10", -size=>"10",
-maxlength=>"10", -maxlength=>"10",
-extras=>qq|onfocus="doCalendar(this.id);" style="width:88%;" onblur="adjustTaskTimeFromDate(this.form.start,this.form.end,this.form.duration,this.form.lagTime,this,true,this.form.dependants,this.form.orig_start,this.form.orig_end,'$seq');" $disabledIfUntimed| -extras=>qq|style="width:88%;" $endDateEvents $disabledIfUntimed|
}); });
$var->{'form.dependants'} = WebGUI::Form::integer($session,{ $var->{'form.dependants'} = WebGUI::Form::integer($session,{
@ -921,8 +932,8 @@ sub www_editTask {
-value=>$dependant || "", -value=>$dependant || "",
-defaultValue=>"", -defaultValue=>"",
-size=>4, -size=>4,
-maxlength=>10, -maxlength=>10,
-extras=>qq|style="width:50%;" onchange="validateDependant(this,this.form.orig_dependant,'$seq',this.form.start,this.form.end,this.form.duration,this.form.lagTime,true,true,this.form.orig_start,this.form.orig_end);"| -extras=>qq|style="width:50%;" onchange="predecessorChanged(this.form, '', true)"|
}); });
tie my %users, "Tie::IxHash"; tie my %users, "Tie::IxHash";
@ -969,12 +980,12 @@ sub www_editTask {
$var->{'form.save'} = WebGUI::Form::submit($session, { $var->{'form.save'} = WebGUI::Form::submit($session, {
-value=>"Save", -value=>"Save",
-extras=>q|name="subbutton"| -extras=>q|name="subbutton"|
}); });
$var->{'form.footer'} = WebGUI::Form::formFooter($session); $var->{'form.footer'} = WebGUI::Form::formFooter($session);
$var->{'extras'} = $config->get("extrasURL"); $var->{'extras'} = $config->get("extrasURL");
$var->{'assetExtras'} = $config->get("extrasURL").'/wobject/ProjectManager'; $var->{'assetExtras'} = $config->get("extrasURL").'/wobject/ProjectManager';
return $self->processTemplate($var,$self->getValue("editTaskTemplateId")) return $self->processTemplate($var,$self->getValue("editTaskTemplateId"))
} }
#------------------------------------------------------------------- #-------------------------------------------------------------------
@ -1171,22 +1182,21 @@ sub www_saveExistingTasks {
$self->_updateProject($projectId); $self->_updateProject($projectId);
$self->_updateDependantDates($projectId); $self->_updateDependantDates($projectId);
return $self->www_drawGanttChart(); return $self->www_drawGanttChart();
} }
#------------------------------------------------------------------- #-------------------------------------------------------------------
sub www_viewProject { sub www_viewProject {
my $self = shift; my $self = shift;
my $var = {}; my $var = {};
#Set Method Helpers #Set Method Helpers
my ($session,$privilege,$form,$db,$dt,$i18n,$user) = $self->setSessionVars; my ($session,$privilege,$form,$db,$dt,$i18n,$user) = $self->setSessionVars;
my $config = $session->config; my $config = $session->config;
my $style = $session->style; my $style = $session->style;
my $eh = $session->errorHandler; my $eh = $session->errorHandler;
my $projectId = $_[0] || $form->get("projectId"); my $projectId = $_[0] || $form->get("projectId");
#Check Privileges #Check Privileges
return $privilege->insufficient unless $self->_userCanObserveProject($user, $projectId); return $privilege->insufficient unless $self->_userCanObserveProject($user, $projectId);
my $extras = $config->get("extrasURL"); my $extras = $config->get("extrasURL");
my $assetExtras = $config->get("extrasURL")."/wobject/ProjectManager"; my $assetExtras = $config->get("extrasURL")."/wobject/ProjectManager";
@ -1194,7 +1204,7 @@ sub www_viewProject {
$var->{'extras'} = $assetExtras; $var->{'extras'} = $assetExtras;
$var->{'extras.base'} = $extras; $var->{'extras.base'} = $extras;
#Set Some Style stuff #Set Some Style stuff
$style->setLink($assetExtras."/subModal.css",{rel=>"stylesheet",type=>"text/css"}); $style->setLink($assetExtras."/subModal.css",{rel=>"stylesheet",type=>"text/css"});
$style->setLink($assetExtras."/taskEdit.css",{rel=>"stylesheet",type=>"text/css"}); $style->setLink($assetExtras."/taskEdit.css",{rel=>"stylesheet",type=>"text/css"});
$style->setLink($extras."/calendar/calendar-win2k-1.css",{rel=>"stylesheet",type=>"text/css"}); $style->setLink($extras."/calendar/calendar-win2k-1.css",{rel=>"stylesheet",type=>"text/css"});
@ -1245,7 +1255,8 @@ sub www_viewProject {
my $hash = {}; my $hash = {};
my $seq = $row->{sequenceNumber}; my $seq = $row->{sequenceNumber};
my $id = $row->{taskId}; my $id = $row->{taskId};
my $isUntimed = ($row->{taskType} ne 'timed'); my $taskType = $row->{taskType};
my $isUntimed = ($taskType ne 'timed');
my $startDate = $dt->epochToSet($row->{startDate}); my $startDate = $dt->epochToSet($row->{startDate});
my $endDate = $dt->epochToSet($row->{endDate}); my $endDate = $dt->epochToSet($row->{endDate});
my $duration = $row->{duration}; my $duration = $row->{duration};
@ -1256,78 +1267,71 @@ sub www_viewProject {
$hash->{'task.name'} = $row->{taskName}; $hash->{'task.name'} = $row->{taskName};
if($canEditTasks) { if($canEditTasks) {
my $startId = "start_".$id."_formId"; my $suffix = '_'.$id;
my $endId = "end_".$id."_formId";
my $durId = "duration_".$id."_formId";
my $lagId = "lagTime_".$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,{ $hash->{'task.start'} = WebGUI::Form::text($session,{
-name=>"start_$id", -name=>'start'.$suffix,
-value=>$startDate, -value=>$startDate,
-size=>"10", -size=>"10",
-maxlength=>"10", -maxlength=>"10",
-extras=>qq<onfocus="doCalendar(this.id);" class="taskdate" onblur="adjustTaskTimeFromDate(this,document.getElementById('$endId'),document.getElementById('$durId'),document.getElementById('$lagId'),this,false,document.getElementById('$predId'),document.getElementById('$origStartFieldId'),document.getElementById('$origEndFieldId'),'$seq');"> -extras=>qq<onfocus="doCalendar(this.id);" class="taskdate" onblur="startDateChanged(this.form, '$suffix', false);">
}); });
$hash->{'task.start'} .= WebGUI::Form::hidden($session,{ $hash->{'task.start'} .= WebGUI::Form::hidden($session,{
-name=>$origStartField, -name=>'orig_start'.$suffix,
-value=>$startDate, -value=>$startDate,
-extras=>qq|id="$origStartFieldId"|
}); });
$hash->{'task.dependants'} = WebGUI::Form::integer($session,{ $hash->{'task.dependants'} = WebGUI::Form::integer($session,{
-name=>"dependants_$id", -name=>'dependants'.$suffix,
-value=>$row->{dependants} || "", -value=>$row->{dependants} || "",
-defaultValue=>"", -defaultValue=>"",
-extras=>qq|class="taskdependant" onchange="validateDependant(this,document.getElementById('$origDepFieldId'),'$seq',document.getElementById('$startId'),document.getElementById('$endId'),document.getElementById('$durId'),document.getElementById('$lagId'),false,document.getElementById('$origStartFieldId'),document.getElementById('$origEndFieldId'));"| -extras=>qq|class="taskdependant" onchange="predecessorChanged(this.form, '$suffix', false);"|
}); });
$hash->{'task.dependants'} .= WebGUI::Form::hidden($session,{ $hash->{'task.dependants'} .= WebGUI::Form::hidden($session,{
-name=>$origDepField, -name=>'orig_dependants'.$suffix,
-value=>$row->{dependants}, -value=>$row->{dependants},
-extras=>qq|id="$origDepFieldId"|
}); });
$hash->{'task.end'} = WebGUI::Form::text($session,{ $hash->{'task.end'} = WebGUI::Form::text($session,{
-name=>"end_$id", -name=>'end'.$suffix,
-value=>$endDate, -value=>$endDate,
-size=>"10", -size=>"10",
-maxlength=>"10", -maxlength=>"10",
-extras=>qq|class="taskdate" onfocus="doCalendar(this.id);" onblur="adjustTaskTimeFromDate(document.getElementById('$startId'),this,document.getElementById('$durId'),document.getElementById('$lagId'),this,false,document.getElementById('$predId'),document.getElementById('$origStartFieldId'),document.getElementById('$origEndFieldId'),'$seq');"| -extras=>qq|class="taskdate" onfocus="doCalendar(this.id);" onblur="endDateChanged(this.form, '$suffix', false);"|
}); });
$hash->{'task.end'} .= WebGUI::Form::hidden($session,{ $hash->{'task.end'} .= WebGUI::Form::hidden($session,{
-name=>$origEndField, -name=>'orig_end'.$suffix,
-value=>$endDate, -value=>$endDate,
-extras=>qq|id="$origEndFieldId"|
}); });
# Don't display duration for untimed tasks. # Don't display duration for untimed tasks.
if ($isUntimed) { if ($isUntimed) {
$hash->{'task.duration'} = $row->{duration}; $hash->{'task.duration'} = $row->{duration};
$hash->{'task.duration'} .= WebGUI::Form::hidden($session,{ $hash->{'task.duration'} .= WebGUI::Form::hidden($session,{
-name=>"duration_$id", -name=>'duration'.$suffix,
-value=>$duration, -value=>$duration,
-extras=>qq|id="$durId"|
}); });
} else { } else {
$hash->{'task.duration'} = WebGUI::Form::float($session,{ $hash->{'task.duration'} = WebGUI::Form::float($session,{
-name=>"duration_$id", -name=>'duration'.$suffix,
-value=>$duration, -value=>$duration,
-extras=>qq|class="taskduration" onchange="adjustTaskTimeFromDuration(document.getElementById('$startId'),document.getElementById('$endId'),this,document.getElementById('$lagId'),false,document.getElementById('$predId'),document.getElementById('$origStartFieldId'),document.getElementById('$origEndFieldId'),'$seq');" | -extras=>qq|class="taskduration" onchange="durationChanged(this.form, '$suffix', false);" |
}); });
} }
$hash->{'task.lagTime'} = WebGUI::Form::hidden($session,{ $hash->{'task.lagTime'} = WebGUI::Form::hidden($session,{
-name => "lagTime_$id", -name => 'lagTime'.$suffix,
-value => $lagTime, -value => $lagTime,
-extras=>qq|id="$lagId"|
}); });
$hash->{'task.taskType'} = WebGUI::Form::hidden($session, {
-name => 'taskType'.$suffix,
-value => $taskType,
});
$hash->{'task.seqNum'} = WebGUI::Form::hidden($session, {
-name => 'seqNum'.$suffix,
-value => $seq
});
} else { } else {
$hash->{'task.duration'} = $duration; $hash->{'task.duration'} = $duration;
$hash->{'task.start'} = $startDate; $hash->{'task.start'} = $startDate;
@ -1335,7 +1339,6 @@ sub www_viewProject {
$hash->{'task.dependants'} = $row->{dependants} || "&nbsp;"; $hash->{'task.dependants'} = $row->{dependants} || "&nbsp;";
} }
$hash->{'task.duration.units'} = $dunits; $hash->{'task.duration.units'} = $dunits;
$hash->{'task.taskType'} = $row->{taskType};
if($canEditTasks) { if($canEditTasks) {
$hash->{'task.edit.url'} = $self->getUrl("func=editTask;projectId=$projectId;taskId=".$row->{taskId}); $hash->{'task.edit.url'} = $self->getUrl("func=editTask;projectId=$projectId;taskId=".$row->{taskId});
$hash->{'task.edit.label'} = $i18n->get("edit task label"); $hash->{'task.edit.label'} = $i18n->get("edit task label");

View file

@ -42,6 +42,8 @@ function closeImage() {
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
function configureForTaskType(form) { function configureForTaskType(form) {
var te = getTaskElements(form, '');
switch (getTaskType(form)) { switch (getTaskType(form)) {
case 'timed': case 'timed':
form.end.disabled = false; form.end.disabled = false;
@ -71,7 +73,6 @@ function configureForTaskType(form) {
break; break;
case 'milestone': case 'milestone':
form.end.value = form.start.value;
form.duration.value = 0; form.duration.value = 0;
form.duration.disabled = true; form.duration.disabled = true;
form.lagTime.value = 'N/A'; form.lagTime.value = 'N/A';
@ -82,6 +83,8 @@ function configureForTaskType(form) {
form.percentComplete.disabled = true; form.percentComplete.disabled = true;
break; break;
} }
setEndFromStartDate(te);
} }
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
@ -99,10 +102,107 @@ function setCheckedOfNodeList(list, value) {
return value; return value;
} }
function getTaskType(form) { return getCheckedOfNodeList(form.taskType); } // TODO: convert this whole bunch of stuff to do with task element groups
function setTaskType(form, value) { setCheckedOfNodeList(form.taskType, value); } // to an actual prototype/class?
function isTimed(form) { return getTaskType(form) == 'timed'; }
function isUntimed(form) { return !isTimed(form); } function getTaskElements(form, suffix) {
var te = new Object();
var keys = ['start', 'end', 'duration', 'lagTime', 'dependants', 'origStart', 'origEnd',
'seqNum', 'taskType', 'orig_start', 'orig_duration', 'orig_dependant', 'orig_end'];
for (var i = 0; i < keys.length; i++)
te[keys[i]] = form[keys[i]+suffix];
return te;
}
function getTaskType(te) {
var taskTypeElt = te.taskType;
if (taskTypeElt.type == 'hidden') return taskTypeElt.value;
else return getCheckedOfNodeList(taskTypeElt);
}
function setTaskType(te, value) {
var taskTypeElt = te.taskType;
if (taskTypeElt.type == 'hidden') taskTypeElt.value = value;
else setCheckedOfNodeList(taskTypeElt, value);
}
function isTimed(te) { return getTaskType(te) == 'timed'; }
function isUntimed(te) { return !isTimed(te); }
function fracDaysOfDuration(dur) {
if (dunits == 'hrs') return dur / hoursPerDay;
else return dur;
}
function durationOfFracDays(days) {
if (dunits == 'hrs') return days * hoursPerDay;
else return days;
}
function getDuration(te) { return fracDaysOfDuration(parseFloat(te.duration.value)); }
function setDuration(te, days) { te.duration.value = durationOfFracDays(days); }
function getDateByKey(te, key) {
var split = te[key].value.split('-');
return new Date(split[0], split[1]-1, split[2], 0, 0, 1, 0);
}
function getStartDate(te) { return getDateByKey(te, 'start'); }
function getEndDate(te) { return getDateByKey(te, 'end'); }
function setDateByKey(te, key, date) { te[key].value = intlDate(date); }
function setStartDate(te, date) { setDateByKey(te, 'start', date); }
function setEndDate(te, date) { setDateByKey(te, 'end', date); }
function setStartFromEndDate(te) { setStartDate(te, datePlusDays(getEndDate(te), -Math.floor(getTotalDuration(te)))); }
function setEndFromStartDate(te) { setEndDate(te, datePlusDays(getStartDate(te), Math.floor(getTotalDuration(te)))); }
function setOrigDates(te) {
te.orig_start.value = te.start.value;
te.orig_end.value = te.end.value;
}
function datePlusDays(date, days) {
var ret = new Date();
ret.setTime(date.getTime() + days * dayMS);
return ret;
}
function getTotalDuration(te) {
return fracDaysOfDuration(parseFloat(te.duration.value) +
parseFloatOrNA(te.lagTime.value));
}
function maybeDefaultTaskValues(te) {
var todayString = intlDate(new Date());
var tomorrowString = intlDate(datePlusDays(new Date(), 1));
var timed = isTimed(te);
if (te.duration.value == "") te.duration.value = timed? durationOfFracDays(1) : 0;
if (te.lagTime.value == "") te.lagTime.value = 0;
if (te.start.value == "" && te.end.value == "") {
te.start.value = todayString;
te.end.value = tomorrowString;
} else if (te.start.value == "") {
setEndFromStartDate(te);
} else if (te.end.value == "") {
setStartFromEndDate(te);
}
updateTaskArray(te);
}
function updateTaskArray(te) {
var seqNum = te.seqNum.value;
if (seqNum == "") return;
var assoc = taskArray[seqNum];
assoc.start = te.start.value;
assoc.end = te.end.value;
assoc.duration = te.duration.value;
assoc.lagTime = ''+parseInt(te.lagTime.value);
assoc.predecessor = te.dependants.value;
assoc.type = getTaskType(te);
}
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
function checkEditTaskForm (form) { function checkEditTaskForm (form) {
@ -131,173 +231,115 @@ function toDateObj(date) {
return dateObj; return dateObj;
} }
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------x
function adjustTaskTimeFromDuration(start, end, duration, lagTime, isTaskForm, predecessor, origStart, origEnd, seqNum) { function durationChanged(form, suffix, isTaskForm, gotDelay) {
//set the form element if (!isTaskForm && !gotDelay)
var form = duration.form; return window.setTimeout(function() {
durationChanged(form, suffix, isTaskForm, true); }, 1);
var today = new Date();
var todayIntl = intlDate(today);
// Set values if not already set. var te = getTaskElements(form, suffix);
if (start.value == "") start.value = todayIntl; maybeDefaultTaskValues(te);
if (end.value == "") end.value = todayIntl;
if (duration.value == "") duration.value = hoursPerDay; // Get the new duration.
var duration = getDuration(te);
// Convert hours to days. var totalDuration = getTotalDuration(te);
var taskDuration = parseFloat(duration.value);
var taskTotalDuration = taskDuration + parseFloatOrNA(lagTime.value);
if(dunits == "hrs") taskTotalDuration = taskTotalDuration / hoursPerDay;
var totalDurationFloor = Math.floor(taskTotalDuration);
// We can't have timed tasks with duration zero. Those are called "milestones". // We can't have timed tasks with duration zero. Those are called "milestones".
if (taskDuration <= 0 && getTaskType(form) == 'timed') { if (duration <= 0 && getTaskType(te) == 'timed') {
if (isTaskForm) { if (isTaskForm) {
// Convert to milestone if desired. // Convert to milestone if desired.
if (confirm("Zero duration tasks are considered milestones. Do you wish to change this task to a milestone?")) { if (confirm("Zero duration tasks are considered milestones. Do you wish to change this task to a milestone?")) {
setTaskType(form, 'milestone'); setTaskType(te, 'milestone');
configureForTaskType(form); configureForTaskType(form);
} else { } else {
duration.value = form.orig_duration.value; form.duration.value = form.orig_duration.value;
if (parseFloat(duration.value) <= 0) if (getDuration(te) <= 0)
duration.value = hoursPerDay; setDuration(te, 1);
} }
} else { } else {
// Do not let users zero out tasks from the quick view. // Do not let users zero out tasks from the quick view.
alert("Zero duration tasks are considered Milestones. Please edit the task by clicking the link if you wish to change this task to a milestone"); alert("Zero duration tasks are considered Milestones. Please edit the task by clicking the link if you wish to change this task to a milestone");
} }
return;
} }
//create the start date switch (getTaskType(te)) {
var aTo = start.value.split("-"); case 'timed':
var toDate = new Date(aTo[0],(aTo[1]-1),aTo[2],0,0,1,0); case 'progressive':
setEndDate(te, datePlusDays(getStartDate(te), Math.floor(totalDuration)));
//add new duration days to the start date break;
toDate.setDate(toDate.getDate() + totalDurationFloor);
case 'milestone':
//set end date to this date setEndDate(te, getStartDate(te));
end.value = intlDate(toDate); break;
}
//Set new duration in taskArray
taskArray[seqNum]["duration"] = taskDuration; te.orig_duration.value = te.duration.value;
//Adjust time based on new end date updateTaskArray(te);
adjustTaskTimeFromDate(start, end, duration, lagTime, end, isTaskForm, predecessor, origStart, origEnd, seqNum);
if (!isTaskForm) {
updateDependantDates();
paintGanttChart();
}
} }
//-------------------------------------------------------------------------------------- function checkPredecessorCollision(te, isTaskForm) {
function adjustTaskTimeFromDate(start, end, duration, lagTime, element, isTaskForm, predecessor, origStart, origEnd, seqNum) { var predecessor = te.dependants.value;
//set the form element if (predecessor == "") return;
var form = element.form; var predAssoc = taskArray[predecessor];
//set original duration from task form to determine whether or not to continue to set duration var predEnd = toDateObj(predAssoc.end);
var orig_duration; if (predEnd.getTime() >= getStartDate(te).getTime()) {
setStartDate(te, predEnd);
if (isTaskForm) { setEndFromStartDate(te);
if (isUntimed(form)) return;
orig_duration = form.orig_duration.value;
} }
}
//Handle case where both start and end are empty
if (start.value == "" && end.value == "") { function dateChanged(form, suffix, isTaskForm, setWhichWay, gotDelay) {
//get today's date if (!isTaskForm && !gotDelay)
var today = new Date(); return window.setTimeout(function() {
var todayIntl = intlDate(today); dateChanged(form, suffix, isTaskForm, setWhichWay, true); }, 1);
//set start and end date if not already set
start.value = todayIntl; var te = getTaskElements(form, suffix);
end.value = todayIntl; maybeDefaultTaskValues(te);
setWhichWay(te);
checkPredecessorCollision(te, isTaskForm);
setOrigDates(te);
updateTaskArray(te);
if (!isTaskForm) {
updateDependantDates();
paintGanttChart();
} }
}
//Handle case where one is set and the other isn't
if (end.value == "") end.value = start.value; function startDateChanged(form, suffix, isTaskForm) { dateChanged(form, suffix, isTaskForm, setEndFromStartDate); }
if (start.value == "") start.value = end.value; function endDateChanged(form, suffix, isTaskForm) { dateChanged(form, suffix, isTaskForm, setStartFromEndDate); }
if (isTaskForm && orig_duration == "") { function predecessorChanged(form, suffix, isTaskForm, gotDelay) {
//Set duration if this is a new record if (!isTaskForm && !gotDelay)
//Check to make sure start date comes before end date return window.setTimeout(function() {
var startcomp = start.value.replace(/-/g,""); predecessorChanged(form, suffix, isTaskForm, true); }, 1);
var endcomp = end.value.replace(/-/g,"");
if(startcomp > endcomp) { var te = getTaskElements(form, suffix);
alert(errorMsgs.greaterthan); var seqNum = te.seqNum.value, predecessor = te.dependants.value;
if(element.name == "start") {
end.value = element.value; if (predecessor != "") {
} else { var assoc = taskArray[predecessor];
start.value = element.value; var revert = function() { te.dependants.value = te.orig_dependant.value; return; }
}
duration.value = (dunits == "hrs")?hoursPerDay:1; if (predecessor < 1) { alert(errorMsgs.noPredecessor); return revert(); }
lagTime.value = 0; if (seqNum != "") {
return; if (predecessor == seqNum) { alert(errorMsgs.samePredecessor); return revert(); }
} if (predecessor > seqNum) { alert(errorMsgs.previousPredecessor); return revert(); }
var d = getDaysInterval(start.value,end.value);
if(d == 0) d = 1;
if(dunits == "hrs") {
d = d * hoursPerDay;
}
duration.value = d - lagTime.value;
} else {
//Set start/end if duration has been saved
var d = parseFloat(duration.value) + parseFloatOrNA(lagTime.value);
if(dunits == "hrs") {
//Convert to days
d = d / hoursPerDay;
}
//Round off duration or set it to zero if less than 1;
//alert("d = " + d + " floor = " + Math.floor(d));
if(d < 1) d = 0;
else d = Math.floor(d);
if(element.name.indexOf("start") > -1) {
//create the date
var aTo = start.value.split("-");
var toDate = new Date(aTo[0],(aTo[1]-1),aTo[2],0,0,1,0);
//add duration days to the start date
toDate.setDate(toDate.getDate() + d);
//set end date to this date
end.value = intlDate(toDate);
} else if(element.name.indexOf("end") > -1) {
//create the date
var aFrom = end.value.split("-");
var fromDate = new Date(aFrom[0],(aFrom[1]-1),aFrom[2],0,0,1,0);
//subtract duration days from the end date
fromDate.setDate(fromDate.getDate() - d);
//set start date to this date
start.value = intlDate(fromDate);
} }
if (assoc["type"] != 'timed') { alert(errorMsgs.untimedPredecessor); return revert(); }
} }
//Check Predecessors before moving stuff te.orig_dependant.value = te.dependants.value;
var pred = predecessor.value; checkPredecessorCollision(te, isTaskForm);
if(pred != "") { updateTaskArray(te);
//Check to make sure that the dependency requirement for this task is still valid
//Get the predecessor end date if (!isTaskForm) {
var taskStart = toDateObj(start.value); updateDependantDates();
var predTaskEnd;
if(isTaskForm) {
predTaskEnd = toDateObj(taskArray[pred]["end"]);
} else {
var predTaskEndId = "end_"+taskArray[pred]["id"]+"_formId"
predTaskEnd = toDateObj(document.getElementById(predTaskEndId).value);
}
if(taskStart.getTime() < predTaskEnd.getTime()) {
alert(errorMsgs.invalidMove);
start.value = origStart.value;
end.value = origEnd.value;
return;
}
}
//Check all tasks past this one and move them forward if necessary (this only needs to happen on the main form)
if(!isTaskForm) {
arrangePredecessors(element,seqNum);
}
//reset orig start and end values
origStart.value = start.value;
origEnd.value = end.value;
if(!isTaskForm) {
//Adjust task form for
paintGanttChart(); paintGanttChart();
} }
} }
@ -308,7 +350,7 @@ function trim(str) {
} }
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
function arrangePredecessors (element,seqNum) { function updateDependantDates() {
for (var i = 1; i <= taskLength; i++) { for (var i = 1; i <= taskLength; i++) {
var seq = i; var seq = i;
var task = taskArray[seq]; var task = taskArray[seq];
@ -394,78 +436,3 @@ function paintGanttChart () {
document.getElementById("mastertable").style.width=mwidth; document.getElementById("mastertable").style.width=mwidth;
document.getElementById("scrolltd").style.width=swidth; document.getElementById("scrolltd").style.width=swidth;
} }
//--------------------------------------------------------------------------------------
function validateDependant(field,origField,seqNum,start,end,duration,lagTime,isTaskForm,origStart,origEnd) {
var pred = field.value;
var newTask = false;
if(pred != "") {
if (seqNum == "") seqNum = taskLength+1;
if (pred < 1) {
alert(errorMsgs.noPredecessor);
field.value = origField.value;
return;
}
if (pred == seqNum) {
alert(errorMsgs.samePredecessor);
field.value = origField.value;
return;
}
if (pred > seqNum) {
alert(errorMsgs.previousPredecessor);
field.value = origField.value;
return;
}
if (taskArray[pred]["type"] != 'timed') {
alert(errorMsgs.untimedPredecessor);
field.value = origField.value;
return;
}
//Set defaults if it's a new record and one of the other options hasn't been checked.
if(start.value == "" || end.value == "") {
//get today's date
newTask = true;
duration.value = (dunits == "hrs")?hoursPerDay:1;
}
//Get the predecessor end date and decide where the new start date belongs
var taskStart = start.value;
var taskStartObj = toDateObj(taskStart);
var predTaskEnd = taskArray[pred]["end"];
var predTaskEndObj = toDateObj(predTaskEnd);
//Change start date if it comes before predecessor end date
if(newTask || (taskStartObj.getTime() < predTaskEndObj.getTime())) {
//Convert predecessor hours to days
var taskTotalDuration = parseFloat(duration.value) + parseFloatOrNA(lagTime.value);
if(dunits == "hrs") taskTotalDuration = taskTotalDuration / hoursPerDay;
var totalDurationFloor = Math.floor(taskTotalDuration);
//Get the predecessor dayPart
var predDayPart = parseFloat(pred["dayPart"]);
if(predDayPart > 0) {
//The previous task took up part of a day. Add the additional day part to the duration
taskTotalDuration += predDayPart;
totalDurationFloor = Math.floor(totalDurationInDays);
}
//Set the start date of this task to the end date of the predecessor
start.value = predTaskEnd;
//Adjust end date for change in start date
adjustTaskTimeFromDate(start,end,duration,lagTime,start,isTaskForm,field,origStart,origEnd,seqNum);
return;
}
}
//Repaint
if(!isTaskForm) {
//Set new predecessor in taskArray
taskArray[seqNum]["predecessor"] = pred;
paintGanttChart();
}
}