# ----------------------------------------------------------------------------- # tpb12 / tpb.pl # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # date description # ---------- ----------------------------------------------------------------- # 11/12/2010 include $user in data sent to add_users.tt so that the javascript # can check if user is deleting themself # 09/28/2011 add filtering to list_plans; change add_extra_data to set admin # 09/29/2011 add change_all_section_status # 02/02/2012 remove use Crypt::GeneratePassword # 06/22/2012 stop using ITO::Config # 02/18/2013 replace $r->no_cache with set_cache_control (q.v. for details) # 05/27/2014 prepend erate_ to template in list_plans when user has single plan # and it is erate # 09/05/2014 rename libraries to TPB12; rename program directory to tpb12 # ----------------------------------------------------------------------------- use strict; use warnings; use Apache2::Const -compile => qw(OK); use Apache2::Request; use Apache2::RequestIO; use Apache2::RequestUtil; use Date::Manip qw( UnixDate ); use Readonly; use ITO::TPB12::Utility::CleanWord; use ITO::TPB12::Utility::FieldValidation; use ITO::TPB12::Utility::LogError; use ITO::TPB12::Utility::Utility; use ITO::TPB12::Activity; use ITO::TPB12::AdminRole; use ITO::TPB12::Benchmark; use ITO::TPB12::BenchmarkActivity; use ITO::TPB12::BudgetCategory; use ITO::TPB12::BudgetItem; use ITO::TPB12::DirectoryToRole; use ITO::TPB12::Goal; use ITO::TPB12::Note; use ITO::TPB12::Objective; use ITO::TPB12::Position; use ITO::TPB12::Purpose; use ITO::TPB12::RoleToUser; use ITO::TPB12::Role; use ITO::TPB12::Section; use ITO::TPB12::Stakeholder; use ITO::TPB12::TechPlan; use ITO::TPB12::TextArea; use ITO::TPB12::Upload; use ITO::TPB::User; # ----------------------------------------------------------------------------- # data used to copy a tech plan # the copy process first clones the TechPlan record, and then recursively # handles the children in this hash to copy the associated records. # accessor - method of the parent used to retrieve the child records # key - primary key column; after a record is copied, we save its # key so that children which use it as a FK can be created with # the new FK values (but see note on special usage for the # TechPlanToUser table) # substitute - FK column names with information on what saved PK value is # to be used when the clone is produced. It can also be used # to null a column, such as timestamp, so that the default # value will be used # see the _clone_records subroutine for details on how key and substitute are used use vars qw(%COPY_CONTROL); %COPY_CONTROL = ( 'children' => [ { # ITO::TPB12::Activity 'accessor' => 'child_activities', 'key' => 'id', 'substitute' => { 'tech_plan' => 'tech_plan:id', 'timestamp' => undef, }, }, { # ITO::TPB12::Benchmark 'accessor' => 'child_benchmarks', 'key' => 'id', 'substitute' => { 'tech_plan' => 'tech_plan:id', 'timestamp' => undef, }, }, { # ITO::TPB12::BenchmarkActivity 'accessor' => 'benchmark_activities', 'key' => 'id', 'substitute' => { 'tech_plan' => 'tech_plan:id', 'benchmark' => 'child_benchmarks:id', 'timestamp' => undef, }, }, { # ITO::TPB12::BudgetItem 'accessor' => 'budget_items', 'key' => 'id', 'substitute' => { 'tech_plan' => 'tech_plan:id', 'timestamp' => undef, }, }, { # ITO::TPB12::Goal 'accessor' => 'goals', 'key' => 'id', 'substitute' => { 'tech_plan' => 'tech_plan:id', 'timestamp' => undef, }, 'children' => [ { # ITO::TPB12::Objective 'accessor' => 'objectives', 'key' => 'id', 'substitute' => { 'goal' => 'goals:id', 'timestamp' => undef, }, 'children' => [ { # ITO::TPB12::Activity 'accessor' => 'activities', 'key' => 'id', 'substitute' => { 'tech_plan' => 'tech_plan:id', 'objective' => 'objectives:id', 'timestamp' => undef, }, }, { # ITO::TPB12::Benchmark 'accessor' => 'benchmarks', 'key' => 'id', 'substitute' => { 'tech_plan' => 'tech_plan:id', 'objective' => 'objectives:id', 'timestamp' => undef, }, }, ], # end children of objectives }, ], # end children of goals }, # end goals { # ITO::TPB12::Note 'accessor' => 'notes', 'key' => 'id', 'conditional' => '*copy_notes', 'substitute' => { 'tech_plan' => 'tech_plan:id', 'timestamp' => undef, }, }, { # ITO::TPB12::Stakeholder 'accessor' => 'stakeholders', 'key' => 'id', 'substitute' => { 'tech_plan' => 'tech_plan:id', 'timestamp' => undef, }, }, # upload must preceed textarea so that the image tags in the # text areas can be updated { # ITO::TPB12::Upload 'accessor' => 'uploads', 'key' => 'id', 'special' => 'upload', 'substitute' => { 'tech_plan' => 'tech_plan:id', 'timestamp' => undef, }, }, { # ITO::TPB12::TextArea 'accessor' => 'textareas', 'key' => 'id', 'special' => 'textarea', 'substitute' => { 'tech_plan' => 'tech_plan:id', 'timestamp' => undef, }, }, { # ITO::TPB12::TechPlanToUser # this table works a bit differently than the rest. it has two columns # as its PK - tech_plan and user. when the copy method is called for # a table with more than one PK column, all PK values must be provided. # for this table, the two PK columns are FK's to TechPlan and User. # to accomodate this, we identify the 'user' column as the PK which # is to be saved for this table. _clone_records puts the old value # of the column into the substitute_keys hash before the copy, so # we can refer to it in 'substitute'; after the copy is made, the # new value of the column (which won't be different in this case) # is put into the substitute_keys hash. 'accessor' => 'users', 'key' => 'user', 'substitute' => { 'tech_plan' => 'tech_plan:id', 'user' => 'users:user', }, }, ], ); # ----------------------------------------------------------------------------- my $r = Apache2::RequestUtil->request; set_cache_control($r); my $req = Apache2::Request->new($r); $r->content_type('text/html; charset=utf-8'); Readonly my $TEMPLATE_PATH => getTemplatePaths($r); my ($error, $user, $admin) = userValidation( $r, $TEMPLATE_PATH, 'tpb12 tpb'); return $error if (defined $error); { no strict 'refs'; my $run_mode = $req->param('run_mode') || 'list_plans'; #errorToLog($r, "tpb12 request"); my $subroutine = '_' . $run_mode; &{$subroutine}( $r, $req, $TEMPLATE_PATH, $user, $admin ); } # ----------------------------------------------------------------------------- # # request handlers # # ----------------------------------------------------------------------------- sub _view_plan { my ( $r, $req, $TEMPLATE_PATH, $user, $admin ) = @_; my ($error, $tpb) = _parameter_checks( $r, $req, $TEMPLATE_PATH, 'view_plan', 'tech_plan' ); return $error if (defined $error); if (! $tpb->has_read_access($user, $admin)) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Authorization failed - user does not have access to plan (tpb12 view_plan)", "Authorization failed -- please contact the system administrator." ); } if ($tpb->status eq 'Deleted') { return errorToLogAndUser($r, $TEMPLATE_PATH, "Plan has been deleted (tpb12 view_plan)", "The plan you requested has been deleted." ); } my %record = (); my $template; if ($req->param('template')) { # parameter isn't used in application (just for testing) so we don't want to adjust it $template = $req->param('template') . '.tt'; } else { $template = 'view_plan.tt'; $template = 'erate_' . $template if ($tpb->purpose->erate eq 'Only'); } $record{'run_mode'} = $req->param('run_mode'); @{ $record{'sections'} } = ITO::TPB12::Section->retrieve_all_sorted_by('seq'); @{ $record{'categories'} } = ITO::TPB12::BudgetCategory->retrieve_all_sorted_by('seq');; $tpb->template_configure( -stash_cache => 0, -template_options => { INCLUDE_PATH => $TEMPLATE_PATH, } ); $tpb->template_define( 'template', $template ); my $content = $tpb->template_render( 'template', %record ); $r->print( $content); open SAVE, '>', '/tmp/sample.html'; print SAVE $content; close SAVE; return Apache2::Const::OK; } # ----------------------------------------------------------------------------- sub _view_plan_section { my ( $r, $req, $TEMPLATE_PATH, $user, $admin ) = @_; my ($error, $tpb) = _parameter_checks( $r, $req, $TEMPLATE_PATH, 'view_plan', 'tech_plan' ); return $error if (defined $error); if (! $tpb->has_read_access($user, $admin)) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Authorization failed - user does not have access to plan (tpb12 view_plan_section)", "Authorization failed -- please contact the system administrator." ); } if ($tpb->status eq 'Deleted') { return errorToLogAndUser($r, $TEMPLATE_PATH, "Plan has been deleted (tpb12 view_plan_section)", "The plan you requested has been deleted." ); } my $section = $req->param('section'); if (! defined $section) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Required field [section] is missing. (tpb12 view_plan_section)", "There was an error. The required field [section] is missing. Please contact the system administrator." ); } my %record = (); $record{'run_mode'} = $req->param('run_mode'); $record{'section'} = ITO::TPB12::Section->retrieve($section); @{ $record{'categories'} } = ITO::TPB12::BudgetCategory->retrieve_all_sorted_by('seq');; $tpb->template_configure( -stash_cache => 0, -template_options => { INCLUDE_PATH => $TEMPLATE_PATH, } ); my $template = 'view_plan_section.tt'; $template = 'erate_' . $template if ($tpb->purpose->erate eq 'Only'); $tpb->template_define( 'template', $template); my $content = $tpb->template_render( 'template', %record ); $r->print( $content); return Apache2::Const::OK; } # ----------------------------------------------------------------------------- sub _list_plans { my ( $r, $req, $TEMPLATE_PATH, $user, $admin ) = @_; my $tpb = 'ITO::TPB12::TechPlan'; my %record = (); $record{'user'} = $user; $record{'admin'} = $admin; my $template = 'list_plans.tt'; my %tpb_hash = (); my $filtered = 0; my %where = ( 'user' => $user->id, 'tech_plan.status' => ['Initiated', 'Active'], ); my $search_type = $req->param('search_type'); if ($search_type) { ++ $filtered; $where{'tech_plan.plan_type'} = $search_type; $record{'search_type'} = $search_type; } my $search_purpose = $req->param('search_purpose'); if ($search_purpose) { ++ $filtered; $where{'tech_plan.purpose'} = $search_purpose; $record{'search_purpose'} = $search_purpose; $record{'search_purpose_text'} = ITO::TPB12::Purpose->retrieve($search_purpose)->name; } my $search_year = $req->param('search_year'); if ($search_year) { ++ $filtered; $where{'tech_plan.section_1_beginning'} = $search_year; $record{'search_year'} = $search_year; } my $search_created = $req->param('search_created'); if ($search_created) { ++ $filtered; $where{'tech_plan.date_created'} = { '-like' => $search_created . '%'}; $record{'search_created'} = $search_created; } my @tpu = ITO::TPB12::TechPlanToUser->deep_search_where(%where); if ( ! @tpu) { $record{'message'} = $filtered ? 'No plans were found which matched the selection criteria.' : "No plans found. Please create a new plan."; } elsif ( scalar @tpu == 1 && ! $filtered) { $tpb = $tpu[0]->tech_plan; $record{'access_level'} = $tpb->access_level($user); if ($tpb->has_edit_access($user)) { $template = 'plan_index.tt'; } else { $template = 'read_plan.tt'; } $template = 'erate_' . $template if ($tpb->purpose->erate eq 'Only'); @{ $record{'sections'} } = ITO::TPB12::Section->retrieve_all_sorted_by('seq'); } else { @{ $record{'tpu'} } = sort { tech_plan_compare(\%tpb_hash, $a->tech_plan, $b->tech_plan) } @tpu; } $record{'filtered'} = $filtered; @{ $record{'purposes'} } = ITO::TPB12::Purpose->search_where( { 'status' => 'Active', }, { 'order_by' => 'seq' } ); $tpb->template_configure( -stash_cache => 0, -template_options => { INCLUDE_PATH => $TEMPLATE_PATH } ); $tpb->template_define( 'template', $template ); $r->print( $tpb->template_render( 'template', %record ) ); return Apache2::Const::OK; } # ----------------------------------------------------------------------------- # index of the section elements of a plan sub _plan_index { my ( $r, $req, $TEMPLATE_PATH, $user, $admin ) = @_; my %record = (); my ($error, $tpb) = _parameter_checks( $r, $req, $TEMPLATE_PATH, 'plan_index', 'tech_plan' ); return $error if (defined $error); if (! $tpb->has_read_access($user, $admin)) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Authorization failed - user does not have access to plan (tpb12 plan_index)", "Authorization failed -- please contact the system administrator.", ); } if ($tpb->status eq 'Deleted' && ! $admin) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Plan has been deleted (tpb12 plan_index)", "The plan you requested has been deleted." ); } # select template based on access level $record{'access_level'} = $tpb->access_level($user, $admin); my $template = 'plan_index.tt'; if (! $tpb->has_edit_access($user, $admin)) { $template = 'read_plan.tt'; } $template = 'erate_' . $template if ($tpb->purpose->erate eq 'Only'); @{ $record{'sections'} } = ITO::TPB12::Section->retrieve_all_sorted_by('seq'); $record{'user'} = $user; $record{'admin'} = $admin; $tpb->template_configure( -stash_cache => 0, -template_options => { INCLUDE_PATH => $TEMPLATE_PATH } ); $tpb->template_define( 'template', $template ); $r->print( $tpb->template_render( 'template', %record ) ); return Apache2::Const::OK; } # ----------------------------------------------------------------------------- sub _plan_notes { my ( $r, $req, $TEMPLATE_PATH, $user, $admin ) = @_; my %record = (); my ($error, $tpb) = _parameter_checks( $r, $req, $TEMPLATE_PATH, 'plan_notes', 'tech_plan' ); return $error if (defined $error); if (! $tpb->has_comment_access($user, $admin)) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Authorization failed - user does not have access to plan comments (tpb12 plan_notes)", "Authorization failed -- please contact the system administrator.", ); } if ($tpb->status eq 'Deleted') { return errorToLogAndUser($r, $TEMPLATE_PATH, "Plan has been deleted (tpb12 plan_notes)", "The plan has been deleted." ); } @{ $record{'sections'} } = ITO::TPB12::Section->retrieve_all_sorted_by('seq'); $record{'user'} = $user; $record{'admin'} = $admin; $record{'access_level'} = $tpb->access_level($user, $admin); $tpb->template_configure( -stash_cache => 0, -template_options => { INCLUDE_PATH => $TEMPLATE_PATH } ); $tpb->template_define( 'template', 'plan_notes.tt' ); $r->print( $tpb->template_render( 'template', %record ) ); return Apache2::Const::OK; } # ----------------------------------------------------------------------------- sub _create_plan { my ( $r, $req, $TEMPLATE_PATH, $user, $admin ) = @_; my %record = (); my $tpb = 'ITO::TPB12::TechPlan'; $tpb = $tpb->create( { 'county' => $user->county, 'district_id' => $user->district_id, 'last_edit_date' => UnixDate( 'today', '%Y-%m-%d %T' ), 'creator' => $user->id, 'date_created' => UnixDate( 'today', '%Y-%m-%d %T' ), 'last_update' => $user->id, } ); # we then add a record to the tech_plan_to_users table my $tpu = 'ITO::TPB12::TechPlanToUser'; $tpu->create( { 'user' => $user->id, 'tech_plan' => $tpb->id } ); @{ $record{'sections'} } = ITO::TPB12::Section->retrieve_all_sorted_by('seq'); @{ $record{'purposes'} } = ITO::TPB12::Purpose->search_where( { 'status' => 'Active', }, { 'order_by' => 'seq' } ); $record{'user'} = $user; $record{'admin'} = $admin; $tpb->template_configure( -template_options => { INCLUDE_PATH => $TEMPLATE_PATH } ); $tpb->template_define( 'template', 'choose_district.tt' ); $r->print( $tpb->template_render( 'template', %record ) ); return Apache2::Const::OK; } # ----------------------------------------------------------------------------- sub _copy_plan { # note that copy plan copies all sections, and sets all Finished # section statuses to Draft, regardless of plan type my ( $r, $req, $TEMPLATE_PATH, $user, $admin ) = @_; my %record = (); my ($error, $old_plan) = _parameter_checks( $r, $req, $TEMPLATE_PATH, 'copy_plan', 'tech_plan' ); if (! $old_plan->has_edit_access($user, $admin)) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Authorization failed - user does not have edit access to plan (tpb12 copy_plan)", "Authorization failed -- please contact the system administrator.", ); } return $error if ($error); if ( (! defined $req->param('title')) || $req->param('title') =~ /^\s*$/) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Required field [title] is missing. (tpb12 copy_plan )", "There was an error. The required field [title] is missing. Please contact the system administrator." ); } # we need to change any status which is 'Finished' to 'Draft'; to do this, # we build a substitution hash by iterating through the columns of the tech plan my %substitute = ( 'timestamp' => undef, 'title' => trim($req->param('title')), 'last_edit_date' => UnixDate( 'today', '%Y-%m-%d %T' ), ); my @columns = $old_plan->columns('All'); foreach my $column (@columns) { next unless ($column =~ /_status$/); if ($old_plan->$column eq 'Finished') { $substitute{$column} = 'Draft'; } } # create the new tech_plan record my $tpb = $old_plan->copy( \%substitute ); # clone_records uses %substitute to get the new values to be used in # foreign keys in duplicated records %substitute = ( 'tech_plan:id' => $tpb->id, ); if ($req->param('copy_notes') ) { $substitute{'*copy_notes'} = $req->param('copy_notes'); } _clone_records(\%COPY_CONTROL, \%substitute, $old_plan); @{ $record{'sections'} } = ITO::TPB12::Section->retrieve_all_sorted_by('seq'); $record{'user'} = $user; $record{'admin'} = $admin; my $template = 'plan_index.tt'; $template = 'erate_' . $template if ($tpb->purpose->erate eq 'Only'); $tpb->template_configure( -template_options => { INCLUDE_PATH => $TEMPLATE_PATH } ); $tpb->template_define( 'template', $template ); $r->print( $tpb->template_render( 'template', %record ) ); return Apache2::Const::OK; } # ----------------------------------------------------------------------------- sub _delete_plan { my ( $r, $req, $TEMPLATE_PATH, $user, $admin ) = @_; my %record = (); my ($error, $tpb) = _parameter_checks( $r, $req, $TEMPLATE_PATH, 'delete_plan', 'tech_plan' ); return $error if ($error); if (! $tpb->has_edit_access($user, $admin)) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Authorization failed - user does not have edit access to plan (tpb12 delete_plan)", "Authorization failed -- please contact the system administrator.", ); } if ( (! defined $req->param('confirmed')) || $req->param('confirmed') !~ /^YES$/i) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Required field [confirmed] is missing or invalid. (tpb12 delete_plan )", "There was an error. The required field [confirmed] is missing or invalid. Please contact the system administrator." ); } if ($tpb->status eq 'Initiated') { # if the plan hasn't been started, just delete the record $tpb->delete(); } else { # otherwise set the status to deleted $tpb->set( 'status' => 'Deleted', 'last_update' => $user->id ); $tpb->update(); } return _list_plans( $r, $req, $TEMPLATE_PATH, $user, $admin ); } # ----------------------------------------------------------------------------- sub _activate_plan { my ( $r, $req, $TEMPLATE_PATH, $user, $admin ) = @_; my %record = (); my ($error, $tpb) = _parameter_checks( $r, $req, $TEMPLATE_PATH, 'activate_plan', 'tech_plan' ); return $error if ($error); if (! $tpb->has_edit_access($user, $admin)) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Authorization failed - user does not have edit access to plan (tpb12 activate_plan)", "Authorization failed -- please contact the system administrator.", ); } # update the tpb12 record $tpb->set( 'status' => 'Active', 'last_update' => $user->id ); $tpb->update(); @{ $record{'sections'} } = ITO::TPB12::Section->retrieve_all_sorted_by('seq'); $record{'user'} = $user; $record{'admin'} = $admin; my $template = 'plan_index.tt'; $template = 'erate_' . $template if ($tpb->purpose->erate eq 'Only'); $tpb->template_configure( -stash_cache => 0, -template_options => { INCLUDE_PATH => $TEMPLATE_PATH } ); $tpb->template_define( 'template', $template ); $r->print( $tpb->template_render( 'template', %record ) ); return Apache2::Const::OK; } # ----------------------------------------------------------------------------- sub _add_users { my ( $r, $req, $TEMPLATE_PATH, $user, $admin ) = @_; my %record = (); my ($error, $tpb) = _parameter_checks( $r, $req, $TEMPLATE_PATH, 'add_users', 'tech_plan' ); return $error if ($error); if (! $tpb->has_edit_access($user, $admin)) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Authorization failed - user does not have edit access to plan (tpb12 add_users)", "Authorization failed -- please contact the system administrator.", ); } if ($tpb->status eq 'Deleted') { return errorToLogAndUser($r, $TEMPLATE_PATH, "Plan has been deleted (tpb12 add_users)", "The plan has been deleted." ); } $record{'user'} = $user; @{ $record{'users'} } = sort { $a->user->last_name cmp $b->user->last_name || $a->user->first_name cmp $b->user->first_name } $tpb->users; @{ $record{'positions'} } = ITO::TPB12::Position->retrieve_all_sorted_by('short_name'); $tpb->template_configure( -template_options => { INCLUDE_PATH => $TEMPLATE_PATH } ); $tpb->template_define( 'template', 'add_users.tt' ); $r->print( $tpb->template_render( 'template', %record ) ); return Apache2::Const::OK; } # ----------------------------------------------------------------------------- # do_choose_district is only run once for the plan, on initial creation sub _do_choose_district { my ( $r, $req, $TEMPLATE_PATH, $user, $admin ) = @_; my %record = (); my ($error, $tpb) = _parameter_checks( $r, $req, $TEMPLATE_PATH, 'do_choose_district', 'tech_plan' ); return $error if (defined $error); if (! $tpb->has_edit_access($user, $admin)) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Authorization failed - user does not have edit access to plan (tpb12 do_choose_district)", "Authorization failed -- please contact the system administrator.", ); } if ($tpb->status eq 'Deleted') { return errorToLogAndUser($r, $TEMPLATE_PATH, "Plan has been deleted (tpb12 do_choose_district)", "The plan has been deleted." ); } if ( (! defined $req->param('title')) || $req->param('title') =~ /^\s*$/) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Required field [title] is missing. (tpb12 do_choose_district )", "There was an error. The required field [title] is missing. Please contact the system administrator." ); } # pick up any optional fields in the form # note that this includes the status columns for title-district and plan duration, # both set to Finished my @optional_fields = $tpb->columns( 'Optional' ); foreach my $field ( @optional_fields ) { if (defined $req->param($field)) { my $old_data = $tpb->$field || ''; my $new_data = trim($req->param($field)); $tpb->set($field => $new_data); } } # set school type based on plan type if ($tpb->plan_type eq 'School') { $tpb->set('school_type' => 'Public'); } elsif ($tpb->plan_type eq 'Private School') { $tpb->set('school_type' => 'Private'); } # preset Appendix J information based on the district or school my $district_or_school; if ($tpb->school_id) { $district_or_school = $tpb->school; } else { $district_or_school = $tpb->district; } # cds has zip+4, but some are just zip with trailing dash my $mail_zip = $district_or_school->mail_zip; if ($mail_zip) { $mail_zip =~ s/-\s*$//; } $tpb->set( 'contact_1_address' => $district_or_school->mail_address, 'contact_1_city' => $district_or_school->mail_city, 'contact_1_zip' => $mail_zip, ); # phone formatted (999) 999-9999 (varchar 15), but could be different my $phone = $district_or_school->phone; if ($phone) { $phone =~ s/\D//g; $tpb->set( 'contact_1_phone_area' => substr($phone, 0, 3), 'contact_1_phone_pre' => substr($phone, 3, 3), 'contact_1_phone_suffix' => substr($phone, 6, 4), ); } $tpb->set('contact_1_fax' => $district_or_school->fax); # update the tpb12 record $tpb->set( 'last_update' => $user->id, 'last_edit_date' => UnixDate( 'today', '%Y-%m-%d %T' ), ); $tpb->update(); # since this is when duration is set, we need to create benchmarks _manage_benchmarks( $r, $req, $TEMPLATE_PATH, $user, 0, $tpb ); @{ $record{'sections'} } = ITO::TPB12::Section->retrieve_all_sorted_by('seq'); $record{'user'} = $user; $record{'admin'} = $admin; my $template = 'plan_index.tt'; $template = 'erate_' . $template if ($tpb->purpose->erate eq 'Only'); $tpb->template_configure( -template_options => { INCLUDE_PATH => $TEMPLATE_PATH } ); $tpb->template_define( 'template', $template ); $r->print( $tpb->template_render( 'template', %record ) ); return Apache2::Const::OK; } # ----------------------------------------------------------------------------- # edit_section and do_edit_section handle a status field, any optional fields # in the tpb12 record, and optionally one or more text areas # do_edit_status is invoked from the same form to only change the status field sub _edit_section { my ( $r, $req, $TEMPLATE_PATH, $user, $admin ) = @_; my %record = (); my ($error, $tpb, $section) = _parameter_checks( $r, $req, $TEMPLATE_PATH, 'edit_section', 'tech_plan section' ); return $error if (defined $error); if (! $tpb->has_read_access($user, $admin)) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Authorization failed - user does not have access to plan (tpb12 edit_section)", "Authorization failed -- please contact the system administrator.", ); } if ($tpb->status eq 'Deleted') { return errorToLogAndUser($r, $TEMPLATE_PATH, "Plan has been deleted (tpb12 edit_section)", "The plan has been deleted." ); } my $template = $tpb->has_edit_access($user, $admin) ? $section->edit_template : $section->read_template; if (! $template) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Section does not have a template defined for " . $tpb->access_level($user, $admin) . " access (tpb12 edit_section)", "There was an error -- please contact the system administrator." ); } my $status_column = $section->status_col_name; $record{'section'} = $section; $record{'section_status'} = $tpb->$status_column if ($status_column); $record{'access_level'} = $tpb->access_level($user, $admin); _add_extra_data($tpb, $section, \%record, $user, $admin); $template = 'erate_' . $template if ($tpb->purpose->erate eq 'Only'); $tpb->template_configure( -template_options => { INCLUDE_PATH => $TEMPLATE_PATH } ); $tpb->template_define( 'template', $template ); $r->print( $tpb->template_render( 'template', %record ) ); return Apache2::Const::OK; } # ----------------------------------------------------------------------------- sub _do_edit_status { my ( $r, $req, $TEMPLATE_PATH, $user, $admin ) = @_; my %record = (); my ($error, $tpb, $section) = _parameter_checks( $r, $req, $TEMPLATE_PATH, 'do_edit_status', 'tech_plan section' ); return $error if (defined $error); if (! $tpb->has_edit_access($user, $admin)) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Authorization failed - user does not have edit access to plan (tpb12 do_edit_status)", "Authorization failed -- please contact the system administrator.", ); } if ($tpb->status eq 'Deleted') { return errorToLogAndUser($r, $TEMPLATE_PATH, "Plan has been deleted (tpb12 do_edit_status)", "The plan has been deleted." ); } my $status_column = $section->status_col_name; # the form can also have a generic status column if ($status_column && $req->param('section_status')) { $tpb->set($status_column => trim($req->param('section_status'))); } $tpb->set( 'last_update' => $user->id, 'last_edit_date' => UnixDate( 'today', '%Y-%m-%d %T' ), ); $tpb->update(); $record{'section'} = $section; $record{'section_status'} = $tpb->$status_column if ($status_column); _add_extra_data($tpb, $section, \%record, $user, $admin); my $template = $section->edit_template; $template = 'erate_' . $template if ($tpb->purpose->erate eq 'Only'); $tpb->template_configure( -template_options => { INCLUDE_PATH => $TEMPLATE_PATH } ); $tpb->template_define( 'template', $template ); $r->print( $tpb->template_render( 'template', %record ) ); return Apache2::Const::OK; } # ----------------------------------------------------------------------------- sub _do_edit_section { my ( $r, $req, $TEMPLATE_PATH, $user, $admin ) = @_; my %record = (); my ($error, $tpb, $section) = _parameter_checks( $r, $req, $TEMPLATE_PATH, 'do_edit_section', 'tech_plan section' ); return $error if (defined $error); if (! $tpb->has_edit_access($user, $admin)) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Authorization failed - user does not have edit access to plan (tpb12 do_edit_section)", "Authorization failed -- please contact the system administrator.", ); } if ($tpb->status eq 'Deleted') { return errorToLogAndUser($r, $TEMPLATE_PATH, "Plan has been deleted (tpb12 do_edit_section)", "The plan has been deleted." ); } my $data_changed = 0; my $auto_update_status = 1; my $status_column = $section->status_col_name; # we will update the status to draft if there's a status column, # this isn't the plan duration section, # it hasn't been set by the user in the past or now, and any data was changed if (! $status_column || $status_column eq 'section_1_status' || $tpb->$status_column ne 'Not Started') { $auto_update_status = 0; } my $old_duration = $tpb->years; my $old_plan_is_erate = $tpb->purpose->erate; # pick up any optional fields in the form (might include the status field) # There is a conflict regarding the county-district-school fields, which # are in two templates which use this subroutine - district_and_title.tt # and stakeholders.tt (via position_cds_select.tt). # The district_and_title.tt fields refer to the tech_plan columns, while # those in stakeholders.tt are input fields used with ajax to edit the # stakeholder records. # To handle this, the optional_fields parameter is used to override what # columns definition is to be used, if any, for optional fields my @optional_fields = (); if ($req->param('optional_fields')) { @optional_fields = $tpb->columns( $req->param('optional_fields') ); } else { @optional_fields = $tpb->columns( 'Optional' ); } foreach my $field ( @optional_fields ) { if (defined $req->param($field)) { my $old_data = $tpb->$field || ''; my $new_data = trim($req->param($field)); $tpb->set($field => $new_data); if ($old_data ne $new_data) { if ($field eq $status_column) { # user changed status, so we won't $auto_update_status = 0; } else { ++$data_changed; } } } } # set school type based on plan type if ($tpb->plan_type eq 'School') { $tpb->set('school_type' => 'Public'); } elsif ($tpb->plan_type eq 'Private School') { $tpb->set('school_type' => 'Private'); } # now do any text areas if ($section->extra_data =~ /\btextarea(s?)\b/i) { # get the names of all the textareas in the form my @parameters = $req->param(); @parameters = grep { /^textarea/ } @parameters; foreach my $parameter (@parameters) { (my $id = $parameter) =~ s/^textarea//; my $new_data = trim($req->param($parameter)); my $textarea = ITO::TPB12::TextArea->retrieve( 'id' => $id ); if (! $textarea) { errorToLogAndUser($r, $req, "Textarea [$id] not found in database. (tpb12 do_edit_section)", "There was an error. Please contact the system administrator." ); } # remove any MS Word markup garbage $new_data = clean_word($new_data); if ($textarea->text ne $new_data) { $textarea->set( 'text' => $new_data, 'last_update' => $user->id, ); $textarea->update(); ++$data_changed; } } } # the form can also have a generic status column if ($status_column && $req->param('section_status')) { my $old_data = $tpb->$status_column; my $new_data = trim($req->param('section_status')); if ($old_data ne $new_data) { $tpb->set($status_column => $new_data); $auto_update_status = 0; } } # update the status to draft if data changed and auto change is allowed if ($status_column && $data_changed && $auto_update_status) { $tpb->set($status_column => 'Draft'); } $tpb->set( 'last_update' => $user->id, 'last_edit_date' => UnixDate( 'today', '%Y-%m-%d %T' ), ); $tpb->update(); # if the plan duration changed, we need to adjust benchmarks if ($old_duration != $tpb->years) { _manage_benchmarks( $r, $req, $TEMPLATE_PATH, $user, $old_duration, $tpb ); } # if the plan was E-rate, and either the plan is no longer E-rate or # the duration decreased, delete unused E-rate text areas # note that we don't need to add text areas for the reverse situation # because they are added automatically when the parent section is edited if ($old_plan_is_erate eq 'Yes' && ($tpb->purpose->erate eq 'No' || $old_duration > $tpb->years)) { my @text_areas = $tpb->textareas; foreach my $text_area (@text_areas) { if ($text_area->section->excluded_erate_area($tpb)) { $text_area->delete; } } } $record{'section'} = $section; $record{'section_status'} = $tpb->$status_column if ($status_column); _add_extra_data($tpb, $section, \%record, $user, $admin); my $template = $section->edit_template; $template = 'erate_' . $template if ($tpb->purpose->erate eq 'Only'); $tpb->template_configure( -template_options => { INCLUDE_PATH => $TEMPLATE_PATH } ); $tpb->template_define( 'template', $template ); $r->print( $tpb->template_render( 'template', %record ) ); return Apache2::Const::OK; } # ----------------------------------------------------------------------------- sub _add_goal { my ( $r, $req, $TEMPLATE_PATH, $user, $admin ) = @_; my %record = (); my ($error, $tpb, $section) = _parameter_checks( $r, $req, $TEMPLATE_PATH, 'add_goal', 'tech_plan section' ); return $error if (defined $error); if (! $tpb->has_edit_access($user, $admin)) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Authorization failed - user does not have edit access to plan (tpb12 add_goal)", "Authorization failed -- please contact the system administrator.", ); } if ($tpb->status eq 'Deleted') { return errorToLogAndUser($r, $TEMPLATE_PATH, "Plan has been deleted (tpb12 add_goal)", "The plan has been deleted." ); } $record{'section'} = $section; $record{'run_mode'} = 'do_add_goal'; _add_extra_data($tpb, $section, \%record, $user, $admin); my $template = 'goal.tt'; $template = 'erate_' . $template if ($tpb->purpose->erate eq 'Only'); $tpb->template_configure( -template_options => { INCLUDE_PATH => $TEMPLATE_PATH } ); $tpb->template_define( 'template', $template ); $r->print( $tpb->template_render( 'template', %record ) ); return Apache2::Const::OK; } # ----------------------------------------------------------------------------- sub _do_add_goal { my ( $r, $req, $TEMPLATE_PATH, $user, $admin ) = @_; my %record = (); my ($error, $tpb, $section) = _parameter_checks( $r, $req, $TEMPLATE_PATH, 'do_add_goal', 'tech_plan section' ); return $error if (defined $error); if ($tpb->status eq 'Deleted') { return errorToLogAndUser($r, $TEMPLATE_PATH, "Plan has been deleted (tpb12 do_add_goal)", "The plan has been deleted." ); } my $goal = 'ITO::TPB12::Goal'; ($error, $goal) = _create_record_from_form( $r, $req, $TEMPLATE_PATH, $user, 'do_add_goal', \%record, $goal ); return $error if (defined $error); $goal = $goal->create( \%record ); _update_last_edit_and_status($tpb, $user, $section); %record = (); $record{'section'} = $section; $record{'goal'} = $goal; $record{'run_mode'} = 'do_edit_goal'; _add_extra_data($tpb, $section, \%record, $user, $admin); my $template = 'goal.tt'; $template = 'erate_' . $template if ($tpb->purpose->erate eq 'Only'); $tpb->template_configure( -template_options => { INCLUDE_PATH => $TEMPLATE_PATH } ); $tpb->template_define( 'template', $template ); $r->print( $tpb->template_render( 'template', %record ) ); return Apache2::Const::OK; } # ----------------------------------------------------------------------------- sub _edit_goal { my ( $r, $req, $TEMPLATE_PATH, $user, $admin ) = @_; my %record = (); my ($error, $tpb, $section, $goal) = _parameter_checks( $r, $req, $TEMPLATE_PATH, 'edit_goal', 'tech_plan section goal' ); return $error if (defined $error); if (! $tpb->has_read_access($user, $admin)) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Authorization failed - user does not have access to plan (tpb12 edit_goal)", "Authorization failed -- please contact the system administrator.", ); } if ($tpb->status eq 'Deleted') { return errorToLogAndUser($r, $TEMPLATE_PATH, "Plan has been deleted (tpb12 edit_goal)", "The plan has been deleted." ); } my $status_column = $section->status_col_name; $record{'section'} = $section; $record{'goal'} = $goal; $record{'section_status'} = $tpb->$status_column if ($status_column); $record{'access_level'} = $tpb->access_level($user, $admin); $record{'run_mode'} = 'do_edit_goal'; _add_extra_data($tpb, $section, \%record, $user, $admin); my $template = $tpb->has_edit_access($user, $admin) ? 'goal.tt' : 'read_goal.tt'; $template = 'erate_' . $template if ($tpb->purpose->erate eq 'Only'); $tpb->template_configure( -template_options => { INCLUDE_PATH => $TEMPLATE_PATH } ); $tpb->template_define( 'template', $template ); $r->print( $tpb->template_render( 'template', %record ) ); return Apache2::Const::OK; } # ----------------------------------------------------------------------------- sub _do_edit_goal { my ( $r, $req, $TEMPLATE_PATH, $user, $admin ) = @_; my %record = (); my ($error, $tpb, $section, $goal) = _parameter_checks( $r, $req, $TEMPLATE_PATH, 'do_edit_goal', 'tech_plan section goal' ); return $error if (defined $error); if (! $tpb->has_edit_access($user, $admin)) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Authorization failed - user does not have edit access to plan (tpb12 do_edit_goal)", "Authorization failed -- please contact the system administrator.", ); } if ($tpb->status eq 'Deleted') { return errorToLogAndUser($r, $TEMPLATE_PATH, "Plan has been deleted (tpb12 do_edit_goal)", "The plan has been deleted." ); } $error = _update_record_from_form( $r, $req, $TEMPLATE_PATH, $user, 'do_edit_goal', $goal ); return $error if (defined $error); $goal->update(); _update_last_edit_and_status($tpb, $user); %record = (); my $status_column = $section->status_col_name; $record{'section'} = $section; $record{'goal'} = $goal; $record{'section_status'} = $tpb->$status_column if ($status_column); $record{'run_mode'} = 'do_edit_goal'; _add_extra_data($tpb, $section, \%record, $user, $admin); my $template = 'goal.tt'; $template = 'erate_' . $template if ($tpb->purpose->erate eq 'Only'); $tpb->template_configure( -template_options => { INCLUDE_PATH => $TEMPLATE_PATH } ); $tpb->template_define( 'template', $template ); $r->print( $tpb->template_render( 'template', %record ) ); return Apache2::Const::OK; } # ----------------------------------------------------------------------------- sub _add_objective { my ( $r, $req, $TEMPLATE_PATH, $user, $admin ) = @_; my %record = (); my ($error, $tpb, $section, $goal) = _parameter_checks( $r, $req, $TEMPLATE_PATH, 'add_objective', 'tech_plan section goal' ); return $error if (defined $error); if (! $tpb->has_edit_access($user, $admin)) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Authorization failed - user does not have edit access to plan (tpb12 add_objective)", "Authorization failed -- please contact the system administrator.", ); } if ($tpb->status eq 'Deleted') { return errorToLogAndUser($r, $TEMPLATE_PATH, "Plan has been deleted (tpb12 add_objective)", "The plan has been deleted." ); } my $status_column = $section->status_col_name; $record{'section'} = $section; $record{'goal'} = $goal; $record{'section_status'} = $tpb->$status_column if ($status_column); $record{'run_mode'} = 'do_add_objective'; _add_extra_data($tpb, $section, \%record, $user, $admin); my $template = 'objective.tt'; $template = 'erate_' . $template if ($tpb->purpose->erate eq 'Only'); $tpb->template_configure( -template_options => { INCLUDE_PATH => $TEMPLATE_PATH } ); $tpb->template_define( 'template', $template ); $r->print( $tpb->template_render( 'template', %record ) ); return Apache2::Const::OK; } # ----------------------------------------------------------------------------- sub _do_add_objective { my ( $r, $req, $TEMPLATE_PATH, $user, $admin ) = @_; my %record = (); my ($error, $tpb, $section, $goal) = _parameter_checks( $r, $req, $TEMPLATE_PATH, 'do_add_objective', 'tech_plan section goal' ); return $error if (defined $error); if (! $tpb->has_edit_access($user, $admin)) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Authorization failed - user does not have edit access to plan (tpb12 do_add_objective)", "Authorization failed -- please contact the system administrator.", ); } if ($tpb->status eq 'Deleted') { return errorToLogAndUser($r, $TEMPLATE_PATH, "Plan has been deleted (tpb12 do_add_objective)", "The plan has been deleted." ); } my $objective = 'ITO::TPB12::Objective'; ($error, $objective) = _create_record_from_form( $r, $req, $TEMPLATE_PATH, $user, '_do_add_objective', \%record, $objective ); return $error if (defined $error); $objective = $objective->create( \%record ); for (my $seq = 1; $seq <= $tpb->years; $seq++) { 'ITO::TPB12::Benchmark'->create( { 'tech_plan' => $tpb->id, 'section' => $section->id, 'objective' => $objective->id, 'status' => 'Not Started', 'seq' => $seq, 'last_update' => $user->id, } ); } _update_last_edit_and_status($tpb, $user); %record = (); my $status_column = $section->status_col_name; $record{'section'} = $section; $record{'goal'} = $goal; $record{'objective'} = $objective; $record{'section_status'} = $tpb->$status_column if ($status_column); $record{'run_mode'} = 'do_edit_objective'; _add_extra_data($tpb, $section, \%record, $user, $admin); my $template = 'objective.tt'; $template = 'erate_' . $template if ($tpb->purpose->erate eq 'Only'); $tpb->template_configure( -template_options => { INCLUDE_PATH => $TEMPLATE_PATH } ); $tpb->template_define( 'template', $template ); $r->print( $tpb->template_render( 'template', %record ) ); return Apache2::Const::OK; } # ----------------------------------------------------------------------------- sub _edit_objective { my ( $r, $req, $TEMPLATE_PATH, $user, $admin ) = @_; my %record = (); my ($error, $tpb, $section, $goal, $objective) = _parameter_checks( $r, $req, $TEMPLATE_PATH, 'edit_objective', 'tech_plan section goal objective' ); return $error if (defined $error); if (! $tpb->has_read_access($user, $admin)) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Authorization failed - user does not have access to plan (tpb12 edit_objective)", "Authorization failed -- please contact the system administrator.", ); } if ($tpb->status eq 'Deleted') { return errorToLogAndUser($r, $TEMPLATE_PATH, "Plan has been deleted (tpb12 edit_objective)", "The plan has been deleted." ); } my $status_column = $section->status_col_name; $record{'section'} = $section; $record{'goal'} = $goal; $record{'objective'} = $objective; $record{'section_status'} = $tpb->$status_column if ($status_column); $record{'access_level'} = $tpb->access_level($user, $admin); $record{'run_mode'} = 'do_edit_objective'; _add_extra_data($tpb, $section, \%record, $user, $admin); my $template = $tpb->has_edit_access($user, $admin) ? 'objective.tt' : 'read_objective.tt'; $template = 'erate_' . $template if ($tpb->purpose->erate eq 'Only'); $tpb->template_configure( -template_options => { INCLUDE_PATH => $TEMPLATE_PATH } ); $tpb->template_define( 'template', $template ); $r->print( $tpb->template_render( 'template', %record ) ); return Apache2::Const::OK; } # ----------------------------------------------------------------------------- sub _do_edit_objective { my ( $r, $req, $TEMPLATE_PATH, $user, $admin ) = @_; my %record = (); my ($error, $tpb, $section, $goal, $objective) = _parameter_checks( $r, $req, $TEMPLATE_PATH, 'do_edit_objective', 'tech_plan section goal objective' ); return $error if (defined $error); if (! $tpb->has_edit_access($user, $admin)) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Authorization failed - user does not have edit access to plan (tpb12 do_edit_objective)", "Authorization failed -- please contact the system administrator.", ); } if ($tpb->status eq 'Deleted') { return errorToLogAndUser($r, $TEMPLATE_PATH, "Plan has been deleted (tpb12 do_edit_objective)", "The plan has been deleted." ); } $error = _update_record_from_form( $r, $req, $TEMPLATE_PATH, $user, 'do_edit_objective', $objective ); return $error if (defined $error); $objective->update(); _update_last_edit_and_status($tpb, $user); %record = (); my $status_column = $section->status_col_name; $record{'section'} = $section; $record{'goal'} = $goal; $record{'objective'} = $objective; $record{'section_status'} = $tpb->$status_column if ($status_column); $record{'run_mode'} = 'do_edit_objective'; _add_extra_data($tpb, $section, \%record, $user, $admin); my $template = 'objective.tt'; $template = 'erate_' . $template if ($tpb->purpose->erate eq 'Only'); $tpb->template_configure( -template_options => { INCLUDE_PATH => $TEMPLATE_PATH } ); $tpb->template_define( 'template', $template ); $r->print( $tpb->template_render( 'template', %record ) ); return Apache2::Const::OK; } # ----------------------------------------------------------------------------- sub _get_file { my ( $r, $req, $TEMPLATE_PATH, $user, $admin ) = @_; my %record = (); my $upload = 'ITO::TPB12::Upload'; if ( ! $req->param('id') ) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Required field [id] is missing. (tpb12 get_file)", "There was an error. No id specified. Please notify the system administrator.", ); } $upload = $upload->retrieve( $req->param('id') ); if ( ! $upload ) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Required field [id] is invalid. (tpb12 get_file)", "There was an error. id is invalid. Please notify the system administrator.", ); } my $tpb = $upload->tech_plan; if (! $tpb->has_read_access($user, $admin)) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Authorization failed - user does not have access to plan (tpb12 get_file)", "Authorization failed -- please contact the system administrator.", ); } if ($tpb->status eq 'Deleted') { return errorToLogAndUser($r, $TEMPLATE_PATH, "Plan has been deleted (tpb12 get_file)", "The plan has been deleted." ); } $r->headers_out->{'Content-Disposition'} = "attachment;filename=" . $upload->filename; $r->content_type($upload->mime_type); # send data $r->print( $upload->contents ); return Apache2::Const::OK; } # ----------------------------------------------------------------------------- sub _change_all_section_status { my ( $r, $req, $TEMPLATE_PATH, $user, $admin ) = @_; my ($error, $tpb) = _parameter_checks( $r, $req, $TEMPLATE_PATH, 'change_all_section_status', 'tech_plan' ); return $error if (defined $error); if (! $tpb->has_edit_access($user, $admin)) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Authorization failed - user does not have access to plan (tpb12 _change_all_section_status)", "Authorization failed -- please contact the system administrator." ); } if ($tpb->status eq 'Deleted') { return errorToLogAndUser($r, $TEMPLATE_PATH, "Plan has been deleted (tpb12 _change_all_section_status)", "The plan you requested has been deleted." ); } my $new_status = $req->param('section_status'); if (! $new_status) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Reaquired parameter [section_status] is missing. (tpb12 _change_all_section_status)", "There was an error -- please contact the system administrator." ); } my @columns = ITO::TPB12::Section->plan_status_columns($tpb->purpose->erate eq 'Only'); foreach my $column ( @columns ) { $tpb->set($column => $new_status); } $tpb->set( 'last_update' => $user->id, 'last_edit_date' => UnixDate( 'today', '%Y-%m-%d %T' ), ); $tpb->update(); my %record = (); $record{'access_level'} = $tpb->access_level($user, $admin); my $template = 'plan_index.tt'; $template = 'erate_' . $template if ($tpb->purpose->erate eq 'Only'); @{ $record{'sections'} } = ITO::TPB12::Section->retrieve_all_sorted_by('seq'); $record{'user'} = $user; $record{'admin'} = $admin; $tpb->template_configure( -stash_cache => 0, -template_options => { INCLUDE_PATH => $TEMPLATE_PATH } ); $tpb->template_define( 'template', $template ); $r->print( $tpb->template_render( 'template', %record ) ); return Apache2::Const::OK; } # ----------------------------------------------------------------------------- # # utility subroutines # # ----------------------------------------------------------------------------- sub _parameter_checks { my ( $r, $req, $TEMPLATE_PATH, $sub_name, $parameters, $parameter_map) = @_; # first parameter of result is error code my @result_array = (undef); foreach my $parameter_name (split(/\s+/, $parameters)) { # default works for section, goal, objective, benchmark, activity # for the swap routines, we allow a :1 or :2 to follow the table name (my $table_name = $parameter_name) =~ s/\:[12]$//; my $table_object = 'ITO::TPB12::' . ucfirst $table_name; if ($table_name eq 'tech_plan') { $table_object = 'ITO::TPB12::TechPlan'; } if (defined $parameter_map && defined $$parameter_map{$parameter_name}) { $parameter_name = $$parameter_map{$parameter_name}; } my ($error, $object) = _get_record( $r, $req, $TEMPLATE_PATH, $sub_name, $parameter_name, $table_object ); return $error if (defined $error); push(@result_array, $object); } return @result_array; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # helper routine for _parameter_checks sub _get_record { my ( $r, $req, $TEMPLATE_PATH, $sub_name, $parameter_name, $object ) = @_; if ( ! $req->param($parameter_name) ) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Required field [$parameter_name] is missing. (tpb12 $sub_name)", "There was an error. No $parameter_name specified. Please notify the system administrator.", ); } $object = $object->retrieve( $req->param($parameter_name) ); if ( ! $object ) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Required field [$parameter_name] is invalid. (tpb12 $sub_name)", "There was an error. $parameter_name is invalid. Please notify the system administrator.", ); } return(undef, $object); } # ----------------------------------------------------------------------------- sub _create_record_from_form { my ( $r, $req, $TEMPLATE_PATH, $user, $sub_name, $record, $object, $transform ) = @_; # first parameter of result is error code my @required_fields = $object->columns('Required'); # make sure all required fields are there -- if not error out and return foreach my $field ( @required_fields ) { if ( (! defined $req->param($field)) || $req->param($field) =~ /^\s*$/) { return errorToLogAndUser($r, $TEMPLATE_PATH, "Required field [$field] is missing. (tpb12 $sub_name)", "There was an error. The required field [$field] is missing. Please contact the system administrator." ); } my $data = trim($req->param($field)); if (defined $transform && defined $$transform{$field}) { $data = nc($data) if ($$transform{$field} eq 'nc'); } $$record{$field} = $data; } # now do the optional fields my @optional_fields = $object->columns( 'Optional' ); foreach my $field ( @optional_fields ) { if ($field eq 'last_update') { $$record{$field} = $user->id; } elsif (defined $req->param($field)) { my $data = trim($req->param($field)); if (defined $transform && defined $$transform{$field}) { $data = nc($data) if ($$transform{$field} eq 'nc'); } $$record{$field} = $data; } } return(undef, $object); } # ----------------------------------------------------------------------------- sub _update_record_from_form { my ( $r, $req, $TEMPLATE_PATH, $user, $sub_name, $object, $transform ) = @_; # first parameter of result is error code my @required_fields = $object->columns('Required'); # make sure all required fields are there -- if not error out and return foreach my $field ( @required_fields ) { if ( (! defined $req->param($field)) || $req->param($field) =~ /^\s*$/) { $object->discard_changes; return errorToLogAndUser($r, $TEMPLATE_PATH, "Required field [$field] is missing. (tpb12 $sub_name)", "There was an error. The required field [$field] is missing. Please contact the system administrator." ); } my $data = trim($req->param($field)); if (defined $transform && defined $$transform{$field}) { $data = nc($data) if ($$transform{$field} eq 'nc'); } $object->set($field, $data); } # now do the optional fields my @optional_fields = $object->columns( 'Optional' ); foreach my $field ( @optional_fields ) { if ($field eq 'last_update') { $object->set($field, $user->id); } elsif (defined $req->param($field)) { my $data = trim($req->param($field)); if (defined $transform && defined $$transform{$field}) { $data = nc($data) if ($$transform{$field} eq 'nc'); } $object->set($field, $data); } } return undef; } # ----------------------------------------------------------------------------- # add additional data to record based on the section sub _add_extra_data { my ($tpb, $section, $record, $user, $admin) = @_; $$record{'user'} = $user; $$record{'admin'} = $admin; # activities # this is for activities which are not associated with an objective if ($section->extra_data =~ /\bactivities\b/i) { @{ $$record{'activities'} } = ITO::TPB12::Activity->search( 'tech_plan' => $tpb->id, 'section' => $section->id, { 'order_by' => 'seq', } ); } # benchmarks if ($section->extra_data =~ /\bbenchmarks\b/i) { @{ $$record{'benchmarks'} } = ITO::TPB12::Benchmark->search( 'tech_plan' => $tpb->id, 'section' => $section->id, { 'order_by' => 'seq' } ); } # budget categories if ($section->extra_data =~ /\bcategories\b/i) { @{ $$record{'categories'} } = ITO::TPB12::BudgetCategory->retrieve_all_sorted_by('seq');; } # budget items if ($section->extra_data =~ /\bbudget_items\b/i) { %{ $$record{'budget_items'} } = $tpb->ordered_budget_items(); } # goals if ($section->extra_data =~ /\bgoals\b/i) { @{ $$record{'goals'} } = ITO::TPB12::Goal->search( 'tech_plan' => $tpb->id, 'section' => $section->id, { 'order_by' => 'seq', } ); } # notes if ($section->extra_data =~ /\bnotes\b/i) { @{ $$record{'notes'} } = ITO::TPB12::Note->search( 'tech_plan' => $tpb->id, 'section' => $section->id, { 'order_by' => 'seq', } ); my $count = scalar @{ $$record{'notes'} }; if ($count) { $$record{'notes_count'} = " ($count)"; } else { $$record{'notes_count'} = ''; } } # positions if ($section->extra_data =~ /\bpositions\b/i) { @{ $$record{'positions'} } = ITO::TPB12::Position->retrieve_all_sorted_by('short_name'); } # purposes if ($section->extra_data =~ /\bpurposes\b/i) { @{ $$record{'purposes'} } = ITO::TPB12::Purpose->search_where( { 'status' => 'Active', }, { 'order_by' => 'seq' } ); } # stakeholders if ($section->extra_data =~ /\bstakeholders\b/i) { @{ $$record{'stakeholders'} } = $tpb->ordered_stakeholders(); } # textarea - used when a section has only one text area # we create the text area the first time the section is accessed if ($section->extra_data =~ /\btextarea\b/i) { my $textarea = ITO::TPB12::TextArea->find_or_create( { 'tech_plan' => $tpb->id, 'section' => $section->id, } ); %{ $$record{'textarea'} } = ( 'id' => $textarea->id, 'text' => $textarea->text, ); } # textareas - used when a section has multiple text areas (e.g., 5a) # we create the text areas the first time the section is accessed if ($section->extra_data =~ /\btextareas\b/i) { my @subsections = ITO::TPB12::Section->search( 'parent' => $section->id, { 'order_by' => 'seq' } ); @{ $$record{'textareas'} } = (); foreach my $subsection (@subsections) { # if the subsection has an erate(\d) flag, it is conditional on # this being an E-rate plan, and on the plan duration being >= # the number next if ($subsection->excluded_erate_area($tpb)); my $textarea = ITO::TPB12::TextArea->find_or_create( { 'tech_plan' => $tpb->id, 'section' => $subsection->id, } ); push @{ $$record{'textareas'} }, { 'id' => $textarea->id, 'level' => $subsection->level, 'title' => $subsection->title, 'text' => $textarea->text, } } } # uploads if ($section->extra_data =~ /\uploads\b/i) { my @uploads = $tpb->uploads; @{ $$record{'uploads'} } = sort {$a->filename cmp $b->filename} @uploads; } } # ----------------------------------------------------------------------------- # _update_last_edit_and_status ( tech_plan, user, section ) # called when creating or editing section data. performs two functions: # for both create and edit, sets the tech plan's last_edit_date # # for create, it is called with the section id; if the section has a status # column, and the status is 'Not Started', sets the status to 'Draft' sub _update_last_edit_and_status { my ($tpb, $user, $section) = @_; if (defined $section) { my $status_column = $section->status_col_name; if ($status_column) { my $status = $tpb->$status_column; if ($status eq 'Not Started') { $tpb->set($status_column, 'Draft'); } } } $tpb->set( 'last_update' => $user->id, 'last_edit_date' => UnixDate( 'today', '%Y-%m-%d %T' ), ); $tpb->update(); } # ----------------------------------------------------------------------------- # _manage_benchmarks ( , old duration, tech plan) # adds or removes benchmarks if the plan duration was changed sub _manage_benchmarks { my ($r, $req, $TEMPLATE_PATH, $user, $old_duration, $tpb ) = @_; my $duration = $tpb->years; my $benchmark = 'ITO::TPB12::Benchmark'; if ($old_duration == 0) { # if the old duration was 0, all we have to do is create benchmarks # for sections identified in the section table my @sections = ITO::TPB12::Section->search_like('flags' => '%populate_benchmarks%'); foreach my $section (@sections) { for (my $seq = 1; $seq <= $duration; $seq++) { $benchmark->create( { 'tech_plan' => $tpb->id, 'section' => $section->id, 'objective' => 0, 'status' => 'Not Started', 'seq' => $seq, 'last_update' => $user->id, } ); } } } elsif ($old_duration > $duration) { # delete the obsoleted benchmarks $benchmark->search_where( { 'tech_plan' => $tpb->id, 'seq' => { '>', $duration }, } )->delete_all; } else { # add benchmarks my @parents = $benchmark->distinct_parents($tpb->id); foreach my $parent(@parents) { for (my $seq = $old_duration + 1; $seq <= $duration; $seq++) { $benchmark->create( { 'tech_plan' => $tpb->id, 'section' => $parent->section, 'objective' => $parent->objective, 'status' => 'Not Started', 'seq' => $seq, 'last_update' => $user->id, } ); } } } return $duration; } # ----------------------------------------------------------------------------- # _clone_records ( reference to %COPY_CONTROL entry, # reference to hash of new PK values for ancestor records, # object whose children are to be cloned # ) # this subroutine is used to duplicate a plan. _copy_plan copies the tech_plan # record, initializes the FK substitutions hash with the id of the new plan, # and calls _clone_records with the original plan object. # _clone_records then does the following: # iterates through all the 'children' entries in the control entry # for each child, uses the 'accessor' method of the parent to obtain the # child records, and then duplicates the record. 'substitute' contains a # hash of the FK columns in the child which need to be updated with the id's # of the new ancestor records; the values are keys to %substitute_keys # recursively calls itself to clone the children for each new record sub _clone_records { my ($control, $substitute_keys, $parent) = @_; foreach my $child_control (@{$$control{'children'}}) { # pick up the control entries for this child my $accessor = $$child_control{'accessor'}; my $substitute = $$child_control{'substitute'}; my $key = $$child_control{'key'}; my $special = $$child_control{'special'}; my $conditional = $$child_control{'conditional'}; if ($conditional) { my $conditional_flag = $$substitute_keys{$conditional}; if (! defined $conditional_flag) { next; } } # get all the child records for the (original) parent my @children = $parent->$accessor; # copy each of the child records and recursively copy their children foreach my $child (@children) { # add the current PK value of the record into the substitute_keys # hash. this lets us clone records with two PK columns, where one # is a FK to an ancestor which has been cloned already, and one # is a FK to a record which doesn't change. (TechPlanToUser) my $old_key = $child->$key; $$substitute_keys{"${accessor}:${key}"} = $old_key; # now build the substitutions which will be used for this record my %substitutions = (); foreach my $skey (keys %$substitute) { my $value = $$substitute{$skey}; if (defined $value) { # use a PK value from %substitute_keys $substitutions{$skey} = $$substitute_keys{$value}; } else { # null the column so that the default is used $substitutions{$skey} = undef; } } # if this is an textarea record, we need to fix the image tags if ($special && $special eq 'textarea') { my $text = $child->text; if ($text =~ /]*src="\/tpb12\/tpb12\.pl\?run_mode=get_file[^"]+id=)(\d+)/_fix_images($1, $2, $substitute_keys)/eg; $substitutions{'text'} = $text; } } my $new = $child->copy(\%substitutions); # save the new PK in %substitute_keys so children can use it my $new_key = $new->$key; $$substitute_keys{"${accessor}:${key}"} = $new_key; # if this is an upload record, also create lookup for the id if ($special && $special eq 'upload') { $$substitute_keys{"${accessor}:${key}:${old_key}"} = $new_key; } # clone children of this record _clone_records($child_control, $substitute_keys, $child); } } } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # _fix_images ( image tag up to id, id, substitute keys hash ) # used in _clone_records s/.../_fix_images(...)/ to change the ids the # text area image tags sub _fix_images { my ($prefix, $id, $substitute_keys) = @_; my $new_id = $$substitute_keys{"uploads:id:$id"}; return $prefix . $new_id; } # ----------------------------------------------------------------------------- sub tech_plan_compare { my ($hash, $tech_plan_1, $tech_plan_2) = @_; my $id_1 = $tech_plan_1->id; my $id_2 = $tech_plan_2->id; if (! defined $$hash{$id_1}) { $$hash{$id_1}{district} = $tech_plan_1->district ? $tech_plan_1->district->name : ''; $$hash{$id_1}{school} = $tech_plan_1->school ? $tech_plan_1->school->name : ''; $$hash{$id_1}{title} = lc $tech_plan_1->title; } if (! defined $$hash{$id_2}) { $$hash{$id_2}{district} = $tech_plan_2->district ? $tech_plan_2->district->name : ''; $$hash{$id_2}{school} = $tech_plan_2->school ? $tech_plan_2->school->name : ''; $$hash{$id_2}{title} = lc $tech_plan_2->title; } return ( $$hash{$id_1}{district} cmp $$hash{$id_2}{district} || $$hash{$id_1}{school} cmp $$hash{$id_2}{school} || $$hash{$id_1}{title} cmp $$hash{$id_2}{title} ); } # -----------------------------------------------------------------------------