Index: openacs-4/contrib/packages/project-manager/project-manager.info =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/project-manager/Attic/project-manager.info,v diff -u -r1.16.2.17 -r1.16.2.18 --- openacs-4/contrib/packages/project-manager/project-manager.info 8 Oct 2004 21:24:57 -0000 1.16.2.17 +++ openacs-4/contrib/packages/project-manager/project-manager.info 26 Oct 2004 01:22:26 -0000 1.16.2.18 @@ -7,14 +7,14 @@ <initial-install-p>f</initial-install-p> <singleton-p>f</singleton-p> - <version name="2.21b1" url="http://openacs.org/repository/download/apm/project-manager-2.21b1.apm"> + <version name="2.50b1" url="http://openacs.org/repository/download/apm/project-manager-2.50b1.apm"> <owner url="mailto:jader@bread.com">Jade Rubick</owner> <summary>Project management tool for OpenACS</summary> - <release-date>2004-10-08</release-date> + <release-date>2004-10-13</release-date> <vendor url="mailto:jader@bread.com">Integrated Bakery Resources</vendor> <description format="text/plain">Track tasks, estimates and actual progress for a project. See the <a href="http://openacs.org/projects/dotwrk/project_management/">project page</a> for more information.</description> - <provides url="project-manager" version="2.21b1"/> + <provides url="project-manager" version="2.50b1"/> <requires url="acs-datetime" version="4.0"/> <requires url="acs-templating" version="4.6.4"/> <requires url="calendar" version="2.0.1"/> @@ -25,8 +25,8 @@ <requires url="organizations" version="0.3d"/> <callbacks> - <callback type="after-instantiate" proc="pm::install::package_instantiate"/> <callback type="before-uninstantiate" proc="pm::install::package_uninstantiate"/> + <callback type="after-instantiate" proc="pm::install::package_instantiate"/> </callbacks> <parameters> <parameter datatype="string" min_n_values="1" max_n_values="1" name="LoggerPrimaryURL" description="This is the primary logger instance that is linked in to this project-manager instance. Links to this instance are added to Fisheye: Tag 1.1 refers to a dead (removed) revision in file `openacs-4/contrib/packages/project-manager/lib/process-instances-postgresql.xql'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.1 refers to a dead (removed) revision in file `openacs-4/contrib/packages/project-manager/lib/process-instances.adp'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.1 refers to a dead (removed) revision in file `openacs-4/contrib/packages/project-manager/lib/process-instances.tcl'. Fisheye: No comparison available. Pass `N' to diff? Index: openacs-4/contrib/packages/project-manager/sql/postgresql/project-manager-functions-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/project-manager/sql/postgresql/Attic/project-manager-functions-create.sql,v diff -u -r1.13.2.3 -r1.13.2.4 --- openacs-4/contrib/packages/project-manager/sql/postgresql/project-manager-functions-create.sql 2 Jul 2004 23:13:48 -0000 1.13.2.3 +++ openacs-4/contrib/packages/project-manager/sql/postgresql/project-manager-functions-create.sql 26 Oct 2004 01:22:27 -0000 1.13.2.4 @@ -434,7 +434,7 @@ -- If it is not associated with a project, then it is placed in the root -- project repository folder. -select define_function_args('pm_task__new_task_item', 'project_id, title, description, html_p, end_date, percent_complete, estimated_hours_work, estimated_hours_work_min, estimated_hours_work_max, status_id, creation_date, creation_user, creation_ip, package_id'); +select define_function_args('pm_task__new_task_item', 'project_id, title, description, html_p, end_date, percent_complete, estimated_hours_work, estimated_hours_work_min, estimated_hours_work_max, status_id, process_instance_id, creation_date, creation_user, creation_ip, package_id'); create or replace function pm_task__new_task_item ( integer, -- project_id @@ -447,6 +447,7 @@ numeric, -- estimated_hours_work_min numeric, -- estimated_hours_work_max, integer, -- status_id + integer, -- process_instance_id timestamptz, -- creation_date integer, -- creation_user varchar, -- creation_ip @@ -464,10 +465,11 @@ p_estimated_hours_work_min alias for $8; p_estimated_hours_work_max alias for $9; p_status_id alias for $10; - p_creation_date alias for $11; - p_creation_user alias for $12; - p_creation_ip alias for $13; - p_package_id alias for $14; + p_process_instance_id alias for $11; + p_creation_date alias for $12; + p_creation_user alias for $13; + p_creation_ip alias for $14; + p_package_id alias for $15; v_item_id cr_items.item_id%TYPE; v_revision_id cr_revisions.revision_id%TYPE; @@ -515,9 +517,9 @@ PERFORM content_item__set_live_revision (v_revision_id); insert into pm_tasks ( - task_id, task_number, status) + task_id, task_number, status, process_instance) values ( - v_item_id, v_task_number, p_status_id); + v_item_id, v_task_number, p_status_id, p_process_instance_id); insert into pm_tasks_revisions ( task_revision_id, end_date, percent_complete, estimated_hours_work, estimated_hours_work_min, estimated_hours_work_max, actual_hours_worked) Index: openacs-4/contrib/packages/project-manager/sql/postgresql/project-manager-table-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/project-manager/sql/postgresql/Attic/project-manager-table-create.sql,v diff -u -r1.19.2.5 -r1.19.2.6 --- openacs-4/contrib/packages/project-manager/sql/postgresql/project-manager-table-create.sql 8 Oct 2004 21:25:00 -0000 1.19.2.5 +++ openacs-4/contrib/packages/project-manager/sql/postgresql/project-manager-table-create.sql 26 Oct 2004 01:22:27 -0000 1.19.2.6 @@ -157,6 +157,146 @@ '; +-- PROCESSES + +create sequence pm_process_seq; + +create table pm_process ( + process_id integer + constraint pm_process_id_pk + primary key, + one_line varchar(200) + constraint pm_process_one_line_nn + not null, + description varchar(1000), + party_id integer + constraint pm_process_party_fk + references parties + constraint pm_process_party_nn + not null, + creation_date timestamptz, + deleted_p char(1) default 'f' + constraint pm_process_deleted_p_ck + check (deleted_p in ('t','f')), +); + +comment on table pm_process is ' + Processes are a set of templates for tasks, so that people can + create sets of tasks quickly. Their structure needs to match that of + tasks. The process holds the meta information, and is also an identifier + that is used by the user to select which process they''d like to copy or + use +'; + +create or replace view +pm_process_active as + SELECT * FROM pm_process where deleted_p = 'f'; + +-- each time a process is used, it creates an instance of that process +-- we use this to allow a user to see overviews of process status, etc.. + +create sequence pm_process_instance_seq start 1; + +create table pm_process_instance ( + instance_id integer + constraint pm_process_instance_id_pk + primary key, + name varchar(200), + process_id integer + constraint pm_process_instance_process_fk + references pm_process on delete cascade, + project_item_id integer + constraint pm_process_project_fk + references cr_items +); + + +create sequence pm_process_task_seq; + +create table pm_process_task ( + process_task_id integer + constraint pm_process_task_id_pk + primary key, + process_id integer + constraint pm_process_process_id_fk + references + pm_process + constraint pm_process_process_id_nn + not null, + one_line varchar(200) + constraint pm_process_task_one_line_nn + not null, + description varchar(4000), + mime_type varchar(200) + constraint pm_process_task_mime_type_fk + references cr_mime_types(mime_type) + on update no action on delete no action + default 'text/plain' + -- dates are optional, because it may be computed in reference + -- to all other items, or simply not have a deadline + -- percent complete is always 0 + estimated_hours_work numeric, + -- PERT charts require minimum and maximum estimates + -- these are optionally used + estimated_hours_work_min numeric, + estimated_hours_work_max numeric, + ordering integer +); + +comment on table pm_process_task is ' + A template for the tasks that will be created by the process +'; + +create sequence pm_process_task_dependency_seq; + +create table pm_process_task_dependency ( + dependency_id integer + constraint pm_proc_task_dependcy_pk + primary key, + process_task_id integer + constraint pm_proc_task_proc_task_fk + references pm_process_task + on delete cascade, + parent_task_id integer + constraint pm_proc_task_parent_id_fk + references pm_process_task + on delete cascade, + dependency_type varchar + constraint pm_process_task_dep_type + references pm_task_dependency_types, + constraint pm_proc_task_depend_uq + unique (process_task_id, parent_task_id) +); + +comment on table pm_process_task_dependency is ' + Keeps track of dependencies. Used to create the dependencies in the + new tasks. +'; + +create table pm_process_task_assignment ( + process_task_id integer + constraint pm_proc_task_assign_task_fk + references pm_process_task(process_task_id) + on delete cascade, + role_id integer + constraint pm_task_assignment_role_fk + references pm_roles, + party_id integer + constraint pm_task_assignment_party_fk + references parties(party_id) + on delete cascade, + constraint pm_proc_task_assgn_uq + unique (process_task_id, role_id, party_id) +); + + +comment on table pm_process_task_assignment is ' + Maps who is assigned to process tasks. These will be the default people + assigned to the new tasks +'; + + + -- TASKS -- we create two tables to store task information @@ -197,12 +337,16 @@ references pm_task_status, deleted_p char(1) default 'f' constraint pm_tasks_deleted_p_ck - check (deleted_p in ('t','f')) + check (deleted_p in ('t','f')), + process_instance integer + constraint pm_tasks_process_instance_fk + references + pm_process_instance; ); CREATE OR REPLACE view pm_tasks_active as - SELECT task_id, task_number, status FROM pm_tasks where deleted_p = 'f'; + SELECT task_id, task_number, status, process_instance FROM pm_tasks where deleted_p = 'f'; create table pm_tasks_revisions ( @@ -431,7 +575,7 @@ ); --- WORKGROUPS +-- WORKGROUPS: currently not used create sequence pm_workgroup_seq; @@ -486,6 +630,8 @@ Maps who is a part of what task, and in what capacity '; +-- TASK CROSS REFERENCES + create table pm_task_xref ( task_id_1 integer constraint pm_task_xref_task1_nn @@ -507,113 +653,6 @@ '; --- PROCESSES - -create sequence pm_process_seq; - -create table pm_process ( - process_id integer - constraint pm_process_id_pk - primary key, - one_line varchar(200) - constraint pm_process_one_line_nn - not null, - description varchar(1000), - party_id integer - constraint pm_process_party_fk - references parties - constraint pm_process_party_nn - not null, - creation_date timestamptz -); - -comment on table pm_process is ' - Processes are a set of templates for tasks, so that people can - create sets of tasks quickly. Their structure needs to match that of - tasks. The process holds the meta information, and is also an identifier - that is used by the user to select which process they''d like to copy or - use -'; - -create sequence pm_process_task_seq; - -create table pm_process_task ( - process_task_id integer - constraint pm_process_task_id_pk - primary key, - process_id integer - constraint pm_process_process_id_fk - references - pm_process - constraint pm_process_process_id_nn - not null, - one_line varchar(200) - constraint pm_process_task_one_line_nn - not null, - description varchar(4000), - -- dates are optional, because it may be computed in reference - -- to all other items, or simply not have a deadline - -- percent complete is always 0 - estimated_hours_work numeric, - -- PERT charts require minimum and maximum estimates - -- these are optionally used - estimated_hours_work_min numeric, - estimated_hours_work_max numeric, - ordering integer -); - -comment on table pm_process_task is ' - A template for the tasks that will be created by the process -'; - -create sequence pm_process_task_dependency_seq; - -create table pm_process_task_dependency ( - dependency_id integer - constraint pm_proc_task_dependcy_pk - primary key, - process_task_id integer - constraint pm_proc_task_proc_task_fk - references pm_process_task - on delete cascade, - parent_task_id integer - constraint pm_proc_task_parent_id_fk - references pm_process_task - on delete cascade, - dependency_type varchar - constraint pm_process_task_dep_type - references pm_task_dependency_types, - constraint pm_proc_task_depend_uq - unique (process_task_id, parent_task_id) -); - -comment on table pm_process_task_dependency is ' - Keeps track of dependencies. Used to create the dependencies in the - new tasks. -'; - -create table pm_process_task_assignment ( - process_task_id integer - constraint pm_proc_task_assign_task_fk - references pm_process_task(process_task_id) - on delete cascade, - role_id integer - constraint pm_task_assignment_role_fk - references pm_roles, - party_id integer - constraint pm_task_assignment_party_fk - references parties(party_id) - on delete cascade, - constraint pm_proc_task_assgn_uq - unique (process_task_id, role_id, party_id) -); - - -comment on table pm_process_task_assignment is ' - Maps who is assigned to process tasks. These will be the default people - assigned to the new tasks -'; - create table pm_users_viewed ( viewing_user integer constraint pm_users_viewed_viewing_user_fk Fisheye: Tag 1.1 refers to a dead (removed) revision in file `openacs-4/contrib/packages/project-manager/sql/postgresql/upgrade/upgrade-2.21b1-2.50b1.sql'. Fisheye: No comparison available. Pass `N' to diff? Index: openacs-4/contrib/packages/project-manager/tcl/calendar-procs-postgresql.xql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/project-manager/tcl/Attic/calendar-procs-postgresql.xql,v diff -u -r1.1.2.6 -r1.1.2.7 --- openacs-4/contrib/packages/project-manager/tcl/calendar-procs-postgresql.xql 15 Sep 2004 23:05:51 -0000 1.1.2.6 +++ openacs-4/contrib/packages/project-manager/tcl/calendar-procs-postgresql.xql 26 Oct 2004 01:22:27 -0000 1.1.2.7 @@ -36,7 +36,8 @@ p.first_names || ' ' || p.last_name || ' (' || substring(r.one_line from 1 for 1) || ')' as full_name, p.person_id, - s.status_type as status + s.status_type as status, + r.is_lead_p FROM pm_tasks_active ts, pm_task_status s, Index: openacs-4/contrib/packages/project-manager/tcl/calendar-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/project-manager/tcl/Attic/calendar-procs.tcl,v diff -u -r1.1.2.8 -r1.1.2.9 --- openacs-4/contrib/packages/project-manager/tcl/calendar-procs.tcl 24 Sep 2004 20:53:52 -0000 1.1.2.8 +++ openacs-4/contrib/packages/project-manager/tcl/calendar-procs.tcl 26 Oct 2004 01:22:27 -0000 1.1.2.9 @@ -86,13 +86,21 @@ # highlight what you're assigned to. if {[string equal $person_id $user_id]} { - set font_begin "<strong>" - set font_end "</strong>" + set font_begin "<span class=\"selected\">" + set font_end "</span>" } else { set font_begin "" set font_end "" } + if { \ + ![empty_string_p $is_lead_p] && \ + [string is true $is_lead_p]} { + + set font_begin "$font_begin<i>" + set font_end "</i>$font_end" + } + # if this is another row of the same item, just add the name. if {[string equal $last_task_id $task_id]} { append day_details "<li>, ${font_begin}${full_name}${font_end}</li>" Fisheye: Tag 1.1 refers to a dead (removed) revision in file `openacs-4/contrib/packages/project-manager/tcl/process-procs-postgresql.xql'. Fisheye: No comparison available. Pass `N' to diff? Index: openacs-4/contrib/packages/project-manager/tcl/process-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/project-manager/tcl/Attic/process-procs.tcl,v diff -u -r1.2.2.1 -r1.2.2.2 --- openacs-4/contrib/packages/project-manager/tcl/process-procs.tcl 2 Jul 2004 23:13:48 -0000 1.2.2.1 +++ openacs-4/contrib/packages/project-manager/tcl/process-procs.tcl 26 Oct 2004 01:22:27 -0000 1.2.2.2 @@ -27,21 +27,7 @@ @error } { - db_transaction { - db_dml delete_tasks { - DELETE FROM - pm_process_task - WHERE - process_id = :process_id - } - - db_dml delete_process { - DELETE FROM - pm_process - WHERE - process_id = :process_id - } - } + db_dml delete_process { } } @@ -182,3 +168,434 @@ return } + + +ad_proc -public pm::process::get { + {-process_id:required} + {-process_task_id ""} + {-one_line_array:required} + {-description_array:required} + {-description_mime_type_array:required} + {-estimated_hours_work_array:required} + {-estimated_hours_work_min_array:required} + {-estimated_hours_work_max_array:required} + {-dependency_array:required} + {-tasks_list:required} +} { + Sets a bunch of information in a set of arrays on all + process tasks for a given process + + @author Jade Rubick (jader@bread.com) + @creation-date 2004-09-23 + + @param process_id + + @param one_line_array + + @param description_array + + @param estimated_hours_work_array + + @param estimated_hours_work_min_array + + @param estimated_hours_work_max_array + + @param dependency_array + + @param tasks_list + + @return + + @error +} { + + # set variables in calling environment, using names passed in + upvar 1 $one_line_array one_line_arr + upvar 1 $description_array description_arr + upvar 1 $description_mime_type_array description_mime_type_arr + upvar 1 $estimated_hours_work_array estimated_hours_work_arr + upvar 1 $estimated_hours_work_min_array estimated_hours_work_min_arr + upvar 1 $estimated_hours_work_max_array estimated_hours_work_max_arr + upvar 1 $dependency_array dependency_arr + upvar 1 $tasks_list process_tasks_l + + if {[exists_and_not_null process_task_id]} { + set process_task_where_clause " and t.process_task_id in ([join $process_task_id ", "])" + } else { + set process_task_where_clause "" + } + + db_foreach get_process_tasks { } { + set one_line_arr($process_tid) $one_line + set description_arr($process_tid) $description + set description_mime_type_arr($process_tid) $description_mime_type + set estimated_hours_work_arr($process_tid) $estimated_hours_work + set estimated_hours_work_min_arr($process_tid) $estimated_hours_work_min + set estimated_hours_work_max_arr($process_tid) $estimated_hours_work_max + set dependency_arr($process_tid) $process_parent_task + + # make sure that we don't have empty values for estimated + # hours work + if {[empty_string_p $estimated_hours_work_arr($process_tid)]} { + set estimated_hours_work_arr($process_tid) 0 + } + if {[empty_string_p $estimated_hours_work_min_arr($process_tid)]} { + set estimated_hours_work_min_arr($process_tid) 0 + } + if {[empty_string_p $estimated_hours_work_max_arr($process_tid)]} { + set estimated_hours_work_max_arr($process_tid) 0 + } + + + lappend process_tasks_l $process_tid + } + +} + + +ad_proc -public pm::process::select_html { +} { + Returns the option part of the + HTML for a select list of process options + + @author Jade Rubick (jader@bread.com) + @creation-date 2004-10-14 + + @return + + @error +} { + + set html "<option value=\"\">Select process</option>" + + db_foreach get_processes get_processes { + append html "<option value=\"$process_id\">$process_name</option>\n" + } + + return $html +} + + +ad_proc -public pm::process::task_assignee_role_list { + {-process_task_id:required} +} { + Returns a list of lists, with all assignees to a particular + process task. {{party_id role_id} {party_id role_id}} + + @author Jade Rubick (jader@bread.com) + @creation-date 2004-10-15 + + @param process_task_id + + @return + + @error +} { + + return [db_list_of_lists get_assignees_roles { }] + +} + + +ad_proc -public pm::process::instantiate { + {-process_id:required} + {-project_item_id:required} + {-name:required} +} { + Returns a unique process instance id + + @author Jade Rubick (jader@bread.com) + @creation-date 2004-10-15 + + @return + + @error +} { + + set instance_id [db_nextval pm_process_instance_seq] + + db_dml add_instance { } + + return $instance_id +} + + +ad_proc -public pm::process::instances { + {-project_item_id:required} +} { + Returns a list of lists of form + {{process_instance_id process_instance_name} { } ...} + + of processes in use by tasks in a particular project + + @author Jade Rubick (jader@bread.com) + @creation-date 2004-10-15 + + @param project_item_id + + @return + + @error +} { + + return [db_list_of_lists get_process_instance { }] + +} + + +ad_proc -public pm::process::instance_options { + {-project_item_id:required} + {-process_instance_id ""} +} { + Returns the options portion of HTML for process instances + associated with tasks in a project + + @author Jade Rubick (jader@bread.com) + @creation-date 2004-10-15 + + @param project_item_id + + @return + + @error +} { + + set instances [pm::process::instances -project_item_id $project_item_id] + + set html "" + + foreach inst $instances { + set instance_id [lindex $inst 0] + set name [lindex $inst 1] + + if {[string equal $instance_id $process_instance_id]} { + set sel "selected=\"selected\"" + } else { + set sel "" + } + + append html "<option $sel value=\"$instance_id\">$name (\#$instance_id)</option>" + } + + return $html +} + + +ad_proc -public pm::process::url { + {-process_instance_id:required} + {-project_item_id:required} + {-fully_qualified_p "t"} +} { + Returns the URL for a process instance + + @author Jade Rubick (jader@bread.com) + @creation-date 2004-10-15 + + @param process_instance_id + + @return + + @error +} { + + return [pm::util::url -fully_qualified_p $fully_qualified_p][export_vars -base one {project_item_id {instance_id $process_instance_id}}] + +} + + +ad_proc -public pm::process::email_alert { + {-process_instance_id:required} + {-project_item_id:required} +} { + Sends out an email notification when a process is instantiated. + Gives the status of all tasks created. + + @author Jade Rubick (jader@bread.com) + @creation-date 2004-10-05 + + @param process_instance_id + + @return + + @error +} { + + set task_term \ + [parameter::get -parameter "Taskname" -default "Task"] + set task_term_lower \ + [parameter::get -parameter "taskname" -default "task"] + set use_uncertain_completion_times_p \ + [parameter::get -parameter "UseUncertainCompletionTimesP" -default "0"] + + set user_id [ad_conn user_id] + + db_1row get_from_address_and_more { } + db_1row get_project_info { } + + set process_name [pm::process::name \ + -process_instance_id $process_instance_id] + + set project_url [pm::project::url -project_item_id $project_item_id] + set process_url [pm::process::url \ + -process_instance_id $process_instance_id \ + -project_item_id $project_item_id] + + set subject_out "New: $process_name" + set intro_text "$mod_username assigned you to a process." + + set task_info [db_list_of_lists get_task_info { }] + + set task_list [list] + foreach ti $task_info { + lappend task_list [lindex $ti 0] + } + + set assignees [db_list_of_lists get_assignees { }] + + # make a list of those who are assigned in some capacity + set to_addresses [list] + + foreach ass $assignees { + set to_address [lindex $ass 0] + + if {[lsearch $to_addresses $to_address] < 0} { + lappend to_addresses $to_address + } + } + + # make the notification for each assignee + foreach to_address $to_addresses { + + set notification_text "${intro_text} +<h3>Process overview</h3> +<table border=\"0\" bgcolor=\"#ddddff\"> + <tr> + <td>Project:</td> + <td><a href=\"${project_url}\">$project_name</a> (\#$project_item_id)</td> + </tr> + <tr> + <td>Overview of process:</td> + <td><a href=\"${process_url}\">$process_name</a></td> + </tr> +</table> + +<h3>$task_term and current status</h3> +<table border=\"0\" bgcolor=\"\#ddddff\" cellpadding=\"1\" cellspacing=\"1\"> + <th>$task_term name</th> + <th>Slack time</th> + <th>Lead</th> + <th>Latest finish</th>" + + foreach ti $task_info { + set task_item_id [lindex $ti 0] + set subject [lindex $ti 1] + set today_j [lindex $ti 2] + set earliest_start_j [lindex $ti 3] + set latest_start_j [lindex $ti 4] + set latest_finish [lindex $ti 5] + set status_type [lindex $ti 6] + + set slack_time [pm::task::slack_time \ + -earliest_start_j $earliest_start_j \ + -today_j $today_j \ + -latest_start_j $latest_start_j] + + set pretty_latest_finish [lc_time_fmt $latest_finish "%x"] + + set lead_html "" + + # we highlight rows when the user is assigned to this task + set assignee_involved_p f + + foreach ass $assignees { + set to_addr [lindex $ass 0] + set role [lindex $ass 1] + set is_lead_p [lindex $ass 2] + set user_name [lindex $ass 3] + set my_task_id [lindex $ass 4] + + # ignore anything that isn't for this task + if {[string equal $my_task_id $task_item_id]} { + + if {[string is true $is_lead_p]} { + append lead_html "$user_name<br />" + } + + if {[string equal $to_addr $to_address]} { + set assignee_involved_p t + } + } + } + + if {[string equal $status_type "c"]} { + append notification_text "<tr bgcolor=\"dddddd\">" + } elseif {[string is true $assignee_involved_p]} { + append notification_text "<tr bgcolor=\"ffdddd\">" + } else { + append notification_text "<tr>" + } + + append notification_text " + <td>$subject</td> + <td>$slack_time</td> + <td>$lead_html</td> + <td>$pretty_latest_finish</td> +</tr>" + } + + # build table of current status + append notification_text "</table> <p>If the row is in red, you are involved in this task. If it is in grey, then it has already been completed.</p>" + + pm::util::email \ + -to_addr $to_address \ + -from_addr $from_address \ + -subject $subject_out \ + -body $notification_text \ + -mime_type "text/html" + } + +} + + +ad_proc -public pm::process::name { + {-process_instance_id:required} +} { + Returns the name when given a process_instance_id + + @author Jade Rubick (jader@bread.com) + @creation-date 2004-10-20 + + @param process_instance_id + + @return + + @error +} { + + db_1row get_name { } + + return $process_name +} + + +ad_proc -public pm::process::process_name { + {-process_id:required} +} { + Returns the name when given a process_id + + @author Jade Rubick (jader@bread.com) + @creation-date 2004-10-20 + + @param process_id + + @return + + @error +} { + + db_1row get_name { } + + return $one_line +} + + Index: openacs-4/contrib/packages/project-manager/tcl/project-manager-procs-postgresql.xql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/project-manager/tcl/Attic/project-manager-procs-postgresql.xql,v diff -u -r1.2.2.1 -r1.2.2.2 --- openacs-4/contrib/packages/project-manager/tcl/project-manager-procs-postgresql.xql 2 Jul 2004 23:13:48 -0000 1.2.2.1 +++ openacs-4/contrib/packages/project-manager/tcl/project-manager-procs-postgresql.xql 26 Oct 2004 01:22:27 -0000 1.2.2.2 @@ -35,5 +35,24 @@ t.name </querytext> </fullquery> + + <fullquery name="pm::util::subsite_assignees_list_of_lists_not_cached.get_assignees"> + <querytext> + SELECT + p.first_names || ' ' || p.last_name as name, + p.person_id + FROM + persons p, + acs_rels r, + membership_rels mr + WHERE + r.object_id_one = :user_group_id and + mr.rel_id = r.rel_id and + p.person_id = r.object_id_two and + member_state = 'approved' + ORDER BY + p.first_names, p.last_name + </querytext> + </fullquery> </queryset> Index: openacs-4/contrib/packages/project-manager/tcl/project-manager-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/project-manager/tcl/Attic/project-manager-procs.tcl,v diff -u -r1.2.2.9 -r1.2.2.10 --- openacs-4/contrib/packages/project-manager/tcl/project-manager-procs.tcl 23 Sep 2004 22:00:06 -0000 1.2.2.9 +++ openacs-4/contrib/packages/project-manager/tcl/project-manager-procs.tcl 26 Oct 2004 01:22:27 -0000 1.2.2.10 @@ -601,6 +601,7 @@ ad_proc -public pm::util::url { + {-fully_qualified_p "t"} } { Returns the URL of where the project manager is located, fully qualified @@ -614,7 +615,49 @@ } { set package_id [pm::util::package_id] - set package_url "[ad_url][site_node::get_url_from_object_id -object_id $package_id]" + if {[string is true $fully_qualified_p]} { + set return_val [ad_url] + } else { + set return_val "" + } + append return_val [site_node::get_url_from_object_id -object_id $package_id] - return $package_url + return $return_val } + + +ad_proc -public pm::util::subsite_assignees_list_of_lists { +} { + Returns a list of lists of possible assignees + + @author Jade Rubick (jader@bread.com) + @creation-date 2004-10-13 + + @return + + @error +} { + return [util_memoize [list pm::util::subsite_assignees_list_of_lists_not_cached]] +} + + +ad_proc -public pm::util::subsite_assignees_list_of_lists_not_cached { +} { + Returns a list of lists of possible assignees + + @author Jade Rubick (jader@bread.com) + @creation-date 2004-10-13 + + @return + + @error +} { + + set subsite_id [ad_conn subsite_id] + set user_group_id [application_group::group_id_from_package_id \ + -package_id $subsite_id] + + set assignees [db_list_of_lists get_assignees { }] + + return $assignees +} Index: openacs-4/contrib/packages/project-manager/tcl/project-procs-postgresql.xql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/project-manager/tcl/Attic/project-procs-postgresql.xql,v diff -u -r1.4.2.3 -r1.4.2.4 --- openacs-4/contrib/packages/project-manager/tcl/project-procs-postgresql.xql 16 Jul 2004 20:59:40 -0000 1.4.2.3 +++ openacs-4/contrib/packages/project-manager/tcl/project-procs-postgresql.xql 26 Oct 2004 01:22:27 -0000 1.4.2.4 @@ -45,6 +45,18 @@ </querytext> </fullquery> + <fullquery name="pm::project::log_hours.add_task_logger_map"> + <querytext> + INSERT INTO + pm_task_logger_proj_map + (task_item_id, + logger_entry) + VALUES + (:task_item_id, + :entry_id) + </querytext> + </fullquery> + <fullquery name="pm::project::new.new_project_item"> <querytext> select pm_project__new_project_item ( @@ -200,4 +212,24 @@ </querytext> </fullquery> + <fullquery name="pm::project::get_list_of_open.get_vals"> + <querytext> + SELECT + case when o.name is null then p.title else p.title || ' (' || o.name || ')' end, + p.item_id + FROM pm_projectsx p + LEFT JOIN + organizations o + ON p.customer_id = o.organization_id, + cr_items i, + pm_project_status s + WHERE + p.project_id = i.live_revision and + s.status_id = p.status_id and + s.status_type = 'o' + ORDER BY + lower(p.title), lower(o.name) + </querytext> + </fullquery> + </queryset> Index: openacs-4/contrib/packages/project-manager/tcl/project-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/project-manager/tcl/Attic/project-procs.tcl,v diff -u -r1.6.2.8 -r1.6.2.9 --- openacs-4/contrib/packages/project-manager/tcl/project-procs.tcl 8 Oct 2004 21:25:04 -0000 1.6.2.8 +++ openacs-4/contrib/packages/project-manager/tcl/project-procs.tcl 26 Oct 2004 01:22:27 -0000 1.6.2.9 @@ -81,7 +81,7 @@ -logger_project_id:required -variable_id:required -value:required - -timestamp_ansi:required + {-timestamp_ansi ""} {-description ""} {-task_item_id ""} {-project_item_id ""} @@ -145,6 +145,10 @@ set creation_user [ad_conn user_id] } + if {[empty_string_p $timestamp_ansi]} { + set timestamp_ansi [clock format [clock seconds] -format "%Y-%m-%d"] + } + # add in the new entry logger::entry::new -entry_id $entry_id \ -project_id $logger_project_id \ @@ -157,21 +161,13 @@ # if we have a pm_task_id, then we need to note that this # entry is logged to a particular task. if {[exists_and_not_null task_item_id]} { - db_dml add_logger_map " - INSERT INTO - pm_task_logger_proj_map - (task_item_id, - logger_entry) - VALUES - (:task_item_id, - :entry_id) - " + db_dml add_task_logger_map { } set returnval [pm::task::update_hours \ -task_item_id $task_item_id \ -update_tasks_p $update_status_p] - if {[string equal $update_status_p "t"]} { + if {[string is true $update_status_p]} { pm::project::compute_status $project_item_id } } @@ -1595,7 +1591,6 @@ } - ad_proc -public pm::project::get_list_of_open { } { Returns a list of lists, of all open project ids and their names @@ -1608,28 +1603,53 @@ @error } { - set return_val [db_list_of_lists get_vals " - SELECT - case when o.name is null then p.title else o.name || ' - ' || p.title end, - p.item_id - FROM pm_projectsx p - LEFT JOIN - organizations o - ON p.customer_id = o.organization_id, - cr_items i, - pm_project_status s - WHERE - p.project_id = i.live_revision and - s.status_id = p.status_id and - s.status_type = 'o' - ORDER BY - lower(o.name), lower(p.title) - "] + set return_val [db_list_of_lists get_vals { }] return $return_val } +ad_proc -public pm::project::select_list_of_open { + {-selected ""} +} { + Returns a select list of all open project ids and their names + + @author Jade Rubick (jader@bread.com) + @creation-date 2004-10-13 + + @return html for select list of open projects + + @error +} { + # is the selected project closed? + set open_p [pm::project::open_p -project_item_id $selected] + + if {[string is false $open_p]} { + set name [pm::project::name -project_item_id $selected] + set html "<option value=\"$selected\">$name</option>" + } else { + set html "" + } + + set list_of_lists [pm::project::get_list_of_open] + + foreach lol $list_of_lists { + set name [lindex $lol 0] + set id [lindex $lol 1] + + if {[string equal $id $selected]} { + set sel "selected=\"selected\"" + } else { + set sel "" + } + + append html "<option $sel value=\"$id\">$name</option>\n" + } + + return $html +} + + ad_proc -public pm::project::close { {-project_item_id:required} } { @@ -1944,3 +1964,5 @@ return "[ad_url][ad_conn package_url]one?project_item_id=$project_item_id" } + + Index: openacs-4/contrib/packages/project-manager/tcl/role-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/project-manager/tcl/Attic/role-procs.tcl,v diff -u -r1.2.2.3 -r1.2.2.4 --- openacs-4/contrib/packages/project-manager/tcl/role-procs.tcl 20 Sep 2004 23:13:49 -0000 1.2.2.3 +++ openacs-4/contrib/packages/project-manager/tcl/role-procs.tcl 26 Oct 2004 01:22:27 -0000 1.2.2.4 @@ -40,11 +40,11 @@ @error } { - return [util_memoize [list pm::role::select_list_filter_helper] 300] + return [util_memoize [list pm::role::select_list_filter_not_cached] 300] } -ad_proc -private pm::role::select_list_filter_helper {} { +ad_proc -private pm::role::select_list_filter_not_cached {} { Returns a select list. Used so pm::role::select_list can be cached. @author Jade Rubick (jader@bread.com) Index: openacs-4/contrib/packages/project-manager/tcl/task-procs-postgresql.xql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/project-manager/tcl/Attic/task-procs-postgresql.xql,v diff -u -r1.4.2.4 -r1.4.2.5 --- openacs-4/contrib/packages/project-manager/tcl/task-procs-postgresql.xql 23 Sep 2004 21:46:41 -0000 1.4.2.4 +++ openacs-4/contrib/packages/project-manager/tcl/task-procs-postgresql.xql 26 Oct 2004 01:22:27 -0000 1.4.2.5 @@ -3,6 +3,20 @@ <queryset> <rdbms><type>postgresql</type><version>7.3</version></rdbms> + <fullquery name="pm::task::name.get_name"> + <querytext> + SELECT + r.title + FROM + cr_items i, + cr_revisions r + WHERE + i.item_id = r.item_id and + i.item_id = :task_item_id and + i.live_revision = r.revision_id + </querytext> + </fullquery> + <fullquery name="pm::task::get_item_id.get_item_id"> <querytext> SELECT @@ -66,9 +80,59 @@ </querytext> </fullquery> - <fullquery name="pm::task::options_list.get_dependency_tasks"> + <fullquery name="pm::task::dependency_delete_all.delete_deps"> <querytext> + DELETE FROM + pm_task_dependency + WHERE + task_id = :task_item_id + </querytext> + </fullquery> + + <fullquery name="pm::task::dependency_add.get_tasks"> + <querytext> SELECT + task.item_id as t_item_id + FROM + cr_items task, + cr_items project + WHERE + task.parent_id = project.item_id and + project.item_id = :project_item_id + </querytext> + </fullquery> + + <fullquery name="pm::task::dependency_add.get_dependencies"> + <querytext> + SElECT + d.task_id as dep_task, + d.parent_task_id as dep_task_parent + FROM + pm_task_dependency d + WHERE + d.task_id in ([join $project_tasks ", "]) + </querytext> + </fullquery> + + <fullquery name="pm::task::dependency_add.insert_dep"> + <querytext> + INSERT INTO + pm_task_dependency + (dependency_id, + task_id, + parent_task_id, + dependency_type) + values + (:dependency_id, + :task_item_id, + :parent_id, + 'finish_before_start') + </querytext> + </fullquery> + + <fullquery name="pm::task::options_list_html.get_dependency_tasks"> + <querytext> + SELECT r.item_id, r.title as task_title FROM @@ -110,6 +174,23 @@ </querytext> </fullquery> + <fullquery name="pm::task::edit.update_logger_entries"> + <querytext> + UPDATE + logger_entries + SET + project_id = :logger_project + WHERE + entry_id in + (select + logger_entry + from + pm_task_logger_proj_map + where + task_item_id = :task_item_id) + </querytext> + </fullquery> + <fullquery name="pm::task::new.new_task_item"> <querytext> select pm_task__new_task_item ( @@ -123,31 +204,157 @@ :estimated_hours_work_min, :estimated_hours_work_max, :status_id, + :process_instance_id, coalesce (:creation_date,current_timestamp), :creation_user, :creation_ip, :package_id) </querytext> </fullquery> - <fullquery name="pm::task::process_task_info.get_process_tasks"> + + <fullquery name="pm::task::email_alert.get_from_address_and_more"> <querytext> - SELECT - t.process_task_id as process_tid, - t.one_line, - t.description, - t.estimated_hours_work, - t.estimated_hours_work_min, - t.estimated_hours_work_max, - d.dependency_id, - d.parent_task_id as process_parent_task - FROM - pm_process_task t LEFT JOIN pm_process_task_dependency d ON t.process_task_id = d.process_task_id + SELECT + p.email as from_address, + p2.first_names || ' ' || p2.last_name as mod_username + FROM + parties p, + persons p2 WHERE - t.process_id = :process_id - ORDER BY - t.ordering, - t.process_task_id + p.party_id = :user_id and + p.party_id = p2.person_id </querytext> </fullquery> + + <fullquery name="pm::task::email_alert.get_task_info"> + <querytext> + SELECT + t.title as subject, + t.description, + t.mime_type as description_mime_type, + to_char(t.earliest_start,'MM-DD-YYYY') as earliest_start, + to_char(t.earliest_finish,'MM-DD-YYYY') as earliest_finish, + to_char(t.latest_start,'MM-DD-YYYY') as latest_start, + to_char(t.latest_finish,'MM-DD-YYYY') as latest_finish, + t.estimated_hours_work as work, + t.estimated_hours_work_min as work_min, + t.estimated_hours_work_max as work_max, + t.percent_complete, + p.title as project_name, + t.parent_id as project_item_id, + a.process_instance + FROM + pm_tasks_revisionsx t, + pm_tasks_active a, + cr_items i, + cr_items project, + pm_projectsx p + WHERE + t.item_id = :task_item_id and + t.item_id = a.task_id and + t.revision_id = i.live_revision and + t.item_id = i.item_id and + t.parent_id = project.item_id and + project.item_id = p.item_id and + project.live_revision = p.revision_id + </querytext> + </fullquery> + + <fullquery name="pm::task::email_alert.get_assignees"> + <querytext> + SELECT + p.email as to_address, + r.one_line as role, + r.is_lead_p + FROM + pm_task_assignment a, + parties p, + pm_roles r + WHERE + task_id = :task_item_id and + a.party_id = p.party_id and + a.role_id = r.role_id + </querytext> + </fullquery> + + + <fullquery name="pm::task::get.get_tasks"> + <querytext> + SELECT + t.title as one_line, + t.description, + t.mime_type as description_mime_type, + t.estimated_hours_work as estimated_hours_work, + t.estimated_hours_work_min as estimated_hours_work_min, + t.estimated_hours_work_max as estimated_hours_work_max, + t.percent_complete, + to_char(t.end_date, 'DD') as end_date_day, + to_char(t.end_date, 'MM') as end_date_month, + to_char(t.end_date, 'YYYY') as end_date_year, + d.parent_task_id, + i.item_id as tid, + t.parent_id as project + FROM + pm_tasks_revisionsx t, + cr_items i + LEFT JOIN + pm_task_dependency d + ON i.item_id = d.task_id + WHERE + t.revision_id = i.live_revision and + t.item_id = i.item_id + $task_where_clause + </querytext> + </fullquery> + + + <fullquery name="pm::task::assignee_role_list.get_assignees_roles"> + <querytext> + SELECT + party_id, + role_id + FROM + pm_task_assignment + WHERE + task_id = :task_item_id + </querytext> + </fullquery> + + + <fullquery name="pm::task::open.update_status"> + <querytext> + UPDATE + pm_tasks + SET + status = :status_code + WHERE + task_id = :task_item_id + </querytext> + </fullquery> + + <fullquery name="pm::task::close.update_status"> + <querytext> + UPDATE + pm_tasks + SET + status = :status_code + WHERE + task_id = :task_item_id + </querytext> + </fullquery> + + <fullquery name="pm::task::get_assignee_names.get_assignees"> + <querytext> + SELECT + p.first_names || ' ' || p.last_name + FROM + pm_task_assignment a, + persons p + WHERE + task_id = :task_item_id and + a.party_id = p.person_id + </querytext> + </fullquery> + </queryset> Index: openacs-4/contrib/packages/project-manager/tcl/task-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/project-manager/tcl/Attic/task-procs.tcl,v diff -u -r1.4.2.24 -r1.4.2.25 --- openacs-4/contrib/packages/project-manager/tcl/task-procs.tcl 8 Oct 2004 21:25:04 -0000 1.4.2.24 +++ openacs-4/contrib/packages/project-manager/tcl/task-procs.tcl 26 Oct 2004 01:22:27 -0000 1.4.2.25 @@ -13,7 +13,24 @@ namespace eval pm::task {} +ad_proc -public pm::task::name { + {-task_item_id:required} +} { + Returns the name of the task + + @author Jade Rubick (jader@bread.com) + @creation-date 2004-10-25 + + @param task_item_id + @return + + @error -1 +} { + return [db_string get_name { } -default "-1"] +} + + ad_proc -public pm::task::options_list { {-edit_p "f"} -project_item_id @@ -81,7 +98,7 @@ SELECT r.item_id, r.title as task_title - FROM + FROM pm_tasks_revisionsx r, cr_items i, pm_tasks_active t @@ -147,6 +164,173 @@ } +ad_proc -public pm::task::options_list_html { + {-edit_p "f"} + -project_item_id + {-task_item_id ""} + {-dependency_task_id ""} + {-dependency_task_ids ""} + {-number "0"} + {-depends_on_new ""} + {-current_number "0"} +} { + Returns a list of options suiteable for HTML. + Contains a list of possible tasks that this task can + depend upon, or selected. These tasks are limited to just the + one project. + + <p /> + + There is one special case that we handle: if you are creating new + tasks (not editing), you can have them depend on each other. + So if you create two tasks at the same time, you may want task + 2 to depend on task 1. Instead of a task_item_id, we then + specify a value of this form: + + <blockquote> + numX + </blockquote> + + where X represents the number of the new task, ranging from 1 + to n. + + <p /> + + To be more efficient when creating multiple tasks at the same + time, we should cache the database calls. + + @author Jade Rubick (jader@bread.com) + @creation-date 2004-05-13 + + @param edit_p Is this for a task being edited? Or a new task? + + @param project_item_id The project we're finding tasks from + + @param task_item_id The task ID. This is used because we do not + want a task to depend on itself, so it is excluded from the list. + + @param dependency_task_ids For edited tasks, the current task_ids + that it depends on. Used because sometimes it can be closed, and + it wouldn't otherwise appear on the list. This is a list. + + @param dependency_task_id For edited tasks, the current task that + it depends on, used for setting the default option in HTML. + + @param number When the list is returned, it includes entries for + number new tasks, in the numX format described in these docs. + + @param depends_on_new When you're using a process, you want the + dependency to be on other new tasks. The format for this should + be num1 num2, etc.. So we make the default if this parameter is set. + + @param current_number The current number. Used for new tasks. It + prevents allowing dependencies on the task being created. + + @return + + @error +} { + + # get tasks this task can depend on + + if {[exists_and_not_null dependency_task_ids]} { + + set union_clause " + UNION + SELECT + r.item_id, + r.title as task_title + FROM + pm_tasks_revisionsx r, + cr_items i, + pm_tasks_active t + WHERE + r.parent_id = :project_item_id and + r.revision_id = i.live_revision and + i.item_id = t.task_id + and t.task_id in ([join $dependency_task_ids ","])" + } else { + set union_clause "" + } + + set keys [list] + + db_foreach get_dependency_tasks { } { + set options($task_title) $item_id + lappend keys $task_title + } + + set keys [lsort $keys] + + # --------------------------------------------------------------- + # Start setting up the output. + # These are for new tasks, the already created tasks are added to + # the list later. + # --------------------------------------------------------------- + + set dependency_options_full "<option value=\"\">--None--</option> " + + if {[string is false $edit_p]} { + + # now set up dependency options + + for {set j 1} {$j <= $number} {incr j} { + + if {[string equal $depends_on_new $j]} { + set selected "selected=\"selected\" " + } else { + set selected "" + } + + if {![string equal $current_number $j]} { + append dependency_options_full "<option ${selected}value=\"num$j\">New Task \#$j</option> " + } + } + } + + # ------------------------------------------------- + # Now add the tasks that are already in the project + # ------------------------------------------------- + + + if {[string is true $edit_p]} { + foreach key $keys { + + # for editing tasks, we skip ourselves (because depending on + # ourselves just sometimes isn't an option) + if {![string equal $task_item_id $options($key)]} { + + if {[string equal $options($key) $dependency_task_id]} { + set selected "selected=\"selected\" " + } else { + set selected "" + } + + # check for case when there is a quote in the name of + # a task. We have to filter this out, or we get an + # error. -- not sure what this comment is for -- JR + + + append dependency_options_full "<option ${selected}value=\"$options($key)\">$key</option> " + } + } + } else { + + foreach key $keys { + + # check for case when there is a quote in the name of a + # task. We have to filter this out, or we get an error. -- + # not sure what this comment is for -- JR + + append dependency_options_full "<option value=\"$options($key)\">$key</option> " + } + } + + + return $dependency_options_full +} + + ad_proc -public pm::task::dependency_delete_all { -task_item_id:required @@ -156,13 +340,14 @@ @author Jade Rubick (jader@bread.com) @creation-date 2004-02-23 - @param task_item_id The task we wish to remove the dependencies from + @param task_item_id The task we wish to remove the dependencies from. @return @error } { - db_dml delete_deps "delete from pm_task_dependency where task_id = :task_item_id" + db_dml delete_deps { } + return 1 } @@ -214,16 +399,7 @@ @error } { - set project_tasks [db_list get_tasks " - SELECT - task.item_id as t_item_id - FROM - cr_items task, - cr_items project - WHERE - task.parent_id = project.item_id and - project.item_id = :project_item_id - "] + set project_tasks [db_list get_tasks { }] # we do not allow tasks to depend on items outside of their # project. So if it's not in the list of tasks for that project, @@ -239,15 +415,7 @@ if {$loop_limit > 0} { set dep_list [list] - db_foreach get_dependencies " - SElECT - d.task_id as dep_task, - d.parent_task_id as dep_task_parent - FROM - pm_task_dependency d - WHERE - d.task_id in ([join $project_tasks ", "]) - " { + db_foreach get_dependencies { } { lappend dep_list d-$dep_task-$dep_task_parent } @@ -266,12 +434,11 @@ } - - if {[string equal $valid_p "TRUE"]} { + if {[string is true $valid_p]} { # after it passes set dependency_id [db_nextval pm_task_dependency_seq] - db_dml insert_dep "insert into pm_task_dependency (dependency_id, task_id, parent_task_id, dependency_type) values (:dependency_id, :task_item_id, :parent_id, 'finish_before_start')" + db_dml insert_dep { } } else { ns_log Notice "Task dependency for $task_item_id on $parent_id was not added due to looping or being outside of the current project" @@ -457,8 +624,6 @@ -estimated_hours_work:required -estimated_hours_work_min:required -estimated_hours_work_max:required - -actual_hours_worked:required - {-status_id} -update_user:required -update_ip:required -package_id:required @@ -491,10 +656,6 @@ @param estimated_hours_work_max - @param actual_hours_worked The number of hours worked to date - - @param status_id The code representing the status - @param update_user The user updating the task @param update_ip The IP address of the request @@ -505,56 +666,42 @@ @error } { - if {![exists_and_not_null status_id]} { - set status_id [pm::task::current_status \ - -task_item_id $task_item_id] - } - # simple sanity check for min and max estimated hours if {$estimated_hours_work_min > $estimated_hours_work_max} { set temp $estimated_hours_work_max set estimated_hours_work_max $estimated_hours_work_min set estimated_hours_work_min $temp } - set return_val [db_exec_plsql new_task_revision { *SQL }] + if {$percent_complete >= 100} { - # we have to update all logged hours to make sure the hours are - # updated whenever the project is changed. + set status_id [pm::task::default_status_closed] - set logger_project [pm::project::get_logger_project -project_item_id $project_item_id] + } elseif {$percent_complete < 100} { - db_dml update_logger_entries { - UPDATE - logger_entries - SET - project_id = :logger_project - WHERE - entry_id in - (select logger_entry from pm_task_logger_proj_map where task_item_id = :task_item_id) + set status_id [pm::task::default_status_open] } - # if the we've done 100% of the work, then we close the task - if {$percent_complete >= 100} { - pm::task::close \ - -task_item_id $task_item_id \ - -comment "$comment" \ - -comment_type $comment_type - } else { - # does not need comment because that's done later - pm::task::open \ - -task_item_id $task_item_id + set actual_hours_worked [pm::task::update_hours \ + -task_item_id $task_item_id] - } + set return_val [db_exec_plsql new_task_revision { *SQL }] + # we have to update all logged hours to make sure the hours are + # set to the correct project whenever the project is changed. + + set logger_project [pm::project::get_logger_project -project_item_id $project_item_id] + + db_dml update_logger_entries { } + return $return_val } ad_proc -public pm::task::new { -project_id:required - -title:required + {-title "Subject missing"} {-description ""} {-mime_type "text/plain"} {-end_date ""} @@ -564,10 +711,25 @@ {-estimated_hours_work_max "0"} {-creation_date ""} {-status_id ""} + {-process_instance_id ""} -creation_user:required -creation_ip:required -package_id:required } { + Creates a new task. + + @param process_instance_id If a process was used to create the + task, then it is linked in to that process instance, so we can + things like display only tasks that are a part of a process. + + @author Jade Rubick (jader@bread.com) + @creation-date who knows? + + @return new task_item_id + + @error + +} { if {![exists_and_not_null status_id]} { set status_id [pm::task::default_status_open] } @@ -578,9 +740,15 @@ set estimated_hours_work_min $temp } - set return_val [db_exec_plsql new_task_item { *SQL }] + set task_revision [db_exec_plsql new_task_item { *SQL }] + set task_item_id [pm::task::get_item_id \ + -task_id $task_revision] - return $return_val + if {$percent_complete >= 100} { + pm::task::close -task_item_id $task_item_id + } + + return $task_item_id } @@ -739,7 +907,7 @@ select sum(le.value) from logger_entries le where entry_id in (select logger_entry from pm_task_logger_proj_map where task_item_id = :task_item_id) and le.variable_id = '[logger::variable::get_default_variable_id]' " -default "0"] - if {[string equal $update_tasks_p "t"]} { + if {[string is true $update_tasks_p]} { db_dml update_current_task " UPDATE @@ -865,10 +1033,9 @@ ad_proc -public pm::task::open { - -task_item_id:required + {-task_item_id:required} } { - Opens a task, and sends notifications, unless it was already - open. If it was already open, does nothing. + Opens a task. @author Jade Rubick (jader@bread.com) @creation-date 2004-04-22 @@ -879,137 +1046,15 @@ @error } { - # find out what the status of the task was, and while we're at it, - # get other interesting information about the task, in case we - # want to close it. Then we can put this info in the email. - - db_1row get_status " - SELECT - t.status, - s.status_type, - s.description as status_description, - r.title as task_title, - r.estimated_hours_work, - r.estimated_hours_work_min, - r.estimated_hours_work_max, - to_char(r.earliest_start, 'YYYY-MM-DD HH24:MI:SS') as earliest_start_ansi, - to_char(r.earliest_finish, 'YYYY-MM-DD HH24:MI:SS') as earliest_finish_ansi, - to_char(r.latest_start, 'YYYY-MM-DD HH24:MI:SS') as latest_start_ansi, - to_char(r.latest_finish, 'YYYY-MM-DD HH24:MI:SS') as latest_finish_ansi, - r.description as task_description, - r.mime_type, - project_revision.title as project_name - FROM - pm_tasks t, - cr_items task_item, - pm_task_status s, - pm_tasks_revisionsx r, - cr_items project_item, - cr_revisions project_revision - WHERE - r.parent_id = project_item.item_id and - t.task_id = task_item.item_id and - task_item.live_revision = r.revision_id and - project_item.live_revision = project_revision.revision_id and - r.item_id = t.task_id and - t.status = s.status_id and - t.task_id = :task_item_id" - - if {[string equal $status_type "o"]} { - - # this is already open - return - - } - - # set the new status - set status_code [pm::task::default_status_open] - db_dml update_status " - UPDATE - pm_tasks - SET - status = :status_code - WHERE - task_id = :task_item_id" - - # send out an email notification - - set earliest_start [lc_time_fmt $earliest_start_ansi "%x"] - set earliest_finish [lc_time_fmt $earliest_finish_ansi "%x"] - set latest_start [lc_time_fmt $latest_start_ansi "%x"] - set latest_finish [lc_time_fmt $latest_finish_ansi "%x"] - - set assignees [pm::task::assignee_email_list -task_item_id $task_item_id] - - if {[llength $assignees] > 0} { - - set to_address $assignees - - set user_id [ad_conn user_id] - - set from_address [db_string get_from_email "select email from parties where party_id = :user_id" -default "nobody@nowhere.com"] - - set task_url [pm::task::get_url $task_item_id] - - set subject "Task Reopened (was $status_description): $task_title" - - set notification_text "Task reopened, was $status_description\n\n" - - set task_description [ad_html_text_convert -from $mime_type -to "text/html" -- $task_description] - - append notification_text " -<h3>Task overview</h3> -<table border=\"0\" bgcolor=\"#ddddff\"> - <tr> - <td>Subject:</td> - <td>$task_title (<a href=\"$task_url\">\#$task_item_id</a>)</td> - </tr> - <tr> - <td>Project:</td> - <td>$project_name</td> - </tr> -</table> - -<h3>Description</h3> -<table border=\"0\" bgcolor=\"\#ddddff\"> - <tr> - <td>$task_description</td> - </tr> -</table> - -<h3>Dates:</h3> -<table border=\"0\" bgcolor=\"#ddddff\"> - <tr> - <td>Latest start:</td> - <td>$latest_start</td> - </tr> - <tr> - <td>Latest finish</td> - <td><i>$latest_finish</i></td> - </tr> -</table> -" - - pm::util::email \ - -to_addr $to_address \ - -from_addr $from_address \ - -subject $subject \ - -body $notification_text \ - -mime_type "text/html" - } - - return + db_dml update_status { } } ad_proc -public pm::task::close { - -task_item_id:required - {-comment ""} - {-comment_type "text/plain"} + {-task_item_id:required} } { - Closes a task, and sends notifications, unless it was already - closed. If it was already closed, does nothing. + Closes a task @author Jade Rubick (jader@bread.com) @creation-date 2004-04-22 @@ -1020,180 +1065,10 @@ @error } { - # find out what the status of the task was - - db_1row get_status " - SELECT - t.status, - s.status_type, - s.description as status_description, - r.title as task_title, - r.estimated_hours_work, - r.estimated_hours_work_min, - r.estimated_hours_work_max, - to_char(r.earliest_start, 'YYYY-MM-DD HH24:MI:SS') as earliest_start_ansi, - to_char(r.earliest_finish, 'YYYY-MM-DD HH24:MI:SS') as earliest_finish_ansi, - to_char(r.latest_start, 'YYYY-MM-DD HH24:MI:SS') as latest_start_ansi, - to_char(r.latest_finish, 'YYYY-MM-DD HH24:MI:SS') as latest_finish_ansi, - r.description as task_description, - r.mime_type, - project_revision.title as project_name - FROM - pm_tasks t, - cr_items task_item, - pm_task_status s, - pm_tasks_revisionsx r, - cr_items project_item, - cr_revisions project_revision - WHERE - r.parent_id = project_item.item_id and - t.task_id = task_item.item_id and - task_item.live_revision = r.revision_id and - project_item.live_revision = project_revision.revision_id and - r.item_id = t.task_id and - t.status = s.status_id and - t.task_id = :task_item_id" - - if {[string equal $status_type "c"]} { - - # this is already closed - return - - } - - # set the new status - set status_code [pm::task::default_status_closed] - db_dml update_status " - UPDATE - pm_tasks - SET - status = :status_code - WHERE - task_id = :task_item_id" + db_dml update_status { } - # send out an email notification - - set earliest_start [lc_time_fmt $earliest_start_ansi "%x"] - set earliest_finish [lc_time_fmt $earliest_finish_ansi "%x"] - set latest_start [lc_time_fmt $latest_start_ansi "%x"] - set latest_finish [lc_time_fmt $latest_finish_ansi "%x"] - - - set assignees [pm::task::assignee_email_list -task_item_id $task_item_id] - - if {[llength $assignees] > 0} { - - set to_address $assignees - - set user_id [ad_conn user_id] - - set from_address [db_string get_from_email "select email from parties where party_id = :user_id" -default "nobody@nowhere.com"] - - set last_time_stamp "" - set work_log "<h3>Work done on this task</h3><table border=\"0\" bgcolor=\"#ddddff\">" - - db_foreach get_logged_time " - SELECT - to_char(le.time_stamp, 'fmDyfm fmMMfm-fmDDfm-YYYY') as time_stamp_pretty, - le.value, - le.description, - r.title as task_name, - submitter.first_names || ' ' || submitter.last_name as user_name - FROM - logger_entries le, - cr_items i, - cr_revisions r, - pm_task_logger_proj_map m, - logger_projects lp, - acs_objects ao, - acs_users_all submitter - WHERE - r.item_id = m.task_item_id and - i.live_revision = r.revision_id and - r.item_id = :task_item_id and - le.project_id = lp.project_id and - ao.object_id = le.entry_id and - le.entry_id = m.logger_entry and - ao.creation_user = submitter.user_id - ORDER BY - le.time_stamp desc" { - append work_log "<tr>" - - if {![string equal $time_stamp_pretty $last_time_stamp]} { - append work_log "<td>$time_stamp_pretty</td><td>" - } else { - append work_log "<td> </td><td>" - } - append work_log "$user_name</td><td>$description</td><td>$value hrs</td>\n" - - set last_time_stamp $time_stamp_pretty - - append work_log "</tr>" - } - - append work_log "</table>" - - set task_url [pm::task::get_url $task_item_id] - - set subject "Task Closed (was $status_description) $task_title" - - set notification_text "Task closed, was $status_description\n\n" - - set task_description [ad_html_text_convert -from $mime_type -to "text/html" -- $task_description] - - set comment [ad_html_text_convert -from $comment_type -to "text/html" -- $comment] - - append notification_text " -<h3>Comment:</h3> -$comment - -<hr /> -<table border=\"0\" bgcolor=\"#ddddff\"> - <tr> - <td>Subject:</td> - <td><a href=\"$task_url\">$task_title</a> (\#$task_item_id)</td> - </tr> - <tr> - <td>Project:</td> - <td>$project_name</td> - </tr> -</table> -" - append notification_text "$work_log" - - append notification_text " -<h3>Description</h3> -<table border=\"0\" bgcolor=\"\#ddddff\"> - <tr> - <td>$task_description</td> - </tr> -</table> - -<h3>Dates:</h3> -<table border=\"0\" bgcolor=\"#ddddff\"> - <tr> - <td>Latest start:</td> - <td>$latest_start</td> - </tr> - <tr> - <td>Latest finish</td> - <td><i>$latest_finish</i></td> - </tr> -</table> -" - - - pm::util::email \ - -to_addr $to_address \ - -from_addr $from_address \ - -subject $subject \ - -body $notification_text \ - -mime_type "text/html" - } - - return } @@ -1452,26 +1327,9 @@ ad_proc -public pm::task::email_alert { -task_item_id:required - {-user_id ""} - {-assignee_id ""} - {-assignee_role_name ""} {-edit_p "t"} {-comment ""} {-comment_mime_type "text/plain"} - {-description ""} - {-description_mime_type "text/plain"} - {-old_description ""} - {-old_description_mime_type "text/plain"} - {-subject ""} - {-work ""} - {-work_min ""} - {-work_max ""} - {-project_name ""} - {-earliest_start ""} - {-earliest_finish ""} - {-latest_start ""} - {-latest_finish ""} - {-url ""} } { Sends out an email notification when changes have been made to a task @@ -1486,168 +1344,108 @@ @param task_item_id - @param user_id The user making the change - - @param assignee_id The party_id of the user assigned to the task. - - @param assignee_role_name The role name for what the party is - assigned to do - @param edit_p Is this an edited task, or a new one? t for edited, f otherwise. - @param description - - @param old_description - - @param subject The one line description of the task - - @param work Estimated hours work - - @param work_min Estimated minimimum hours work - - @param work_max Estimated maximum hours work - - @param project_name - - @param earliest_start - - @param earliest_finish - - @param latest_start - - @param latest_finish - - @param url Optionally, a URL that the user is directed to - @return @error } { - set task_term [parameter::get -parameter "Taskname" -default "Task"] - set task_term_lower [parameter::get -parameter "taskname" -default "task"] - set use_uncertain_completion_times_p [parameter::get -parameter "UseUncertainCompletionTimesP" -default "0"] + set task_term \ + [parameter::get -parameter "Taskname" -default "Task"] + set task_term_lower \ + [parameter::get -parameter "taskname" -default "task"] + set use_uncertain_completion_times_p \ + [parameter::get -parameter "UseUncertainCompletionTimesP" -default "0"] - # from address + set user_id [ad_conn user_id] - if {![exists_and_not_null $user_id]} { - set user_id [ad_conn user_id] - } + db_1row get_from_address_and_more { } - db_1row get_from_address_and_more { - SELECT - p.email as from_address, - p2.first_names || ' ' || p2.last_name as mod_username - FROM - parties p, - persons p2 - WHERE - p.party_id = :user_id and - p.party_id = p2.person_id - } + db_1row get_task_info { } - # to address + if {[string is true $edit_p]} { - if {![exists_and_not_null assignee_id]} { + # ---- + # EDIT + # ---- - # bug: we should get the list of assignees here. - ns_log Error "the proc pm::task::email_alert is not complete: assignee" - - } + set subject_out "Edited $task_term \#$task_item_id: $subject" + set intro_text "$mod_username edited this $task_term_lower" - set to_address [db_string get_email "select email from parties where party_id = :assignee_id"] + } else { - # if they left out any of the task info, then we get it from the database - if { \ - ![exists_and_not_null subject] || \ - ![exists_and_not_null work] || \ - ![exists_and_not_null work_min] || \ - ![exists_and_not_null work_max] || \ - ![exists_and_not_null project_name] || \ - ![exists_and_not_null earliest_start] || \ - ![exists_and_not_null earliest_finish] || \ - ![exists_and_not_null latest_start] || \ - ![exists_and_not_null latest_finish] \ - } { - - db_1row get_task_info { - SELECT - t.title as subject, - to_char(t.earliest_start,'MM-DD-YYYY') as earliest_start, - to_char(t.earliest_finish,'MM-DD-YYYY') as earliest_finish, - to_char(t.latest_start,'MM-DD-YYYY') as latest_start, - to_char(t.latest_finish,'MM-DD-YYYY') as latest_finish, - t.estimated_hours_work as work, - t.estimated_hours_work_min as work_min, - t.estimated_hours_work_max as work_max, - t.percent_complete, - p.title as project_name - FROM - pm_tasks_revisionsx t, - cr_items i, - cr_items project, - pm_projectsx p - WHERE - t.item_id = :task_item_id and - t.revision_id = i.live_revision and - t.item_id = i.item_id and - t.parent_id = project.item_id and - project.item_id = p.item_id and - project.live_revision = p.revision_id - } + # --- + # NEW + # --- - } - - - if {[string is true $edit_p]} { - set subject_out "Edited $task_term \#$task_item_id: $subject" - set intro_text "$mod_username edited this $task_term_lower" - } else { set subject_out "New $task_term \#$task_item_id: $subject" set intro_text "$mod_username assigned you to a new $task_term_lower" + } + if {[empty_string_p $comment]} { set comment_text "" } else { set comment_text "<h3>Comment:</h3>$comment<p />" } - - if {[exists_and_not_null url]} { - set task_url $url - } else { - set task_url "unavailable" - } + set url [pm::task::get_url $task_item_id] + set description [ad_html_text_convert -from $description_mime_type -to "text/html" -- $description] - set old_description [ad_html_text_convert -from $old_description_mime_type -to "text/html" -- $old_description] - if {![string equal $description $old_description] && [string is true $edit_p]} { - set description_out "$description <h3>Old description:</h3>$old_description" + set description_out $description + set assignees [db_list_of_lists get_assignees { }] + + if {[exists_and_not_null $process_instance]} { + + set process_url [pm::process::url \ + -process_instance_id $process_instance \ + -project_item_id $project_item_id] + + set process_description [pm::process::name \ + -process_instance_id $process_instance] + + set process_html " +<h3>Process</h3> +<table border=\"0\" bgcolor=\"\#ddddff\"> + <tr> + <td><a href=\"$process_url\">$process_description</a></td> + </tr> +</table> +" } else { - set description_out $description + set process_html "" } - set notification_text "${intro_text}${comment_text} + foreach ass $assignees { + + set to_address [lindex $ass 0] + set role [lindex $ass 1] + set is_lead_p [lindex $ass 2] + + set notification_text "${intro_text}${comment_text} <h3>Task overview</h3> <table border=\"0\" bgcolor=\"#ddddff\"> <tr> <td>Subject:</td> - <td><a href=\"${task_url}\">$subject</a> (\#$task_item_id)</td> + <td><a href=\"${url}\">$subject</a> (\#$task_item_id)</td> </tr> <tr> <td>Project:</td> <td>$project_name</td> </tr> <tr> <td>Your role:</td> - <td>$assignee_role_name</td> + <td>$role</td> </tr> </table> +$process_html <h3>Description</h3> <table border=\"0\" bgcolor=\"\#ddddff\"> <tr> @@ -1667,13 +1465,13 @@ </tr> </table>" - pm::util::email \ - -to_addr $to_address \ - -from_addr $from_address \ - -subject $subject_out \ - -body $notification_text \ - -mime_type "text/html" - + pm::util::email \ + -to_addr $to_address \ + -from_addr $from_address \ + -subject $subject_out \ + -body $notification_text \ + -mime_type "text/html" + } } @@ -1894,38 +1692,121 @@ } -ad_proc -public pm::task::process_task_info { - {-process_id:required} - {-one_line_array:required} - {-description_array:required} - {-estimated_hours_work_array:required} - {-estimated_hours_work_min_array:required} - {-estimated_hours_work_max_array:required} - {-dependency_array:required} - {-tasks_list:required} + +ad_proc -public pm::task::assignee_html { + {-number:required} + {-process_task_id ""} + {-task_item_id ""} } { - Sets a bunch of information in a set of arrays on all - process tasks for a given process + Assignee HTML for new tasks @author Jade Rubick (jader@bread.com) - @creation-date 2004-09-23 + @creation-date 2004-10-13 - @param process_id + @return + + @error +} { - @param one_line_array + # ------------------------------ + # cache these to speed it all up - @param description_array + set roles_list_of_lists [pm::role::select_list_filter] + set assignee_list_of_lists [pm::util::subsite_assignees_list_of_lists] - @param estimated_hours_work_array - @param estimated_hours_work_min_array + # Get assignments for when using processes + if {[exists_and_not_null process_task_id]} { - @param estimated_hours_work_max_array + # PROCESS - @param dependency_array + set task_assignee_list_of_lists \ + [pm::process::task_assignee_role_list \ + -process_task_id $process_task_id] - @param tasks_list + } elseif {[exists_and_not_null task_item_id]} { + # EDITING + + set task_assignee_list_of_lists \ + [pm::task::assignee_role_list \ + -task_item_id $task_item_id] + + } else { + + # NEW + + set task_assignee_list_of_lists [list] + } + + # Get assignments for when editing + + + set html "<table border=\"0\">" + + foreach role_list $roles_list_of_lists { + + set role_name [lindex $role_list 0] + set role [lindex $role_list 1] + + append html " + <td align=\"left\" valign=\"top\"><p /><B><I>$role_name</I></B><p />" + + foreach assignee_list $assignee_list_of_lists { + set name [lindex $assignee_list 0] + set person_id [lindex $assignee_list 1] + + if {[lsearch $task_assignee_list_of_lists [list $person_id $role]] >= 0} { + + append html " + <input name=\"assignee\" value=\"$number-$person_id-$role\" type=\"checkbox\" checked=\"checked\" /><span class=\"selected\">$name</span> + <br />" + + } else { + + append html " + <input name=\"assignee\" value=\"$number-$person_id-$role\" type=\"checkbox\" />$name + <br />" + } + + } + + append html "</td>" + + } + + append html "</table>" + + return $html +} + + +ad_proc -public pm::task::get { + {-tasks_item_id:required} + {-one_line_array:required} + {-description_array:required} + {-description_mime_type_array:required} + {-estimated_hours_work_array:required} + {-estimated_hours_work_min_array:required} + {-estimated_hours_work_max_array:required} + {-dependency_array:required} + {-percent_complete_array:required} + {-end_date_day_array:required} + {-end_date_month_array:required} + {-end_date_year_array:required} + {-project_item_id_array:required} +} { + Stuff information about tasks into several arrays + + @author Jade Rubick (jader@bread.com) + @creation-date 2004-10-14 + + @param tasks_item_id a list of tasks to retrieve and stuff in + arrays + + @param one_line_array stuff one_line info in + one_line_array(task_item_id) + @return @error @@ -1934,34 +1815,411 @@ # set variables in calling environment, using names passed in upvar 1 $one_line_array one_line_arr upvar 1 $description_array description_arr + upvar 1 $description_mime_type_array description_mime_type_arr upvar 1 $estimated_hours_work_array estimated_hours_work_arr upvar 1 $estimated_hours_work_min_array estimated_hours_work_min_arr upvar 1 $estimated_hours_work_max_array estimated_hours_work_max_arr upvar 1 $dependency_array dependency_arr - upvar 1 $tasks_list process_tasks_l + upvar 1 $percent_complete_array percent_complete_arr + upvar 1 $end_date_day_array end_date_day_arr + upvar 1 $end_date_month_array end_date_month_arr + upvar 1 $end_date_year_array end_date_year_arr + upvar 1 $project_item_id_array project_item_id_arr - db_foreach get_process_tasks { } { - set one_line_arr($process_tid) $one_line - set description_arr($process_tid) $description - set estimated_hours_work_arr($process_tid) $estimated_hours_work - set estimated_hours_work_min_arr($process_tid) $estimated_hours_work_min - set estimated_hours_work_max_arr($process_tid) $estimated_hours_work_max - set dependency_arr($process_tid) $process_parent_task + set task_where_clause " and i.item_id in ([join $tasks_item_id ", "])" + db_foreach get_tasks { } { + set one_line_arr($tid) $one_line + set description_arr($tid) $description + set description_mime_type_arr($tid) $description_mime_type + set estimated_hours_work_arr($tid) $estimated_hours_work + set estimated_hours_work_min_arr($tid) $estimated_hours_work_min + set estimated_hours_work_max_arr($tid) $estimated_hours_work_max + set dependency_arr($tid) $parent_task_id + set percent_complete_arr($tid) $percent_complete + set end_date_day_arr($tid) $end_date_day + set end_date_month_arr($tid) $end_date_month + set end_date_year_arr($tid) $end_date_year + set project_item_id_arr($tid) $project + # make sure that we don't have empty values for estimated # hours work - if {[empty_string_p $estimated_hours_work_arr($process_tid)]} { - set estimated_hours_work_arr($process_tid) 0 + if {[empty_string_p $estimated_hours_work_arr($tid)]} { + set estimated_hours_work_arr($tid) 0 } - if {[empty_string_p $estimated_hours_work_min_arr($process_tid)]} { - set estimated_hours_work_min_arr($process_tid) 0 + if {[empty_string_p $estimated_hours_work_min_arr($tid)]} { + set estimated_hours_work_min_arr($tid) 0 } - if {[empty_string_p $estimated_hours_work_max_arr($process_tid)]} { - set estimated_hours_work_max_arr($process_tid) 0 + if {[empty_string_p $estimated_hours_work_max_arr($tid)]} { + set estimated_hours_work_max_arr($tid) 0 } + } + +} - lappend process_tasks_l $process_tid + +ad_proc -public pm::task::date_html { + {-selected_month ""} + {-selected_day ""} + {-selected_year ""} +} { + Returns HTML for the date widget in the task-add-edit page + + @author Jade Rubick (jader@bread.com) + @creation-date 2004-10-15 + + @return + + @error +} { + for {set i 1} {$i <= 12} {incr i} { + + # numbers are in the form of 01 - 12 + if {$i < 10} { + set j "0$i" + } else { + set j $i + } + set selected_[set j] "" } + set selected_[set selected_month] "selected=\"selected\"" + + return " + <table border=\"0\" cellpadding=\"0\" cellspacing=\"2\"> + <tr> + <td nowrap=\"nowrap\"> + <select name=\"end_date_month\" > + <option value=\"\">--</option> + <option $selected_01 value=\"1\">January</option> + <option $selected_02 value=\"2\">February</option> + <option $selected_03 value=\"3\">March</option> + <option $selected_04 value=\"4\">April</option> + <option $selected_05 value=\"5\">May</option> + <option $selected_06 value=\"6\">June</option> + <option $selected_07 value=\"7\">July</option> + <option $selected_08 value=\"8\">August</option> + <option $selected_09 value=\"9\">September</option> + <option $selected_10 value=\"10\">October</option> + <option $selected_11 value=\"11\">November</option> + <option $selected_12 value=\"12\">December</option> + </select> </td> + <td nowrap=\"nowrap\"> + <input type=\"text\" name=\"end_date_day\" size=\"2\" value=\"$selected_day\" /> + </td> + <td nowrap=\"nowrap\"><input type=\"text\" name=\"end_date_year\" size=\"4\" maxlength=\"4\" value=\"$selected_year\" /> + </td> + </tr> + <tr> + <td nowrap=\"nowrap\" align=\"center\"> + <font size=\"-2\">Month</font> + </td> + <td nowrap=\"nowrap\" align=\"center\"> + <font size=\"-2\">Day</font> + </td> + <td nowrap=\"nowrap\" align=\"center\"> + <font size=\"-2\">Year</font> + </td> + </tr> + </table>" + +} + + +ad_proc -public pm::task::get_assignee_names { + {-task_item_id:required} +} { + Returns a list of assignees to a task (first name + last name) + @author Jade Rubick (jader@bread.com) + @creation-date 2004-10-20 + + @param task_item_id + + @return + + @error +} { + + return [db_list get_assignees { }] + } + + +ad_proc -public pm::task::assignee_role_list { + {-task_item_id:required} +} { + Returns a list of lists, with all assignees to a particular + task. {{party_id role_id} {party_id role_id}} + + Todo: dependency changes, deadline changes + + @author Jade Rubick (jader@bread.com) + @creation-date 2004-10-18 + + @param task_item_id + + @return + + @error +} { + + return [db_list_of_lists get_assignees_roles { }] + +} + + +ad_proc -public pm::task::what_changed { + {-comments_array:required} + {-comments_mime_type_array:required} + {-task_item_id_array:required} + {-number:required} + {-old_one_line_array:required} + {-old_description_array:required} + {-old_description_mime_type_array:required} + {-old_estimated_hours_work_array:required} + {-old_estimated_hours_work_min_array:required} + {-old_estimated_hours_work_max_array:required} + {-old_dependency_array:required} + {-old_percent_complete_array:required} + {-old_end_date_day_array:required} + {-old_end_date_month_array:required} + {-old_end_date_year_array:required} + {-old_project_item_id_array:required} + {-old_assignees_array:required} +} { + Compares how a task was and how it currently is, and + adds to the comments array a list of changes. + + @author Jade Rubick (jader@bread.com) + @creation-date 2004-10-20 + + @param comments_array + + @param comments_mime_type_array + + @param task_item_id_array an array of task_item_ids, with keys + based on number + + @param number the keys to the task_item_id array, a list of integers + + @param old_one_line_array + + @param old_description_array + + @param old_description_mime_type_array + + @param old_estimated_hours_work_array + + @param old_estimated_hours_work_min_array + + @param old_estimated_hours_work_max_array + + @param old_dependency_array + + @param old_percent_complete_array + + @param old_assignees_array + + @return + + @error +} { + + # we will append the changes to these arrays and convert them to + # text/html format + upvar 1 $comments_array comments_arr + upvar 1 $comments_mime_type_array comments_mime_type_arr + + upvar 1 $task_item_id_array task_item_id + upvar 1 $old_one_line_array old_one_line_arr + upvar 1 $old_description_array old_description_arr + upvar 1 $old_description_mime_type_array old_description_mime_type_arr + upvar 1 $old_estimated_hours_work_array old_estimated_hours_work_arr + upvar 1 $old_estimated_hours_work_min_array old_estimated_hours_work_min_arr + upvar 1 $old_estimated_hours_work_max_array old_estimated_hours_work_max_arr + upvar 1 $old_percent_complete_array old_percent_complete_arr + upvar 1 $old_end_date_day_array old_end_date_day_arr + upvar 1 $old_end_date_month_array old_end_date_month_arr + upvar 1 $old_end_date_year_array old_end_date_year_arr + upvar 1 $old_project_item_id_array old_project_item_id_arr + upvar 1 $old_assignees_array old_assignees_arr + upvar 1 $old_dependency_array old_dependency_arr + + set use_uncertain_completion_times_p [parameter::get -parameter "UseUncertainCompletionTimesP" -default "1"] + + # get the new task values + set tasks_item_id [list] + foreach num $number { + lappend tasks_item_id $task_item_id($num) + } + + pm::task::get \ + -tasks_item_id $tasks_item_id \ + -one_line_array one_line_array \ + -description_array description_array \ + -description_mime_type_array description_mime_type_array \ + -estimated_hours_work_array estimated_hours_work_array \ + -estimated_hours_work_min_array estimated_hours_work_min_array \ + -estimated_hours_work_max_array estimated_hours_work_max_array \ + -dependency_array dependency_array \ + -percent_complete_array percent_complete_array \ + -end_date_day_array end_date_day_array \ + -end_date_month_array end_date_month_array \ + -end_date_year_array end_date_year_array \ + -project_item_id_array project_item_id_array + + + foreach num $number { + + set changes [list] + + set tid $task_item_id($num) + + set old $old_percent_complete_arr($tid) + set new $percent_complete_array($tid) + + if {![string equal $old $new]} { + + if {$new >= 100 && $old < 100} { + + lappend changes "<b>Closing task</b>" + + } elseif {$new < 100 && $old >= 100} { + + lappend changes "<b>Reopening task</b>" + + } else { + lappend changes "Percent complete changed <i>from</i> $old%<i>to</i> $new%" + } + + } + + # end date + if { \ + ![string equal $old_end_date_day_arr($tid) $end_date_day_array($tid)] || \ + ![string equal $old_end_date_month_arr($tid) $end_date_month_array($tid)] || \ + ![string equal $old_end_date_year_arr($tid) $end_date_year_array($tid)]} { + + # internationalize the dates + set iso_date_old "$old_end_date_year_arr($tid)-$old_end_date_month_arr($tid)-$old_end_date_day_arr($tid) 00:00:00" + set iso_date_new "$end_date_year_array($tid)-$end_date_month_array($tid)-$end_date_day_array($tid) 00:00:00" + + if {[string equal $iso_date_old "-- 00:00:00"]} { + set date_old "no hard deadline" + } else { + set date_old [lc_time_fmt $iso_date_old "%x"] + } + + if {[string equal $iso_date_new "-- 00:00:00"]} { + set date_new "no hard deadline" + } else { + set date_new [lc_time_fmt $iso_date_new "%x"] + } + + lappend changes "Hard deadline changed <i>from</i> $date_old <i>to</i> <b>$date_new</b>" + } + + # one_line + if {![string equal $old_one_line_arr($tid) $one_line_array($tid)]} { + lappend changes "Subject changed <i>from</i> $old_one_line_arr($tid) <i>to</i> $one_line_array($tid)" + } + + # description + if { \ + ![string equal $old_description_arr($tid) $description_array($tid)] || \ + ![string equal $old_description_mime_type_arr($tid) $description_mime_type_array($tid)]} { + + set richtext_list [list $old_description_arr($tid) $old_description_mime_type_arr($tid)] + set old_description_html [template::util::richtext::get_property html_value $richtext_list] + set richtext_list [list $description_array($tid) $description_mime_type_array($tid)] + set new_description_html [template::util::richtext::get_property html_value $richtext_list] + + lappend changes "Description changed from<br />$old_description_html <i>(see below for new description)</i>" + } + + # estimated_hours_work + if {[string is true $use_uncertain_completion_times_p]} { + + if {![string equal $old_estimated_hours_work_min_arr($tid) $estimated_hours_work_min_array($tid)]} { + lappend changes "Work estimate (min) changed <i>from</i> $old_estimated_hours_work_min_arr($tid) <i>to</i> $estimated_hours_work_min_array($tid) hrs" + } + + if {![string equal $old_estimated_hours_work_max_arr($tid) $estimated_hours_work_max_array($tid)]} { + lappend changes "Work estimate (max) changed <i>from</i> $old_estimated_hours_work_max_arr($tid) <i>to</i> $estimated_hours_work_max_array($tid) hrs" + } + } else { + + if {![string equal $old_estimated_hours_work_arr($tid) $estimated_hours_work_array($tid)]} { + lappend changes "Work estimate (min) changed <i>from</i> $old_estimated_hours_work_arr($tid) <i>to</i> $estimated_hours_work_array($tid) hrs" + } + + } + + set new_assignees [pm::task::get_assignee_names \ + -task_item_id $task_item_id($num)] + + # check for assignees that have been added + + foreach new $new_assignees { + if { [lsearch $old_assignees_arr($tid) $new] == -1} { + lappend changes "Added: $new" + } + } + + # check for assignees that have been removed + foreach old $old_assignees_arr($tid) { + if { [lsearch $new_assignees $old] == -1} { + lappend changes "Removed: $old" + } + } + + # project + + if {![string equal $old_project_item_id_arr($tid) $project_item_id_array($tid)]} { + + set old [pm::project::name -project_item_id $old_project_item_id_arr($tid)] + + lappend changes "Project changed <i>from</i> $old" + + } + + # dependency + if {![string equal $old_dependency_arr($tid) $dependency_array($tid)]} { + + if {[empty_string_p $old_dependency_arr($tid)]} { + set old "Nothing" + } else { + set old [pm::task::name \ + -task_item_id $old_dependency_arr($tid)] + } + + if {[empty_string_p $dependency_array($tid)]} { + set new "Nothing" + } else { + set new [pm::task::name \ + -task_item_id $dependency_array($tid)] + } + + lappend changes "Dependency changed <i>from</i> $old ($old_dependency_arr($tid)) <i>to</i> $new ($dependency_array($tid))" + } + + + # convert comments to richtext + set richtext_list [list $comments_arr($num) $comments_mime_type_arr($num)] + set comment_html [template::util::richtext::get_property html_value $richtext_list] + + + # add in changes + + if {[llength $changes] > 0} { + append comment_html "<ul><li>[join $changes "<li>"]</ul>" + + set comments_arr($num) $comment_html + set comments_mime_type_arr($num) "text/html" + } + + } + + +}