#!/usr/bin/perl require v5.6.1; # before anything else, the script needs to find out its own name # # some servers (notably IIS on windows) don't set the cwd to the script's # directory before executing it. So we get that information # from $0 (the full name & path of the script). BEGIN{($_=$0)=~s![\\/][^\\/]+$!!;push@INC,$_} $name = $0; $name =~ s/.+\/?.+\///; # for unix $name =~ s/.+\\.+\\//; # for windows $path = $0; $path =~ s/(.+\/).+/$1/g; # for unix $path =~ s/(.+\\).+/$1/g; # for windows # The "use Cwd" method would be nice, but it doesn't work with # some versions of IIS/ActivePerl #use Cwd; #$path = cwd; if ($path ne "") { chdir $path; push @INC,$path; } # finished discovering name #use Data::Dumper; # some global variables (more further down) local $plans_version = "8.1.4"; # version local $debug_info; local %options; local $fatal_error = 0; # fatal errors cause plans to abort and print an error message to the browser local $error_info = ""; local $html_output; local $script_url = ""; local $messages = ""; # formatted in plain text with newlines. Converted to html at display time. local $template_html; local $local_template_file = 0; # tells whether the template was loaded via a filesystem open or through a http request. local $event_details_template; local $list_item_template; local $calendar_item_template; local $upcoming_item_template; local %calendars; local %current_calendar; local %latest_calendar; local %new_calendars; local $normalized_timezone = 0; local $normalized_timezone_pending_events = 0; # used when adding new entries local $max_cal_id = 0; local $max_event_id = 0; local $max_series_id = 0; local $max_user_id = 0; local $max_action_id = 0; # used to protect against refreshes local $latest_cal_id = 0; local $latest_event_id = 0; local $latest_new_cal_id = 0; local $latest_new_event_id = 0; local $session; local $json = new JSON::PP; local %users; my $profile; local $logged_in = 0; local $logged_in_as_root = 0; local $logged_in_as_current_cal_user = 0; local $logged_in_as_current_cal_admin = 0; local $lg_name = ""; local $lg_password = ""; local %events; local %new_events; local @pending_events_to_display; local %text; local %cookie_parms; local $cookie_text = ""; local $cookie_header_text = ""; local $max_remote_event_id = 0; local $options{default_template_path} = ""; local $theme_url = ""; local $options{choose_themes} = ""; local $graphics_url = ""; local $ical_export_url = ""; local $icons_url = ""; local $input_cal_id_valid = 0; local $options{right_click_menus_enabled} = 0; local %cal_options; local $rightnow; local @months; local @months_abv; local @day_names; local $loaded_all_events; # flag used to avoid calling load_events("all") twice # not needed for calendars (we always load all calendars) local @disabled_tabs; # check for required modules. require "includes.pl"; $module_found=0; foreach $temp_path (@INC) { if (-e "$temp_path/JSON") { $module_found=1;} } if ($module_found == 0) { $fatal_error=1; $error_info .= "unable to locate required module JSON!\n"; } else { require "JSON/PP58.pm"; use JSON::PP; } if ($fatal_error == 1) { # print error and bail out &fatal_error(); } # anonymous events only work when sessions are turned on $options{'anonymous_events'} = $options{'anonymous_events'} && $options{'sessions'}; if (defined $options{language_files}) { my @language_files = split(',', $options{language_files}); # create a javascript file with language strings open (FH, "$options{default_theme_path}/$options{js_language_file}") || {$debug_info.= "unable to open file $options{default_theme_path}/$options{js_language_file}\n"}; flock FH,2; my $first_lang_line=; close FH; if ($options{generate_js_lang} eq "1" || $first_lang_line !~ /$plans_version/) { my $lang_string = ""; $lang_string .= "//$plans_version\n"; $lang_string .= "var plans_lang = {};\n"; # generate %lang keys foreach $lang_key (keys %lang) { if (ref $lang{$lang_key} eq "ARRAY") { $lang_string .= "plans_lang['$lang_key']=["; my $first = 1; foreach $key (@{$lang{$lang_key}}) { if (!$first) {$lang_string .= ',';} if ($first) {$first = 0;} my $lang_val = &js_string($key); $lang_string .= "'$lang_val'"; } $lang_string .= "];\n"; } else { my $lang_val = &js_string($lang{$lang_key}); $lang_string .= "plans_lang['$lang_key']='$lang_val';\n" } } open (FH, ">$options{default_theme_path}/$options{js_language_file}") || {$debug_info .= "unable to open file $options{default_theme_path}/$options{js_language_file} for writing!\n"}; flock FH,2; print FH $lang_string; close FH; } } else { $fatal_error=1; $error_info .= "No language files defined in plans.config!\n"; } if ($fatal_error == 1) { # print error and bail out &fatal_error(); } # init cgi stuff $q = new CGI; if ($options{calendar_url} ne "") { $script_url = $options{calendar_url}; } else { $script_url = $q->url(-path_info>=1); } $script_url =~ /(.*)\//; # remove trailing / and all text after $script_url = $1; # remove trailing / and all text after %cookie_parms = %{ &extract_cookie_parms() }; my $cookie_path = $q->url( -absolute => 1 ); $cookie_path =~ s/$name$//; $cookie_path =~ s/admin\/?$//; # This is better than using just '/' # check if data files or tables are present &check_data(); # fatal error? Print error and bail out if ($fatal_error == 1) { &fatal_error();} if ($theme_url eq "") { # not defined in config file $theme_url = "$script_url/theme"; } if ($options{choose_themes}) { $chosen_url = $q->param('theme_url'); $chosen_url = $cookie_parms{'theme_url'} if ($chosen_url eq ""); $theme_url = $chosen_url if ($chosen_url ne ""); } $graphics_url ="$theme_url/graphics"; # where misc. graphics are $ical_export_url ="$theme_url/ical"; # where icalendar .ics files are placed $ical_export_url =~ s/http:\/\//$options{ical_prefix}/; # replace with custom prefix $icons_url = "$theme_url/icons"; # where icons are $css_path = "$theme_url/plans.css"; # css file # globals from http parameters my $active_tab = $q->param('active_tab') + 0; # +0 ensures numericity $active_tab = 0 if ($active_tab > scalar @{$lang{tab_text}} - 1); my $api_command = $q->param('api_command'); my $add_edit_cal_action = $q->param('add_edit_cal_action'); $add_edit_cal_action = "" if (!&contains(["add", "edit", "view_pending"],$add_edit_cal_action)); # validate my $add_edit_event = $q->param('add_edit_event'); $add_edit_event = "" if (!&contains(["add", "edit"],$add_edit_event)); # validate local $current_event_id = $q->param('evt_id'); $current_event_id = "" if ($current_event_id !~ /^R?\d+$/); # validate local $pending_event_id = $q->param('pending_event_id'); $pending_event_id = "" if ($pending_event_id !~ /^R?\d+$/); # validate local $cal_start_month = $q->param('cal_start_month') + 0; # +0 ensures numericity local $cal_start_year = $q->param('cal_start_year') + 0; # +0 ensures numericity local $cal_num_months = $q->param('cal_num_months') + 0; # +0 ensures numericity # if view parameters not supplied in http request, check cookie $cal_start_month = $cookie_parms{'cal_start_month'} if ($q->param('cal_start_month') eq ""); $cal_start_year = $cookie_parms{'cal_start_year'} + 0 if ($cal_start_year == 0); $cal_num_months = $cookie_parms{'cal_num_months'} + 0 if ($cal_num_months == 0); my $special_action = $q->param('special_action'); # needs no validation - never used in output local $display_type = $q->param('display_type') + 0; # +0 ensures numericity $display_type = $cookie_parms{'display_type'} if ($q->param('display_type') eq ""); $messages = $q->param('messages') if ($q->param('messages') ne ""); # other globals my $event_start_date; my $event_start_timestamp; my $event_days; my $start_mday; my $start_mon; my $start_year; my @timestamp_array; my $prev_month_link = ""; my $next_month_link = ""; # load calendar data &load_calendars(); &load_users(); &load_actions(); local $current_cal_id = 0; if ($q->param('cal_id') eq "") { $current_cal_id = $cookie_parms{'current_cal_id'} if ($current_cal_id == 0); } else { $current_cal_id = $q->param('cal_id'); } $current_cal_id += 0; # +0 ensures numericity # if calendar id not supplied, but evt_id is supplied (like when viewing an event) use that event's calendar as the current calendar #if ($current_event_id ne "") #{ # &load_event($current_event_id); # # my %temp_current_event = %{$events{$current_event_id}}; # if ($current_cal_id eq "") # { # $current_cal_id = $temp_current_event{cal_ids}[0]; # } #} foreach $cal_id (keys %calendars) { if ($cal_id eq $current_cal_id) { $input_cal_id_valid = 1;} } if ($current_cal_id eq "") { $input_cal_id_valid = 0;} if ($current_cal_id =~ /\D/) { $input_cal_id_valid = 0;} $current_cal_id = 0 if ($current_event_id eq "" && !$input_cal_id_valid); # make all calendars selectable by default foreach $cal_id (keys %calendars) { $default_cal{selectable_calendars}{$cal_id} = 1;} %current_calendar = %{$calendars{$current_cal_id}}; # time-related globals $rightnow = time() + 3600 * $current_calendar{gmtime_diff}; @rightnow_array = gmtime $rightnow; $rightnow_year = $rightnow_array[5]+1900; $rightnow_month = $rightnow_array[4]; $rightnow_mday = $rightnow_array[3]; $next_year = $rightnow_year+1; $rightnow_description = formatted_time($rightnow, "hh:mm:ss mn md yyyy"); @weekday_sequence = @day_names; # session stuff if ($options{sessions} eq "1") { $lg_name = $current_cal_id; $lg_password = $q->param('cal_password'); &delete_old_sessions(1); # in days my $current_session_id = $q->cookie("plans_sid") || undef; $session = new CGI::Session(undef, $current_session_id, {Directory=>$options{sessions_directory}}); $session->expire("+1d"); #$debug_info .= "current_session_id: $current_session_id\n"; # log out? if ($q->param('logout') eq "1") { $session->delete(); $logged_in = 0; $cookie_text .= "Set-Cookie; plans_sid=deleted; path=$cookie_path;\n"; } else { # try to match session with user id. (If this fails, it's not really a session.) my $results = &init_session($q, $session); $profile = $session->param("~profile"); if (defined $profile->{calendar_permissions}) { $logged_in = 1; $cookie_text .= "Set-Cookie: plans_sid=".$session->id."; path=$cookie_path;\n"; } } } # $debug_info .= "cal password: " . $q->param('cal_password') . "\n"; # $debug_info .= "encrypted cal password: " . crypt($q->param('cal_password'), $options{salt}) . "\n"; # $debug_info .= "root cal password: " . $calendars{0}{password} . "\n"; if ($options{sessions} eq "1") { $logged_in_as_root = ($profile->{calendar_permissions}->{0}->{admin} eq "1") ? 1:0; $logged_in_as_current_cal_user = ($profile->{calendar_permissions}->{$current_cal_id}->{user} ne "") ? 1:0; $logged_in_as_current_cal_admin = ($profile->{calendar_permissions}->{$current_cal_id}->{admin} ne "") ? 1:0; } elsif ($q->param('cal_password') ne "") { $logged_in_as_root = ($calendars{0}{password} eq crypt($q->param('cal_password'), $options{salt})) ? 1:0; $logged_in_as_current_cal_admin = ($current_calendar{password} eq crypt($q->param('cal_password'), $options{salt})) ? 1:0; foreach $user_id (keys %users) { my %user = %{$users{$user_id}}; my %user_calendars = %{$user{calendars}}; foreach $user_cal_id (keys %user_calendars) { if ($user_cal_id eq $current_cal_id && $user{calendars}{$user_cal_id}{edit_events} eq "1" && $user{password} eq crypt($q->param('cal_password'), $options{salt})) { $logged_in_as_current_cal_user = 1; last; } last if ($logged_in_as_current_cal_user == 1); } last if ($logged_in_as_current_cal_user == 1); } } $logged_in_as_current_cal_user = 0 if (!$options{users}) ; #$debug_info .= "init_session results: $results\n"; #$debug_info .= "logged-in: ".$session->param("~logged-in")."\n"; #$debug_info .= "session id: ".$session->id."\n"; #$debug_info .= "profile user_id: ".$profile->{calendar_permissions}->{$current_cal_id}."\n"; #$debug_info .= "options{sessions}: $options{sessions}\n"; #$debug_info .= "logged_in_as_root: $logged_in_as_root\n"; #$debug_info .= "logged_in_as_current_cal_user: $logged_in_as_current_cal_user\n"; #$debug_info .= "logged_in_as_current_cal_admin: $logged_in_as_current_cal_admin\n"; #$debug_info .= "current_calendar{password}: $current_calendar{password}\n"; #$debug_info .= ($profile->{calendar_permissions}->{$current_cal_id}->{admin})."\n"; # custom stylesheet? if ($current_calendar{custom_stylesheet} ne "") { $css_path = "http://$current_calendar{custom_stylesheet}"; } # if this is a custom calendar request, shoehorn the request parameters in if ($q->param('custom_calendar') eq "1") { $current_cal_id = $q->param('custom_calendar_calendar') + 0; @custom_calendar_backgound_calendars = $q->param('custom_calendar_background_calendars'); foreach $local_background_calendar (keys %{$calendars{$current_cal_id}{local_background_calendars}}) { delete $calendars{$current_cal_id}{local_background_calendars}{$local_background_calendar};} foreach $local_background_calendar (@custom_calendar_backgound_calendars) { $calendars{$current_cal_id}{local_background_calendars}{$local_background_calendar} = 1;} %current_calendar = %{$calendars{$current_cal_id}}; } # make sure we can select the current calendar #$current_calendar{selectable_calendars}{$current_cal_id} = 1; # rotate weekday_sequence by the offset defined in the week start day. for ($l1=0;$l1 < $current_calendar{week_start_day};$l1++) { push @weekday_sequence, (shift @weekday_sequence);} # load background_colors my @temp_lines = split ("\n", $event_background_colors); foreach $temp_line (@temp_lines) { next if ($temp_line !~ /\w/); # skip any blank lines $temp_line =~ s/^\s+//; my ($hex_color, $hex_color_title) = split (/,*\s+/, $temp_line, 2); $hex_color_title = " " if ($hex_color_title eq ""); push @event_bgcolors, {color => $hex_color, title => $hex_color_title}; } #evaluate browser type and version $_ = $ENV{HTTP_USER_AGENT}; if (/Mozilla/) { if (/Opera.([0-9\.]+)/) { $browser_type = 'Opera'; $browser_version=$1;} elsif (/MSIE.([0-9.]+)/) { $browser_type = 'IE'; $browser_version = $1;} elsif (/Mozilla\/([0-9\.]+)/) {$browser_type = 'Mozilla'; $browser_version=$1; if (($browser_version<5) || (/Netscape/)) {$browser_type = "Netscape";} } if (/\)[^0-9.]+[0-9]*[\/\ ]([0-9.]+)/) {$browser_version=$1;} } elsif (/(\w+)\/([0-9\.]+)/) {$browser_type = $1; $browser_version = $2} #evaluate, transform, tweak, adjust, modify input values #$debug_info .= "browser type: $browser_type
"; #if no month is selected, use the current month if ($cal_start_month eq "" ) { $cal_start_month = $rightnow_month; #$cal_start_month = 2; } #if the input year is out of range use the current year if (($cal_start_year+0) < 1902 || ($cal_start_year+0)> 2037) { $cal_start_year = $rightnow_year; } $cal_num_months = $current_calendar{default_number_of_months} if ($cal_num_months < 1); $cal_num_months = $current_calendar{default_number_of_months} if ($cal_num_months > $current_calendar{max_number_of_months}); $cal_num_months = 1 if ($cal_num_months > $current_calendar{max_number_of_months}); $cal_num_months = 1 if ($cal_num_months == 0); #calculate calendar end month and year $cal_end_month = $cal_start_month; $cal_end_year = $cal_start_year; for ($l1=1;$l1<$cal_num_months;$l1++) { $cal_end_month++; if ($cal_end_month == 12) { $cal_end_month=0; $cal_end_year++; } } #check to make sure num_months+cal_start_date doesn't go out of bounds if ($cal_end_year < 1902 || $cal_end_year> 2037) { $cal_end_year = $cal_start_year; $cal_end_month = $cal_start_month; $cal_num_months = 1; } # time window for loading events my $cal_start_timestamp = timegm(0,0,0,1,$cal_start_month,$cal_start_year) - 2592000; my $cal_end_timestamp = timegm(0,0,0,1,$cal_end_month,$cal_end_year) + 5184000; if ($q->param('cal_start_timestamp') ne "" && $q->param('cal_start_timestamp') !~ /\D/) { $cal_start_timestamp = $q->param('cal_start_timestamp');} if ($q->param('cal_end_timestamp') ne "" && $q->param('cal_end_timestamp') !~ /\D/) { $cal_end_timestamp = $q->param('cal_end_timestamp');} #$debug_info .="start: $cal_start_timestamp\nend: $cal_end_timestamp\nrightnow: $rightnow\n"; # load event data, for main calendar and its background calendars my @temp_calendars = ($current_cal_id); foreach $local_background_calendar (keys %{$current_calendar{local_background_calendars}}) { push @temp_calendars, $local_background_calendar; } my $initial_load_events = 1; $initial_load_events = 0 if ($q->param('get_upcoming_events') eq "1"); &load_events($cal_start_timestamp, $cal_end_timestamp, \@temp_calendars) if ($initial_load_events == 1); if ($current_event_id ne "") { &load_event($current_event_id); } # load events from remote background calendars if (scalar keys %{$current_calendar{remote_background_calendars}} > 0) { $remote_calendars_status=""; my $temp = scalar keys %{$current_calendar{remote_background_calendars}}; foreach $remote_calendar_id (keys %{$current_calendar{remote_background_calendars}}) { # pull in remote calendar name my $remote_calendar_url = $current_calendar{remote_background_calendars}{$remote_calendar_id}{url}; $remote_calendar_complete_url = $remote_calendar_url; #$debug_info .= "remote calendar: $remote_calendar_complete_url\n"; $remote_calendar_complete_url .= "?remote_calendar_request=1&cal_id=$current_calendar{remote_background_calendars}{$remote_calendar_id}{remote_id}&cal_start_year=$cal_start_year&cal_start_month=$cal_start_month&num_months=$cal_num_months"; #$debug_info .= "remote calendar url: $remote_calendar_complete_url\n"; my $xml_results = &get_remote_file($remote_calendar_complete_url); if ($xml_results =~ //) { $xml_results =~ s//>/g; #$debug_info .= "Error fetching remote calendar: $xml_results\n"; } else { my %remote_calendar = %{&xml2hash($xml_results)}; my $remote_cal_title = $remote_calendar{'xml'}{calendar}{title}; my $remote_cal_gmtime_diff = $remote_calendar{'xml'}{calendar}{gmtime_diff}; #$debug_info .= "remote_cal_gmtime_diff: $remote_cal_gmtime_diff\n"; #my $temp = $xml_results; #$temp=~ s/>/>/g; #$temp=~ s/ 1) { $prev_string = $lang{previous_months}; $prev_string =~ s/###num###/$cal_num_months/; } else { $prev_string = $lang{previous_month}; } # calculate next X months range. my $next_cal_start_month = $cal_start_month + $cal_num_months; my $next_cal_start_year = $cal_start_year; if ($next_cal_start_month > 11) { $next_cal_start_year = $cal_start_year + int(abs($cal_num_months + $cal_start_month) / 12); $next_cal_start_month = abs($cal_start_month + $cal_num_months) % 12; } # singular or plural? if ($cal_num_months > 1) { $next_string = $lang{next_months}; $next_string =~ s/###num###/$cal_num_months/; } else { $next_string = $lang{next_month}; } if ($q->param('diagnostic_mode') eq "1") { my $diagnostic_results = &diagnostic_info; $html_output = < Diagnostic mode

Plans Diagnostic information

$diagnostic_results

Debug info:

$debug_info
p1 print $html_output; exit(0); } # dispatch all actions if ($api_command eq "delete_event" || $api_command eq "add_update_event" ) { &api_add_delete_events(); exit(0); } if ($api_command eq "delete_calendar" || $api_command eq "add_update_calendar" ) { &api_add_delete_calendar(); exit(0); } if ($api_command eq "approve_delete_pending_calendars" ) { &api_approve_delete_pending_calendars(); exit(0); } if ($api_command eq 'add_edit_user') { &add_edit_user(); exit(0); } if ($api_command eq 'detect_remote_calendars' ) { &detect_remote_calendars(); exit(0); } if ($api_command eq 'add_ical') { &add_new_ical(); exit(0); } if ($api_command eq 'js_login') { &js_login(); exit(0); } if ($api_command eq 'preview_date') { &load_templates(); &preview_date(); exit(0); } if ($api_command eq 'set_email_reminder') { &load_templates(); &set_email_reminder(); exit(0); } if ($api_command eq 'manage_pending_events') { &manage_pending_events(); exit(0); } if ($q->param('remote_calendar_request') eq "1") { &remote_calendar_request(); exit(0); } if ($q->param('export_calendar') eq "1") { if ($q->param('export_type') eq "ascii_text") { &ascii_text_cal($cal_start_month, $cal_start_year, $cal_end_month, $cal_end_year); exit(0); } elsif ($q->param('export_type') eq "csv_file") { &csv_file($cal_start_month, $cal_start_year, $cal_end_month, $cal_end_year); exit(0); } elsif ($q->param('export_type') eq "csv_file_palm") { &csv_file_palm($cal_start_month, $cal_start_year, $cal_end_month, $cal_end_year); exit(0); } elsif ($q->param('export_type') eq "vcalendar") { &vcalendar_export_cal($cal_start_month, $cal_start_year, $cal_end_month, $cal_end_year); exit(0); } elsif ($q->param('export_type') eq "icalendar") { my $html_output =<param('export_event') eq "1") { if ($q->param('export_type') eq "ascii_text") { &ascii_text_event(); exit(0); } elsif ($q->param('export_type') eq "icalendar") { &icalendar_export_event(); exit(0); } elsif ($q->param('export_type') eq "vcalendar") { &vcalendar_export_event(); exit(0); } } elsif ($q->param('get_upcoming_events') eq "1") { &get_upcoming_events(); exit(0); } elsif ($q->param('view_event') eq "1") { &load_templates(); &view_event(); exit(0); } elsif ($q->param('view_pending_event') eq "1") { &load_templates(); my %pending_event = %{$new_events{$pending_event_id}}; &view_pending_event(\%pending_event); exit(0); } &load_templates(); # ssi-style includes in the template if ($local_template_file) { my $new_html = $template_html; $template_html =~ s/###include\s+(.+)###/&load_file($1)/ge; #while ($new_html =~ s/###include\s+(.+)###//g) if(0) { my $include_file=$1; if (-e $include_file) { open (FH, "$include_file") || ($debug_info .="
unable to open include file $include_file for reading
"); flock FH,2; my @include_lines=; close FH; $include_html = join "", @include_lines; } $template_html =~ s/###include\s+(.+)###/$include_html/; } } if($options{choose_themes}) { my $theme_file="choose_theme.html"; my $theme_html=""; if (-e $theme_file) { open (FH, "$theme_file") || ($debug_info .="
unable to open theme file $theme_file for reading
"); flock FH,2; my @theme_lines=; close FH; $theme_html = join "", @theme_lines; } $template_html =~ s/###choose theme###/$theme_html/; } else { $template_html =~ s/###choose theme###//; } my %new_cookie_parms = ( "cal_start_month" => $cal_start_month, "cal_start_year" => $cal_start_year, "cal_num_months" => $cal_num_months, "current_cal_id" => $current_cal_id, "display_type" => $display_type ); my $view_cookie = &encode( encode_json( \%new_cookie_parms ) ); $cookie_text .= "Set-Cookie: plans_view=$view_cookie; path=$cookie_path; expires=Thu, 31-Dec-2099 00:00:00 GMT\n"; $cookie_header_text = $cookie_text; $html_output .=< $b} keys %calendars) { $json_calendars .= "jQuery.planscalendar.calendars.push(" . calendar2json( $calendars{$calendar_id} ) . ");\n"; } $json_pending_calendars = &calendars2json( \%new_calendars, 1 ); @temp = &assemble_icon_menus( $event_icons_menu ); $json_icons = encode_json( \@temp ); $json_event_background_colors = encode_json( \@event_bgcolors); $insert_text =< p1 chomp $insert_text; $html_output =~ s/###javascript stuff###/$insert_text/; # insert library javascript before all other javascript. $temp = &get_js_includes( $theme_url ); $html_output =~ s/( p1 $html_output =~ s/###css stuff###/$css_text/g; $html_output =~ s/###css file###/$css_path/g; # holdover for pre-8.0 themes # tab menu stuff $insert_text =< p1 #lay out the actual menu tabs for ($l1=0;$l1 $lang{tab_text}[$l1] p1 } chomp $insert_text; if ($q->param('custom_calendar') == 1) { $html_output =~ s/###tab menu stuff###//g; } else { $html_output =~ s/###tab menu stuff###/$insert_text/g; } $insert_text =""; $event_details_js_template = ""; foreach $event_id (keys %events) { $event_details_js_template = generate_event_details_template($events{$event_id}); last; # we only need to do this once } #invisible html for context menu $insert_text .=< $event_details_js_template p1 # finished displaying tab menus, now display the appropriate stuff for the selected tab if ($active_tab eq "0") { # tab 1 = main calendar view $prev_month_link .=<$prev_string p1 $next_month_link .=<$next_string p1 $cal_controls_text = &generate_calendar_controls(); if ($q->param('custom_calendar') == 1) { $html_output =~ s/###calendar controls###//g; } else { $html_output =~ s/###calendar controls###/$cal_controls_text/g; } if ( !$logged_in && $options{force_login} ) { $insert_text .= &forced_login(); } else { $insert_text .= &do_calendar_list_view(); } #select event range $cal_month_start_date = timegm(0,0,0,1,$cal_start_month,$cal_start_year); @cal_month_start_date_array = gmtime $cal_month_start_date; $events_start_timestamp = $cal_month_start_date - 604800; # +7 day margin $events_end_timestamp = &find_end_of_month($cal_end_month, $cal_end_year) + 604800; # +7 day margin #now that we have selected the appropriate events, we can #generate the corresponding javascript and calendar view #and insert/add it to the html output. $page_javascript .= &calendar_view_javascript($events_start_timestamp, $events_end_timestamp); #replace javascript placeholders with actual html/javascript code $html_output =~ s/###previous month link###/$prev_month_link/g; $html_output =~ s/###next month link###/$next_month_link/g; } #done with main active tab stuff (the stuff that's different depending #on which tab is active. The following stuff is the same regardless #of which tab is active. $html_output =~ s/###calendar area###/$insert_text/g; $html_output =~ s/###version###/$plans_version/g; my $add_event_to_current_cal_text =<$lang{add_event_to_this_calendar} p1 chomp $add_event_to_current_cal_text; my $current_calendar_options_text =<$lang{edit_calendar_options} p1 chomp $current_calendar_options_text; my $current_calendar_subscribe_text =<$lang{subscribe_to_this_calendar} p1 chomp $current_calendar_subscribe_text; $current_calendar_subscribe_text = '' if ( $options{'ical_export'} ne "1" ); if ($active_tab eq "0") { $html_output =~ s/###add event to current calendar link###/$add_event_to_current_cal_text/; $html_output =~ s/###edit calendar options link###/$current_calendar_options_text/; if ( !$logged_in && $options{force_login} ) { $html_output =~ s/###subscribe calendar link###//g; $html_output =~ s/###export calendar link###//; $html_output =~ s/###custom calendar link###//; } else { $html_output =~ s/###subscribe calendar link###/$current_calendar_subscribe_text/g; my $temp = &export_calendar_link(); $html_output =~ s/###export calendar link###/$temp/; my $custom_calendar_link =<$lang{make_custom_calendar} p1 chomp $custom_calendar_link; $html_output =~ s/###custom calendar link###/$custom_calendar_link/; #$debug_info .= "custom calendar link: $custom_calendar_link\n"; } } else { $html_output =~ s/###subscribe calendar link###//g; $html_output =~ s/###add event to current calendar link###//; $html_output =~ s/###edit calendar options link###//; $html_output =~ s/###custom calendar link###//; $html_output =~ s/###export calendar link###//; } # pending event stuff $html_output =~ s/###messages###//; my $pending_events_area = &generate_pending_events_area(); if ($pending_events_area ne "") { $pending_events_area = < $pending_events_area p1 } $html_output =~ s/###logged-in stuff###/$pending_events_area/; $common_javascript .= &common_javascript(); $common_javascript .= &generate_pending_events_javascript() if (&pending_events_visible()); #replace javascript placeholders with actual html/javascript code $html_output =~ s/###page-specific javascript###/\n$page_javascript/; $html_output =~ s/###common javascript###/\n$common_javascript/; $debug_info = "$error_info$debug_info"; if ($debug_info =~ /\S/) { $debug_info =~ s/\n/\n/g; $debug_info = < Error, Warnings, & Debug Messages:
$debug_info <\/div> p1 } $html_output =~ s/###debug stuff###/$debug_info/g; $html_output =~ s/###cookie_text###/$cookie_header_text/; print $html_output; } #********************end default view code***************************** sub add_edit_calendars { my %results; $results{'messages'} = []; $results{'success'} = 0; my $cal_id = $current_cal_id; # need to validate cal id for add/edit my $cal_valid = 1; if ($q->param('add_edit_cal_action') eq "delete") { #delete calendar &load_events("all"); &normalize_timezone(); my $del_valid=1; #check password. if ($options{disable_passwords} ne "1" && !$logged_in_as_root && !$logged_in_as_current_cal_admin) { $del_valid=0; push @{$results{messages}}, "$lang{update_cal_error1}$current_calendar{title}"; } # prevent delete of primary calendar if ($cal_id eq "0") { $del_valid=0; push @{$results{messages}}, $lang{update_cal_error2}; } if ($del_valid == 1) { #actually delete the calendar. # first, delete all its events my @deleted_event_ids; my @updated_event_ids; foreach $event_id (keys %events) { # if the event is only on one calendar, delete it if (scalar@{$events{$event_id}{cal_ids}} == 1) { if ($events{$event_id}{cal_ids}[0] eq $cal_id) { push @deleted_event_ids, $event_id; } else {next;} } else { # otherwise, just remove that calendar from its cal_ids my $index=0; foreach $temp_cal_id (@{$events{$event_id}{cal_ids}}) { if ($temp_cal_id eq $cal_id) { break; } $index++; } splice @{$events{$event_id}{cal_ids}}, $index, 1; { push @updated_event_ids, $event_id;} } } &delete_events(\@deleted_event_ids); &update_events(\@updated_event_ids); # next, delete the calendar in question &delete_calendar($cal_id); # redundant in flat-file mode, needed for sql mode # finally, delete any references in other calendars (background calendars) my @cals_to_update; foreach $calendar_id (sort {$a <=> $b} keys %calendars) { #$debug_info .= "calendar $calendar_id\n"; if ($calendars{$calendar_id}{local_background_calendars}{$cal_id} eq "1") { delete $calendars{$calendar_id}{local_background_calendars}{$cal_id}; push @cals_to_update, $calendar_id; } if ($calendars{$calendar_id}{selectable_calendars}{$cal_id} eq "1") { delete $calendars{$calendar_id}{selectable_calendars}{$cal_id}; push @cals_to_update, $calendar_id; } } &update_calendars(\@cals_to_update); my $temp = $lang{update_cal_error3}; $temp =~ s/###title###/$current_calendar{title}/; push @{$results{messages}}, $temp; } $results{success} = $del_valid; # properly format errors, warnings } else { #the user added/updated a calendar #check all input fields for validity my $cal_title = $q->param('cal_title'); my $cal_link = $q->param('cal_link'); my $cal_details = $q->param('cal_details'); my @local_background_calendars = $q->param('background_calendars'); my @selectable_calendars = $q->param('selectable_calendars'); my $list_background_calendars_together = $q->param('list_background_calendars_together'); my $background_events_display_style = $q->param('background_events_display_style'); my $background_events_fade_factor = $q->param('background_events_fade_factor'); my $background_events_color = $q->param('background_events_color'); my $new_calendars_automatically_selectable = "y" if ($q->param('new_calendars_automatically_selectable') =~ "y"); my $allow_remote_calendar_requests = $q->param('allow_remote_calendar_requests'); my $remote_calendar_requests_require_password = $q->param('remote_calendar_requests_require_password'); my $remote_calendar_requests_password = $q->param('remote_calendar_requests_password'); my $new_remote_calendars_xml = $q->param('new_remote_calendars_xml'); my $calendar_events_color = $q->param('calendar_events_color'); my $default_number_of_months = $q->param('default_number_of_months'); my $max_number_of_months = $q->param('max_months'); my $gmtime_diff = $q->param('gmtime_diff'); my $date_format = $q->param('date_format'); $date_format = lc $date_format; my $week_start_day = $q->param('week_start_day'); my $event_change_email = $q->param('event_change_email'); my $custom_template = $q->param('custom_template'); $custom_template =~ s/http:\/\///g; my $custom_stylesheet = $q->param('custom_stylesheet'); $custom_stylesheet =~ s/http:\/\///g; my $cal_password = $q->param('cal_password'); my $new_cal_password = $q->param('new_cal_password'); my $repeat_new_cal_password = $q->param('repeat_new_cal_password'); $cal_title =~ s/\r//g; # some browsers sneak these in $cal_link =~ s/\r//g; # some browsers sneak these in $cal_details =~ s/\r//g; # some browsers sneak these in #check for required fields if ($cal_title eq "") { $cal_valid=0; push @{$results{messages}}, '[error]' . $lang{update_cal_error5}; } #strip all html from label field if ($cal_title =~ m/<(.*)>/) { push @{$results{messages}}, '[warning]' .$lang{update_cal_error6}; $cal_title =~ s/<(.*)>//g; } $cal_link =~ s/http:\/\///g; #strip http:// from link field #check for date format if ( $date_format =~ /y{2}/ && $date_format !~ /y{4}/ ) { $date_format =~ s/yy/yyyy/g; } if ($date_format !~ /^(mm|dd|yyyy)\W(mm|dd|yyyy)\W(mm|dd|yyyy)$/ ) { $cal_valid=0; push @{$results{messages}}, '[error]' .$lang{update_cal_error6_5}; } if ($add_edit_cal_action eq "edit") { if ($options{disable_passwords} ne "1") { #this action is an edit of an existing calendar, so we need to make a replacement. if (!(defined $calendars{$cal_id})) { $cal_valid=0; push @{$results{messages}}, '[error]' .$lang{update_cal_error7}; } else { #check password if ($options{disable_passwords} ne "1" && !$logged_in_as_root && !$logged_in_as_current_cal_admin) { $cal_valid=0; push @{$results{messages}}, '[error]' ."$lang{update_cal_error1} $calendars{$cal_id}{title}"; } } #check for new password if ($new_cal_password ne "" || $repeat_new_cal_password ne "") { if ($new_cal_password ne $repeat_new_cal_password) { $cal_valid=0; push @{$results{messages}}, '[error]' .$lang{update_cal_error8}; } else { $calendars{$cal_id}{password} = crypt($new_cal_password, $options{salt}); } } } # check for gmtime_diff field if ($options{force_single_timezone} eq "1" && $cal_id ne "0") { $gmtime_diff = $calendars{0}{gmtime_diff} } # encrypt remote calendar password $remote_calendar_requests_password = crypt($remote_calendar_requests_password, $options{salt}); if ($cal_valid == 1) { # update calendar record my $xml_data = ""; $calendars{$cal_id}{title} = $cal_title; $calendars{$cal_id}{details} = $cal_details; $calendars{$cal_id}{link} = $cal_link; $calendars{$cal_id}{new_calendars_automatically_selectable} = $new_calendars_automatically_selectable; $calendars{$cal_id}{list_background_calendars_together} = $list_background_calendars_together; $calendars{$cal_id}{calendar_events_color} = $calendar_events_color; $calendars{$cal_id}{background_events_display_style} = $background_events_display_style; $calendars{$cal_id}{background_events_fade_factor} = $background_events_fade_factor; $calendars{$cal_id}{background_events_color} = $background_events_color; $calendars{$cal_id}{default_number_of_months} = $default_number_of_months; $calendars{$cal_id}{max_number_of_months} = $max_number_of_months; $calendars{$cal_id}{gmtime_diff} = $gmtime_diff; $calendars{$cal_id}{date_format} = $date_format; $calendars{$cal_id}{week_start_day} = $week_start_day; $calendars{$cal_id}{event_change_email} = $event_change_email; $calendars{$cal_id}{custom_template} = $custom_template; $calendars{$cal_id}{custom_stylesheet} = $custom_stylesheet; $calendars{$cal_id}{allow_remote_calendar_requests} = $allow_remote_calendar_requests; $calendars{$cal_id}{remote_calendar_requests_require_password} = $remote_calendar_requests_require_password; $calendars{$cal_id}{remote_calendar_requests_password} = $remote_calendar_requests_password; # update local background calendars foreach $local_background_calendar (keys %{$calendars{$cal_id}{local_background_calendars}}) { delete $calendars{$cal_id}{local_background_calendars}{$local_background_calendar}; } foreach $local_background_calendar (@local_background_calendars) { $calendars{$cal_id}{local_background_calendars}{$local_background_calendar} = 1; } #$debug_info .= "new remote calendars xml: $new_remote_calendars_xml\n"; #delete existing remote background calendars foreach $current_remote_calendar_id (keys %{$current_calendar{remote_background_calendars}}) { if ($q->param("delete_remote_calendar_$current_remote_calendar_id") ne "") { my $temp = $lang{get_remote_calendar5}; $temp =~ s/###remote url###/$current_calendar{remote_background_calendars}{$current_remote_calendar_id}{url}/g; $temp =~ s/###remote id###/$current_calendar{remote_background_calendars}{$current_remote_calendar_id}{remote_id}/g; push @{$results{messages}}, $temp; delete $calendars{$current_cal_id}{remote_background_calendars}{$current_remote_calendar_id}; } } # update remote background calendars unless ($new_remote_calendars_xml eq "") { my %new_remote_calendars = %{&xml2hash($new_remote_calendars_xml)}; #$debug_info .= "$new_remote_calendars{remote_calendars}{remote_calendar}\n"; my $new_remote_cal_id = &max(keys %{$calendars{$cal_id}{remote_background_calendars}}) + 1; #$debug_info .= (scalar keys %{$calendars{$cal_id}{remote_background_calendars}})." remote calendars already\n"; #$debug_info .= "new_remote_cal_id: $new_remote_cal_id\n"; if ($new_remote_calendars{remote_calendars}{remote_calendar} =~ /array/i) { # multiple remote background calendars foreach $temp (@{$new_remote_calendars{remote_calendars}{remote_calendar}}) { my %new_remote_calendar = %{$temp}; $found=0; foreach $current_remote_calendar_id (keys %{$current_calendar{remote_background_calendars}}) { #$debug_info .= "comparing $current_calendar{remote_background_calendars}{$current_remote_calendar_id}{url} with $new_remote_calendar{url}\n"; $found=1 if ($current_calendar{remote_background_calendars}{$current_remote_calendar_id}{url} eq $new_remote_calendar{url} && $current_calendar{remote_background_calendars}{$current_remote_calendar_id}{type} eq $new_remote_calendar{type} && $current_calendar{remote_background_calendars}{$current_remote_calendar_id}{version} eq $new_remote_calendar{version} && $current_calendar{remote_background_calendars}{$current_remote_calendar_id}{password} eq $new_remote_calendar{password} && $current_calendar{remote_background_calendars}{$current_remote_calendar_id}{remote_id} eq $new_remote_calendar{remote_id}); } if ($found==0) { $calendars{$cal_id}{remote_background_calendars}{$new_remote_cal_id}{url} = $new_remote_calendar{url}; $calendars{$cal_id}{remote_background_calendars}{$new_remote_cal_id}{type} = $new_remote_calendar{type}; $calendars{$cal_id}{remote_background_calendars}{$new_remote_cal_id}{version} = $new_remote_calendar{version}; $calendars{$cal_id}{remote_background_calendars}{$new_remote_cal_id}{password} = $new_remote_calendar{password}; $calendars{$cal_id}{remote_background_calendars}{$new_remote_cal_id}{remote_id} = $new_remote_calendar{remote_id}; $calendars{$cal_id}{remote_background_calendars}{$new_remote_cal_id}{title} = $new_remote_calendar{title}; $new_remote_cal_id++; } else { my $temp = $lang{get_remote_calendar4}; $temp =~ s/###remote url###/$new_remote_calendar{url}/g; $temp =~ s/###remote id###/$new_remote_calendar{remote_id}/g; push @{$results{messages}}, $temp; } #$debug_info .= "remote calendar: $new_remote_calendar{url}\n"; #$debug_info .= "type: $new_remote_calendar{type}\n"; } } else { # single remote background calendar # check against existing remote background calendars. my %new_remote_calendar = %{$new_remote_calendars{remote_calendars}{remote_calendar}}; $found=0; foreach $current_remote_calendar_id (keys %{$current_calendar{remote_background_calendars}}) { #$debug_info .= "comparing $current_calendar{remote_background_calendars}{$current_remote_calendar_id}{url} with $new_remote_calendar{url}\n"; if ($current_calendar{remote_background_calendars}{$current_remote_calendar_id}{url} eq $new_remote_calendar{url} && $current_calendar{remote_background_calendars}{$current_remote_calendar_id}{type} eq $new_remote_calendar{type} && $current_calendar{remote_background_calendars}{$current_remote_calendar_id}{version} eq $new_remote_calendar{version} && $current_calendar{remote_background_calendars}{$current_remote_calendar_id}{password} eq $new_remote_calendar{password} && $current_calendar{remote_background_calendars}{$current_remote_calendar_id}{remote_id} eq $new_remote_calendar{remote_id}) { $found=1; } } if ($found==0) { $calendars{$cal_id}{remote_background_calendars}{$new_remote_cal_id}{url} = $new_remote_calendar{url}; $calendars{$cal_id}{remote_background_calendars}{$new_remote_cal_id}{type} = $new_remote_calendar{type}; $calendars{$cal_id}{remote_background_calendars}{$new_remote_cal_id}{version} = $new_remote_calendar{version}; $calendars{$cal_id}{remote_background_calendars}{$new_remote_cal_id}{password} = $new_remote_calendar{password}; $calendars{$cal_id}{remote_background_calendars}{$new_remote_cal_id}{remote_id} = $new_remote_calendar{remote_id}; $calendars{$cal_id}{remote_background_calendars}{$new_remote_cal_id}{title} = $new_remote_calendar{title}; } else { my $temp = $lang{get_remote_calendar4}; $temp =~ s/###remote url###/$new_remote_calendar{url}/g; $temp =~ s/###remote id###/$new_remote_calendar{remote_id}/g; push @{$results{messages}}, $temp; } } } #$calendars{$cal_id}{remote_background_calendars} = $remote_calendar_requests_password; # update selectable calendars foreach $selectable_calendar (keys %{$calendars{$cal_id}{selectable_calendars}}) { delete $calendars{$cal_id}{selectable_calendars}{$selectable_calendar}; } foreach $selectable_calendar (@selectable_calendars) { $calendars{$cal_id}{selectable_calendars}{$selectable_calendar} = 1; } } if ($cal_valid == 1) { #all checks successful, add/update calendar! &update_calendar($cal_id); push @{$results{messages}}, "$calendars{$current_cal_id}{title} $lang{update_cal_success}"; } else { $cal_add_results .= $lang{update_cal_failure}; } } else { # add new calendar # check password, if necessary if ($options{anonymous_calendar_requests} eq "1") { # TODO - come up with a better auth token #my $t1 = $q->param('as'); #my $t2 = $rightnow; #if (abs($t1 - $t2) > 3600) { # $cal_valid=0; #} } else { if ($options{disable_passwords} ne "1" && !$logged_in_as_root && !$logged_in_as_current_cal_admin) { $cal_valid=0; push @{$results{messages}}, '[error]' ."$lang{update_cal_error0}"; } } #check new password if ($options{disable_passwords} ne "1") { if ($new_cal_password ne $repeat_new_cal_password) { $cal_valid=0; push @{$results{messages}}, '[error]' . $lang{update_cal_error9}; } elsif ($new_cal_password eq "" || $repeat_new_cal_password eq "" ) { $cal_valid=0; push @{$results{messages}}, '[error]' . $lang{update_cal_error10}; } else { $input_password = crypt($new_cal_password, $options{salt}); } } my $new_cal_id; if ($cal_valid == 1) { $new_cal_id = $max_action_id + int(rand(100)); # just in case several people decide to add something new at the same time. $new_calendars{$new_cal_id}{id} = $new_cal_id; $new_calendars{$new_cal_id}{title} = $cal_title; $new_calendars{$new_cal_id}{details} = $cal_details; $new_calendars{$new_cal_id}{link} = $cal_link; $new_calendars{$new_cal_id}{list_background_calendars_together} = $list_background_calendars_together; $new_calendars{$new_cal_id}{calendar_events_color} = $calendar_events_color; $new_calendars{$new_cal_id}{background_events_fade_factor} = $background_events_fade_factor; $new_calendars{$new_cal_id}{background_events_color} = $background_events_color; $new_calendars{$new_cal_id}{default_number_of_months} = $default_number_of_months; $new_calendars{$new_cal_id}{max_number_of_months} = $max_number_of_months; $new_calendars{$new_cal_id}{gmtime_diff} = $gmtime_diff; $new_calendars{$new_cal_id}{date_format} = $date_format; $new_calendars{$new_cal_id}{week_start_day} = $week_start_day; $new_calendars{$new_cal_id}{custom_template} = $custom_template; $new_calendars{$new_cal_id}{custom_stylesheet} = $custom_stylesheet; $new_calendars{$new_cal_id}{password} = $input_password; $new_calendars{$new_cal_id}{update_timestamp} = $rightnow; $new_calendars{$new_cal_id}{allow_remote_calendar_requests} = $allow_remote_calendar_requests; $new_calendars{$new_cal_id}{remote_calendar_requests_require_password} = $remote_calendar_requests_require_password; $new_calendars{$new_cal_id}{remote_calendar_requests_password} = $remote_calendar_requests_password; # local background calendars foreach $local_background_calendar (@local_background_calendars) { $new_calendars{$new_cal_id}{local_background_calendars}{$local_background_calendar} = 1; } # selectable calendars foreach $selectable_calendar (@selectable_calendars) { $new_calendars{$new_cal_id}{selectable_calendars}{$selectable_calendar} = 1; } } # check for refreshes! if ($cal_valid == 1) { my %latest_new_calendar = $new_calendars{$latest_new_cal_id}; if ($new_calendars{$new_cal_id}{title} eq $latest_new_calendar{title} && $new_calendars{$new_cal_id}{details} eq $latest_new_calendar{details} && $new_calendars{$new_cal_id}{link} eq $latest_new_calendar{link}) { $cal_valid = 0; push @{$results{messages}}, $lang{update_cal_dup}; } } if ($cal_valid == 1) { #all checks successful, add calendar! &add_action($new_cal_id, "new_calendar"); $results{'calendar'} = $new_calendars{$new_cal_id}; my $new_cal_details = &generate_cal_details($new_calendars{$new_cal_id}); $new_cal_details =~ s///; $new_cal_details =~ s/Link directly.+<\/a>//s; if ($options{new_calendar_request_notify} ne "") { my $body = <$lang{add_cal_success3} p1 &send_email($options{new_calendar_request_notify}, $options{reply_address}, $options{reply_address}, $lang{add_cal_email_notify2}, $body); } my $temp = $lang{add_cal_success1}; # add successful $temp = $lang{add_cal_success4} if ($add_edit_cal_action eq "edit"); # update successful push @{$results{messages}}, $temp; push @{$results{messages}}, $lang{add_cal_success2}; $results{'calendars_waiting_for_approval'} = \%new_calendars; } else { push @{$results{messages}}, $lang{add_cal_fail1}; } } $results{'success'} = $cal_valid; } return \%results; } #********************end add_edit_calendars code***************************** sub approve_delete_pending_calendars { my %results; $results{'messages'} = []; $results{'success'} = 1; my @pending_calendars_to_delete; my @calendars_to_add; my @calendars_to_update; #check password $input_password = crypt($q->param('main_password'), $options{salt}); if ($options{disable_passwords} ne "1") { if ($input_password ne $master_password) { $results{'success'} = 0; push @{$results{messages}}, $lang{view_pending_calendars7}; return \%results; } } #go through each new calendar in the new calendars file, take appropriate action foreach $new_cal_id (keys %new_calendars) { my $new_cal_details = &generate_cal_details($new_calendars{$new_cal_id}); if ($q->param("pending_calendar_".$new_cal_id."_approve_delete") eq "approve") { $max_cal_id+=1; #calculate new id # for the new calendar foreach $cal_id (keys %calendars) { if ($calendars{$cal_id}{new_calendars_automatically_selectable} =~ "y") { $calendars{$cal_id}{selectable_calendars}{$max_cal_id} = 1; push @calendars_to_update, $cal_id; } } $calendars{$max_cal_id} = $new_calendars{$new_cal_id}; $calendars{$max_cal_id}{id} = $max_cal_id; # make sure the calendar can select itself. $calendars{$max_cal_id}{selectable_calendars}{$max_cal_id} = 1 if (scalar keys %{$calendars{$max_cal_id}{selectable_calendars}} > 0); $approve_or_delete_result = $new_calendars{$new_cal_id}{'title'} . ' : ' .$lang{view_pending_calendars8}; delete $new_calendars{$new_cal_id}; push @pending_calendars_to_delete, $new_cal_id; push @calendars_to_add, $max_cal_id; } elsif ($q->param("pending_calendar_".$new_cal_id."_approve_delete") eq "delete") { $approve_or_delete_result = $new_calendars{$new_cal_id}{'title'} . ' : ' . $lang{view_pending_calendars9}; delete $new_calendars{$new_cal_id}; push @pending_calendars_to_delete, $new_cal_id; } else { $approve_or_delete_result = $new_calendars{$new_cal_id}{'title'} . ' : ' . $lang{view_pending_calendars10}; } push @{$results{messages}}, $approve_or_delete_result; } &delete_pending_actions(\@pending_calendars_to_delete); &add_calendars(\@calendars_to_add); &update_calendars(\@calendars_to_update); return \%results; } sub do_calendar_list_view() { my $return_text = ""; if ($cal_num_months> 1) { $cal_title_string .=< $prev_month_link $cal_title_string $next_month_link
p1 if ($display_type == 1) { #list view $return_text .= &render_list($cal_start_month, $cal_start_year, $cal_end_month, $cal_end_year); } else { #calendar view $return_text .= &render_calendar($cal_start_month, $cal_start_year, $cal_end_month, $cal_end_year); } $return_text .=< p1 return $return_text; } ###############end do_calendar_list_view ################### sub add_edit_events { my %results; my $recurring_event = $q->param('recurring_event'); my $all_in_series = $q->param('all_in_series'); # load (reload) all events (we have to write events beyond the default time window) if ( $options{data_storage_mode} == 0 ) { unless ($loaded_all_events eq "1") { &load_events("all"); &normalize_timezone(); } } my $login_valid = 0; if ($options{disable_passwords} eq "1") { $login_valid = 1 } elsif ($current_cal_id ne "") { #$debug_info .= "current_cal_id: $current_cal_id\n"; if ($options{multi_calendar_event_mode} == 0 || $options{multi_calendar_event_mode} == 1) { # only the event's calendar's user or the root user are allowed $login_valid = 1 if ($logged_in_as_root || $logged_in_as_current_cal_admin || $logged_in_as_current_cal_user); # current calendar user } elsif ($options{multi_calendar_event_mode} == 2) { # any of the event's calendars' users or the root user are allowed if ($logged_in_as_root || $logged_in_as_current_cal_admin || $logged_in_as_current_cal_user) { # current calendar user $login_valid = 1; } else { my $cal_password_valid = 0; foreach $cal_id (@evt_other_cal_ids) { my $temp = $cal_id; $cal_password_valid = 1 if ($profile->{calendar_permissions}->{$cal_id}->{admin} eq "1"); # shared calendar admin $cal_password_valid = 1 if ($profile->{calendar_permissions}->{$cal_id}->{user} ne ""); # shared calendar user } $login_valid = 1 if ($cal_password_valid == 1); } } } if ($q->param('del_event_button') ne "" || $api_command eq "delete_event") { $del_valid = 1; #delete event. if ($login_valid == 0) { $del_valid = 0; push @{$results{messages}}, "[error]$lang{update_event_err1} '$current_calendar{title}'"; } if ( ! &event_exists( $current_event_id ) ) { $del_valid=0; push @{$results{messages}}, "[error]$lang{update_event_err2}"; } if ($del_valid == 1) { #actually delete the event(s). if ($all_in_series ne "1") { my $subj = $lang{notify_subj}; $subj =~ s/\$1/$script_url\/$name/; my $body = $lang{event_delete_notify}; $body =~ s/\$1/$events{$current_event_id}{title}/; $body =~ s/\$2/$calendars{$current_cal_id}{title}/; foreach $email (@{$current_calendar{delete_emails}}) { #$debug_info .= "email notification sent to $email\n"; &send_email($email, $options{reply_address}, $options{reply_address}, $subj, $body) if ($options{email_mode} > 0); } &delete_event($current_event_id); push @{$results{messages}}, "[status]$lang{update_event_delete_successful}"; } else { # get the ids of the events in the series. my @events_in_series = &get_events_in_series($q->param('series_id')); &normalize_timezone(); &delete_events(\@events_in_series); push @{$results{messages}}, "[status]$lang{update_event_delete_successful_recurring}"; } $results{success} = 1; } else { $results{success} = 0; } } else { # not a delete if (!$login_valid) { if ($options{anonymous_events} && $add_edit_event eq "add" ) { my $temp = $lang{update_event_err16}; $temp =~ s/###calendar###/$current_calendar{title}<\/b>/g; push @{$results{messages}}, ("[error]".$temp); } else { push @{$results{messages}}, "[error]".($lang{update_event_err1}."'".$current_calendar{title}."'"); } } #check all input fields for validity my $event_valid = 1; my $event_id = $q->param('evt_id'); # only if editing. #my $event_cal_id = $current_cal_id; my @evt_other_cal_ids; @evt_other_cal_ids = $q->param('evt_other_cal_ids') if ($options{multi_calendar_event_mode} > 0); my $event_cal_password = $q->param('cal_password'); my $event_title = $q->param('evt_title'); my $event_icon = $q->param('evt_icon'); my $event_details = $q->param('evt_details'); my $event_unit_number = $q->param('unit_number'); my $event_bgcolor = $q->param('evt_bgcolor'); my $event_block_merge = $q->param('evt_block_merge'); my $event_series_id = $q->param('series_id'); my $event_duration = 0; my $event_start_timestamp = 0; my $event_end_timestamp = 0; $recur_end_date = $q->param('recur_end_date'); my $all_day_event = $q->param('evt_all_day_event'); my $no_end_time = ""; my $event_start_time = $q->param('evt_start_time'); my $event_end_time = $q->param('evt_end_time'); $event_start_date = $q->param('evt_start_date'); my $event_days = $q->param('evt_days'); my %recurrence_parms; $recurrence_parms{'recurrence_type'} = $q->param('recurrence_type'); $recurrence_parms{'weekday_of_month_type'} = $q->param('weekday_of_month_type'); $recurrence_parms{'every_x_days'} = $q->param('every_x_days'); $recurrence_parms{'every_x_weeks'} = $q->param('every_x_weeks'); $recurrence_parms{'year_fit_type'} = $q->param('year_fit_type'); $recurrence_parms{'recur_end_date'} = $q->param('recur_end_date'); my @custom_months = $q->param('custom_months'); $recurrence_parms{'custom_months'} = \@custom_months; $recurrence_parms{'recur_end_timestamp'} = 0; my %input_parms; # parms that are input by the user, but not stored as final event data $input_parms{'update_all_in_series'} = $q->param('all_in_series'); $input_parms{'event_days'} = $event_days; $input_parms{'event_start_time'} = $event_start_time; $input_parms{'event_end_time'} = $event_end_time; $input_parms{'all_day'} = $all_day_event; # Check data for legitimacy. # some of these checks might be a bit redundant. if ($current_cal_id eq "") { $event_valid = 0; push @{$results{messages}}, "[error]".$lang{update_event_err3}; } if ($event_title eq "") { $event_valid = 0; push @{$results{messages}}, "[error]".$lang{update_event_err4}; } if ($event_icon eq "") { $event_valid = 0; push @{$results{messages}}, "[error]".$lang{update_event_err5}; } $event_title =~ s/\r//g; # some browsers sneak these in $event_details =~ s/\r//g; # some browsers sneak these in #strip html if ($event_title =~ m/<(.*)>/) { my $temp = $event_title; $temp =~ s//>/g; push @{$results{messages}}, "[warning]".$lang{update_event_err7}; $event_title =~ s/<(.*)>//g; } # strip out all non-numeric information from unit number my $unit_number = $event_unit_number; $unit_number =~ s/\D//g; #check event calendar name against existing calendars if (!defined == $calendars{$current_cal_id}) { $event_valid = 0; push @{$results{messages}}, "[error]".$lang{update_event_err8}; } $event_valid = 0 if ($login_valid == 0 && !$options{anonymous_events}); # check dates if ($event_valid == 1) { my $verify_date_results = &verify_date($event_start_date); if ($verify_date_results ne "") { $event_valid = 0; my @sub_results = split("\n", $verify_date_results); foreach $sub_result (@sub_results) { push @{$results{messages}}, "[error]$lang{update_event_err9} $sub_result"; } if ($event_days eq "") { push @{$results{messages}}, "[error]$lang{update_event_err9}$lang{date_verify_err2}"; } if ($event_days =~ m/\D/ || $event_days <= 0) { my $temp = $lang{date_verify_err3}; $temp =~ s/\$1/$event_days/; push @{$results{messages}}, "[error]$lang{update_event_err9}$temp"; } } } # check recurring "repeat until" date if ($event_valid == 1) { if ($recurring_event ne "" && $add_edit_event eq "add") { my $verify_date_results = &verify_date($recurrence_parms{'recur_end_date'}, \%recurrence_parms); if ($verify_date_results ne "") { $event_valid = 0; my @sub_results = split("\n", $verify_date_results); foreach $sub_result (@sub_results) { push @{$results{messages}}, "[error]$lang{update_event_err10} $sub_result"; } } } } # check time if ($event_valid == 1 && $all_day_event ne "1") { my $verify_time_results = &verify_time($event_start_time); if ($verify_time_results ne "") { $event_valid = 0; my @sub_results = split("\n", $verify_time_results); foreach $sub_result (@sub_results) { push @{$results{messages}}, "[error]$lang{update_event_err14} $sub_result"; } } if ($event_end_time ne "") { my $verify_time_results = &verify_time($event_end_time); if ($verify_time_results ne "") { $event_valid = 0; my @sub_results = split("\n", $verify_time_results); foreach $sub_result (@sub_results) { push @{$results{messages}}, "[error]$lang{update_event_err15} $sub_result"; } } } } if ($event_valid == 1) { my ($start_mon, $start_mday, $start_year) = &format2mdy($event_start_date, $current_calendar{date_format}); $start_mon--; # convert month to 0-11 format ($event_start_timestamp, $event_end_timestamp) = ×tamp_from_datetime($start_mday,$start_mon,$start_year,$event_days,$event_start_time,$event_end_time,$all_day_event); $event_end_timestamp +=1 if ($event_start_timestamp == $event_end_timestamp); # give all events a duration of at least 1 second $event_duration = $event_end_timestamp - $event_start_timestamp; $no_end_time = 1 if ($event_duration == 1); $recurrence_parms{'duration'} = $event_duration if ($recurring_event ne ""); if ($recurring_event ne "" && $add_edit_event eq "add") { my ($recur_end_mon, $recur_end_mday, $recur_end_year) = &format2mdy($recurrence_parms{'recur_end_date'}, $current_calendar{date_format}); $recur_end_mon--; $recurrence_parms{'recur_end_timestamp'} = timegm(0,0,0,$recur_end_mday,$recur_end_mon,$recur_end_year); } # display warning if start timestamp is before present date if ($event_start_timestamp < $rightnow-86400) { push @{$results{messages}}, "[warning]$lang{update_event_err11}"; } } # check for refreshes! if ($recurring_event eq "") { my %latest_event = $events{$latest_event_id}; if ($latest_event{cal_ids}[0] eq $current_cal_id && $latest_event{start} eq $event_start_timestamp && $latest_event{end} eq $event_end_timestamp && $latest_event{title} eq $event_title && $latest_event{details} eq $event_details && $latest_event{icon} eq $event_icon && $latest_event{bgcolor} eq $event_bgcolor && $latest_event{unit_number} eq $event_unit_number) { $event_valid = 0; push @{$results{messages}}, "[error]$lang{update_event_err12}"; } } else { # recurring event refresh protection is a little trickier. # it's currently not implemented } if ($add_edit_event eq "edit") { #check to make sure the event id matches some event in the data structure. It always should, but we check anyway. if (!defined $events{$event_id}) { $event_valid = 0; push @{$results{messages}}, "[error]$lang{update_event_err13}"; } } # properly format errors & warnings $message_results=""; if ($event_valid == 1) { $results{success} = 1; #$debug_info .= "(add_update_event) event_id: $event_id\n"; unshift @evt_other_cal_ids, $current_cal_id; my %new_event = (recurring => $recurring_event, cal_ids => \@evt_other_cal_ids, start => $event_start_timestamp, end => $event_end_timestamp, all_day_event => $all_day_event, days => &calculate_event_days($event_start_timestamp, $event_end_timestamp), title => $event_title, details => $event_details, icon => $event_icon, bgcolor => $event_bgcolor, block_merge => $event_block_merge, unit_number => $event_unit_number, update_timestamp => $rightnow, existing_id => $event_id, recurrence_parms => \%recurrence_parms); if ($login_valid) { my %commit_results = %{&commit_event(\%new_event, \%input_parms)}; my @commit_messages = @{$commit_results{messages}}; my @new_event_ids = @{$commit_results{new_event_ids}}; $results{messages} = \(@{$results{messages}}, @commit_messages); # array concatenate $results{new_event_ids} = \@new_event_ids; } elsif ( $options{anonymous_events} && $add_edit_event eq "add" ) { # anonymous event add -> update queue my $new_event_id = $max_action_id + int(rand(100)); # just in case several people decide to add something new at the same time. $new_event{id} = $new_event_id; # add event to %events data structure $new_events{$new_event_id} = \%new_event; # notify email(s) if ($options{email_mode} > 0) { my $subj = $lang{notify_subj}; $subj =~ s/\$1/$script_url\/$name/; my $body = $lang{event_pending_notify}; $body =~ s/\$1/$events{$current_event_id}{title}/; $body =~ s/\$2/$calendars{$current_cal_id}{title}/; $body =~ s/\$3/$script_url\/$name?view_pending_event=1&pending_event_id=$new_event_id/; foreach $add_email (@{$current_calendar{add_emails}}) { push @{$results{messages}}, "[status]email notification sent to $add_email"; &send_email($add_email, $options{reply_address}, $options{reply_address}, $subj, $body); } } &add_action($new_event_id, "new_event"); $event_box_text .= &generate_event_details(\%new_event, $event_details_template); $results{text} .= <$lang{update_event_add_pending_successful}

$event_box_text p1 } else { $results{'success'} = 0; } } } return \%results; } #******************** end add_edit_event ***************************** sub commit_event() { # commit an add or update of an event my ($new_event_ref, $input_parms_ref) = @_; my %results; $results{'messages'} = []; $results{'new_event_ids'} = []; my $new_event_id; my %new_event = %{$new_event_ref}; my %input_parms = %{$input_parms_ref}; #unless ($loaded_all_events eq "1") { # &load_events("all"); # &normalize_timezone(); #} if ($add_edit_event eq "add") { # add a new event if ($new_event{'recurring'} eq "") { my $event_box_text = ""; $new_event_id = ++$max_event_id; $events{$new_event_id} = deep_copy(\%new_event); $events{$new_event_id}{id} = $new_event_id; &add_event($new_event_id); $event_box_text .= &generate_event_details($events{$new_event_id}, $event_details_template); push @{$results{new_event_ids}}, $new_event_id; #temporarily offset event times by calendar gmtime diff push @{$results{messages}}, $lang{update_event_add_successful}; } else { # recurring events loop my %recurrence_parms = %{$new_event{'recurrence_parms'}}; my $new_series_id = ++$max_series_id; my $date_text = ""; my @recurring_events_timestamps = @{&calculate_recurring_events($new_event{'start'}, \%recurrence_parms)}; my @recurring_event_ids = (); foreach $recurring_event_start_timestamp (@recurring_events_timestamps) { my $new_event_id = ++$max_event_id; my $recurring_event_end_timestamp = $recurring_event_start_timestamp + $recurrence_parms{'duration'}; $events{$new_event_id} = deep_copy(\%new_event); $events{$new_event_id}{id} = $new_event_id; $events{$new_event_id}{series_id} = $new_series_id; $events{$new_event_id}{start} = $recurring_event_start_timestamp; $events{$new_event_id}{end} = $recurring_event_end_timestamp; $events{$new_event_id}{recurrence_parms} = []; push @recurring_event_ids, $new_event_id; push @{$results{new_event_ids}}, $new_event_id; my $date_range = &nice_date_range_format($recurring_event_start_timestamp, $recurring_event_end_timestamp, "-"); $date_text .= <$date_range p1 } &add_events(\@recurring_event_ids); $event_box_text .= &generate_event_details($events{$max_event_id}, $event_details_template); my $temp = <$lang{update_event_add_successful_recurring}

    $date_text
$event_box_text p1 push @{$results{messages}}, $temp; } if ($options{email_mode} > 0) { my $subj = $lang{notify_subj}; $subj =~ s/\$1/$script_url\/$name/; my $body = $lang{event_add_notify}; $body =~ s/\$1/$new_event{title}/; $body =~ s/\$2/$calendars{$current_cal_id}{title}/; $body =~ s/\$3/$script_url\/$name?view_event=1&evt_id=$max_event_id/; foreach $add_email (@{$current_calendar{add_emails}}) { push @{$results{messages}}, "[status]email notification sent to $add_email"; &send_email($add_email, $options{reply_address}, $options{reply_address}, $subj, $body); } } } elsif ($add_edit_event eq "edit") { #if we need to replace an existing record my $event_id = $new_event{'existing_id'}; &load_event( $event_id ); &normalize_timezone( ); my $old_series_id = $events{$event_id}{'series_id'}; if ($new_event{'recurring'} eq "" || $input_parms{'update_all_in_series'} ne "1") { $events{$event_id} = deep_copy(\%new_event); $events{$event_id}{id} = $event_id; $events{$event_id}{'series_id'} = $old_series_id; &update_event($event_id); push @{$results{new_event_ids}}, $event_id; $event_box_text .= &generate_event_details($events{$event_id}, $event_details_template); push @{$results{messages}}, $lang{update_event_update_successful}; } else { # update recurring events # get the ids of the events in the series. my @events_in_series = &get_events_in_series($old_series_id); &normalize_timezone( ); foreach $event_id (@events_in_series) { my %event = %{$events{$event_id}}; my @temp = gmtime($event{end}); ($recurring_event_start_timestamp, $recurring_event_end_timestamp) = ×tamp_from_datetime($temp[3],$temp[4],$temp[5]+1900,$input_parms{'event_days'},$input_parms{'event_start_time'},$input_parms{'event_end_time'},$input_parms{'all_day'}); $events{$event_id} = deep_copy(\%new_event); $events{$event_id}{id} = $event_id; $events{$event_id}{series_id} = $series_id; $events{$event_id}{start} = $recurring_event_start_timestamp; $events{$event_id}{end} = $recurring_event_end_timestamp; $events{$event_id}{recurrence_parms} = []; push @{$results{new_event_ids}}, $event_id; } &update_events(\@events_in_series); push @{$results{messages}}, $lang{update_event_update_successful_recurring}; } if ($options{email_mode} > 0) { my $subj = $lang{notify_subj}; $subj =~ s/\$1/$script_url\/$name/; my $body = ""; $body = $lang{event_update_notify}; $body =~ s/\$1/$new_event{title}/; $body =~ s/\$2/$calendars{$current_cal_id}{title}/; $body =~ s/\$3/$script_url\/$name?view_event=1&evt_id=$event_id/; foreach $email (@{$current_calendar{update_emails}}) { $debug_info .= "email notification sent to $email\n"; &send_email($email, $options{reply_address}, $options{reply_address}, $subj, $body); } } } return \%results; } sub common_javascript { my $logged_in_boolean = ($logged_in) ? "true":"false"; my $date_format = lc $current_calendar{date_format}; $return_string .=< $lang{custom_calendar_title}

p1 foreach $cal_id (sort {$a <=> $b} keys %calendars) { $custom_form_text .=<$calendars{$cal_id}{title} p1 } $custom_form_text .=<

p1 if ($display_type == 1) {$list_selected = "selected";} else {$cal_selected = "selected";} $custom_form_text .=< $lang{custom_calendar_display_type} p1 #list each month in the year $month_index=0; foreach $possible_month (@months) { if ($cal_start_month eq $month_index) { $custom_form_text .=<$possible_month p1 } else { $custom_form_text .=<$possible_month p1 } $month_index++; } $custom_form_text .=< $lang{controls_num_months}

p1 $custom_form_text =~ s/\//\\\//g; $custom_form_text =~ s/\n/\\n/g; $custom_form_text =~ s/"/\\"/g; $return_string .=<'); doc.write('$lang{custom_calendar_title}<\\/title>'); doc.write('<base target=\\"'+main_window_name+'\\">'); doc.write('<link rel=\\"stylesheet\\" href=\\"$css_path\\" type=\\"text/css\\" media=screen>'); doc.write('<body>'); doc.write("$custom_form_text"); doc.write('<\\/body><\\/html>'); doc.close(); //info_window.focus(); } p1 return $return_string; } #***********************end calendar_view_javascript************************" sub generate_users_javascript() { my ($cal_id) = @_; my $results = ""; my @user_results = []; foreach $user_id (keys %users) { my $valid_for_current_calendar = 0; foreach $cal_id (keys %{$users{$user_id}{calendars}}) { if ($cal_id eq $current_cal_id) { $valid_for_current_calendar = 1; last; } } next if ($valid_for_current_calendar == 0); #next if ($user_id eq ""); my %user = %{$users{$user_id}}; push @user_results, {'user_id' => $user_id, 'user_name' => $user{'name'}}; } return @user_results; } sub generate_pending_events_javascript() { my $results = ""; my @pending_events = &pending_events_as_array(); $results = 'jQuery.planscalendar.pending_events = ' . encode_json( \@pending_events ) . ";\n"; return $results; } sub pending_events_as_array() { my $results = ""; my @pending_events; foreach $pending_event_id (sort {$new_events{$a}{update_timestamp} <=> $new_events{$b}{update_timestamp}} keys %new_events) { my %pending_event = %{$new_events{$pending_event_id}}; push @pending_events, \%pending_event; } return @pending_events; } sub add_edit_calendars_javascript { my $return_string = ""; $return_string .= &generate_users_javascript($current_calendar{id}); my $editing_calendar = ( $q->param('add_edit_cal_action') eq "edit" ) ? 'true' : 'false'; $return_string.=<<p1; var editing_calendar = $editing_calendar; function update_users() { if (!users || users.length == 0) return; } p1 $return_string.=<<p1; function show_help() { info_window = this.open("", "info_window", "resizable=yes,status=yes,scrollbars=yes,top="+info_window_y+",left="+info_window_x+",width=400,height=400"); doc = info_window.document; doc.open('text/html'); doc.write('<html>'); doc.write('<title>$lang{help_box_title}<\\/title>'); doc.write('<link rel=\\"stylesheet\\" href=\\"$css_path\\" type=\\"text/css\\" media=screen>'); doc.write('$popup_javascript_info'); doc.write('<body onResize=\\"javascript:do_onresize()\\">'); doc.write(help_text); doc.write('<\\/body><\\/html>'); doc.close(); //info_window.focus(); } function preview_cal() { var cal_title = document.update_cal_form.cal_title.value var cal_link = document.update_cal_form.cal_link.value var cal_details = document.update_cal_form.cal_details.value; if (cal_title == "") { evt_label = "<span style=\\"color:#ff0000\\">$lang{preview_calendar_temp_title}<\\/span>"; add_disable = true; } // if (document.getElementById("preview_warning")) { document.getElementById("preview_warning").innerHTML=""; } info_window = this.open("", "info_window", "resizable=yes,status=yes,scrollbars=yes,top="+info_window_y+",left="+info_window_x+",width=400,height=400"); doc = info_window.document; doc.open('text/html'); doc.write('<html>'); doc.write('<title>$lang{preview_calendar_title}<\\/title>'); doc.write('<link rel=\\"stylesheet\\" href=\\"$css_path\\" type=\\"text/css\\" media=screen>'); doc.write('$popup_javascript_info'); doc.write('<body onResize=\\"javascript:do_onresize()\\">'); doc.write("<div class=\\"info_box\\" style=\\"padding:5px;\\"><br/><div style=\\"white-space:nowrap;\\"><span class=\\"cal_title\\">"); doc.write("<a target=\\"_blank\\" href=\\""+cal_link+"\\">"+cal_title+"<\\/a><\\/span><\\/div><br/><div>"+cal_details+"<\\/div><\\/div>"); doc.write('<\\/body><\\/html>'); doc.close(); //info_window.focus(); } p1 my @help_text_map = ("title", "link", "details", "password", "new_password", "current_password", "change_password", "selectable_calendars", "background_calendars", "remote_background_calendars", "background_events_display_style", "events_display_style", "list_background_calendars_together", "default_number_of_months", "max_months", "gmtime_diff", "date_format", "week_start_day", "new_calendars_automatically_selectable", "allow_remote_calendar_requests", "remote_calendar_requests_password", "event_change_email", "popup_window_size", "custom_stylesheet", "custom_template", "add_user", "add_new_ical"); my $help_text_javascript = ""; foreach $key (@help_text_map) { my $help_text=<<p1; <div class="help_box"> $lang{'help_cal_'.$key} </div> p1 $help_text =~ s/\n/\\n/g; $help_text =~ s/"/\\"/g; $help_text =~ s/'/\\'/g; $help_text =~ s/\//\\\//g; $help_text =~ s/###css file###/$css_path/g; $help_text_javascript .=<<p1; if (topic == "$key") help_text += "$help_text"; p1 } return $return_string } #********************end add_edit_calendars_javascript********************** sub generate_cal_details() { my ($calendar_ref) = @_; my %calendar = %{$calendar_ref}; my $results = <<p1; <div class="cal_title"> $calendar{title} </div> <div> $calendar{details} </div> p1 $writable{calendar_file} and $return_text .= <<p1; <div style="white-space:nowrap;"> <span class="small_note"> <a href="$script_url/$name?active_tab=2&add_edit_cal_action=edit&cal_id=$calendar{id}">$lang{calendar_add_edit}</a> </span> </div> p1 $return_text .= <<p1; <div style="white-space:nowrap;margin-top:2em;"> <span class="small_note"> $lang{calendar_direct_link}<br/> <a href="$script_url/$name?cal_id=$calendar{id}">$script_url/$name?cal_id=$calendar{id}</a> </span> </div> p1 return $results; } sub render_calendar { my ($cal_start_month, $cal_start_year, $cal_end_month, $cal_end_year, $selected_events) = @_; my $return_text = ""; my $week_events = {}; my $week_slots = {}; my $debug = 0; my @debug_events = ("105"); my $show_month_breaks = ($cal_num_months > 1 && $options{continuous_multimonth} ne '1' ? 1 : 0); my $cal_month_idx = 0; my $last_cal_month; my $is_first_cal_date = 1; my @use_other_month = (0, 1); #initialize loop variables my $cal_start_timestamp = timegm(0,0,0,1,$cal_start_month,$cal_start_year); my $cal_end_timestamp = find_end_of_month($cal_end_month, $cal_end_year); my $current_month = $cal_start_month; my $current_year = $cal_start_year; while ($current_year < $cal_end_year || ($current_year == $cal_end_year && $current_month <= $cal_end_month)) { $last_cal_month = ($current_year * 100 + $current_month) == ($cal_end_year * 100 + $cal_end_month); foreach $key (keys %week_events) {delete $week_events{$key};} foreach $key (keys %week_slots) {delete $week_slots{$key};} #for calendars with multiple months, display the name of each month above the calendar if ($show_month_breaks) { $return_text .=<<p1; <p class="cal_month_title" style="padding:5px;"> $months[$current_month] $current_year </p> p1 } #calculate where to start the calendar (first sunday) #first, calculate what day of the week this month begins on #format for timegm: timegm($sec,$min,$hour,$mday,$mon,$year); my $cal_month_start_date = timegm(0,0,0,1, $current_month, $current_year); my @cal_month_start_date_array = gmtime $cal_month_start_date; my $cal_start_day_offset = $cal_month_start_date_array[6] - $current_calendar{week_start_day}; $cal_start_day_offset += 7 if ($cal_start_day_offset < 0); my $cal_start_date = $cal_month_start_date - (86400 * $cal_start_day_offset); # in continuous_multimonth mode, skip first week if we already drew it in the previous iteration $cal_start_date = $cal_start_date + 604800 if (!$show_month_breaks && $cal_month_idx > 0 && $cal_start_day_offset != 0); # start with other_month if the first month does not start on the first day of the week (i.e there is an offset) @use_other_month = (1, 0) if (!$show_month_breaks && $cal_month_idx == 0 && $cal_start_day_offset != 0 && $current_month % 2); my @cal_start_date_array = gmtime $cal_start_date; my $cal_end_date = $cal_start_date + 86400*37; my $next_month = $current_month+1; if ($next_month == 12) {$next_month=0;} #cal_date keeps track of the date (in timestamp format) #as the calendar loop iterates through each day on the calendar page my $cal_date = $cal_start_date; my @cal_date_array = gmtime $cal_date; my %max_day_events; my %week_max_slots; #make a first pass through the month, assemble event week events structure: #week_events{week_index}{id} ={} #this hash has 4 keys--start_weekday, start_day, length and slot_order (slot_order will be calculated in the second pass) for ($l1=0;$cal_date_array[4] != $next_month;$l1++) { #each calendar has 5 or 6 weeks $week_start_timestamp = $cal_date; $week_end_timestamp = $week_start_timestamp + 604800; $max_day_events{$l1} = 0; @cal_date_array = gmtime $cal_date; foreach $event_id (keys %events) { $debug = 1 if ($debug == 1 && (&contains(\@debug_events, $event_id) || $debug_events[0] eq "all")); my %event = %{$events{$event_id}}; if (&time_overlap($event{start}, $event{end}, $week_start_timestamp, $week_end_timestamp)) { my @event_date_array = gmtime $event{start}; my @event_end_date_array = gmtime $event{end}; my $event_start_weekday = $event_date_array[6] - $current_calendar{week_start_day}; my $event_end_weekday = $event_end_date_array[6] - $current_calendar{week_start_day}; $event_start_weekday +=7 if ($event_start_weekday < 0); $event_end_weekday +=7 if ($event_end_weekday < 0); #$event_start_weekday = 0 if ($event_start_weekday < 0); #$event_end_weekday = 0 if ($event_end_weekday < 0); #$event_start_weekday = 6 if ($event_start_weekday > 6); #$event_end_weekday = 6 if ($event_end_weekday > 6); my $days_before_week_start = 0; my $days_after_week_end = 0; $debug_info .= "week $l1 event $event_id event_date_array[6]: $event_date_array[6]\n" if ($debug); $debug_info .= "week $l1 event $event_id event_end_date_array[6]: $event_end_date_array[6]\n" if ($debug); $debug_info .= "week $l1 event $event_id current_calendar{week_start_day}: $current_calendar{week_start_day}\n" if ($debug); $debug_info .= "week $l1 event $event_id event_start_weekday: $event_start_weekday event_end_weekday: $event_end_weekday\n" if ($debug); # the event might fall completely within the week boundary, or it # might overlap event begins or ends outside the week boundaries # (there are four possible cases): if ($event{start} < $week_start_timestamp && $event{end} > $week_end_timestamp) { # the event both starts and ends outside this week $debug_info .= "case 0\n" if ($debug); $week_events{$l1}{$event{id}}{start_weekday} = 0; $week_events{$l1}{$event{id}}{length} = 7; } elsif ($event{start} < $week_start_timestamp) { # the event starts before this week and ends within it $debug_info .= "case 1\n" if ($debug); #$days_before_week_start = int(($week_start_timestamp - $event{start})/86400); $week_events{$l1}{$event{id}}{start_weekday} = 0; #$week_events{$l1}{$event{id}}{length} = $event{days} - $days_before_week_start; $week_events{$l1}{$event{id}}{length} = $event_end_weekday+1; $debug_info .= "week $l1 event $event_id week_start_timestamp: $week_start_timestamp start: $event{start} start_wkday: $week_events{$l1}{$event{id}}{start_weekday} length: $week_events{$l1}{$event{id}}{length}\n" if ($debug); } elsif ($event{end} > $week_end_timestamp) { # the event starts within this week and ends after it $debug_info .= "case 2\n" if ($debug); $week_events{$l1}{$event{id}}{start_weekday} = $event_start_weekday; $week_events{$l1}{$event{id}}{length} = 7-$event_start_weekday; if ($debug == 1) { foreach $debug_event_id (@debug_events) { next if ($debug_event_id ne $event{id}); $debug_info .= "event $debug_event_id days: $events{$debug_event_id}{days} event_start_weekday: $event_start_weekday event_end_weekday: $event_end_weekday length: $week_events{$l1}{$debug_event_id}{length}\n"; } } } else { #the event begins and ends within the week $debug_info .= "case 3\n" if ($debug); $week_events{$l1}{$event{id}}{length} = $event{days}; $week_events{$l1}{$event{id}}{start_weekday} = $event_start_weekday; } if ($week_events{$l1}{$event{id}}{start_weekday} < 0) { $week_events{$l1}{$event{id}}{start_weekday} += 7; } elsif ($week_events{$l1}{$event{id}}{start_weekday} > 6) { $week_events{$l1}{$event{id}}{start_weekday} -= 7; } $week_events{$l1}{$event{id}}{length} = 7 if ($week_events{$l1}{$event{id}}{length} > 7); $debug_info .= "week $l1 event $event_id event_start_weekday: $week_events{$l1}{$event{id}}{start_weekday} length: $week_events{$l1}{$event{id}}{length}\n\n" if ($debug); } } $temp_debug_info = ""; $cal_date += 604800; # each day has at least two slots (the date, and a blank box beneath it) for ($l2=0;$l2<7;$l2++) { $week_slots{$l1}{$l2}{0}{width}=1; $week_slots{$l1}{$l2}{0}{depth}=1; $week_slots{$l1}{$l2}{1}{width}=1; $week_slots{$l1}{$l2}{1}{depth}=1; } if ($debug == 1) { foreach $debug_event_id (@debug_events) { $debug_info .= "event $debug_event_id length: $week_events{$l1}{$debug_event_id}{length} start: $events{$debug_event_id}{start}\n"; } } #order the week_events #fill in the %slots data structure: # $week_slots{week_index}{day_index}{slot_index} # $width = colspan # $depth = rowspan # $spacer = 1 if spacer slot. # @ids = event ids my $max_week_needed_slots = 0; my %max_day_needed_slots; # hey man, that's a sharp-lookin' sort you got there foreach $week_event_id (sort { return $week_events{$l1}{$b}{length} <=> $week_events{$l1}{$a}{length} if ($week_events{$l1}{$b}{length} != $week_events{$l1}{$a}{length}); return $events{$a}{title} cmp $events{$b}{title} if ($events{$b}{all_day_event} ne "" && $events{$a}{all_day_event} ne ""); return $events{$b}{all_day_event} cmp $events{$a}{all_day_event} if ($events{$b}{all_day_event} ne "" || $events{$a}{all_day_event} ne ""); return $events{$a}{start} <=> $events{$b}{start}; } keys %{$week_events{$l1}}) { #$debug = 0; #$debug = 1 if (&contains(\@debug_events, $week_event_id)); #$debug_info .= "week $l1 week_event $week_event_id length: $week_events{$l1}{$week_event_id}{length} start: $events{$week_event_id}{start}\n" if ($debug); $empty_slot = 0; #starting at 1 leaves a row of empty slots (row 0), where the calendar dates will go. for ($l4=1; $empty_slot != 1; $l4++) { $empty_slot = 1; #check each day of the week_event, to make sure the slot is empty for ($l2=0; $l2<$week_events{$l1}{$week_event_id}{length}; $l2++) { $day_index=$l2+$week_events{$l1}{$week_event_id}{start_weekday}; if (scalar @{$week_slots{$l1}{$day_index}{$l4}{ids}} > 0) { $empty_slot = 0; } } $slot_index=$l4; } #$debug_info .= "event $week_event_id start_wkday $week_events{$l1}{$week_event_id}{start_weekday} slot $slot_index length $week_events{$l1}{$week_event_id}{length}\n" if ($debug); #fill up $week_slots with the new event (extend horizontally) for ($l2=0; $l2<$week_events{$l1}{$week_event_id}{length}; $l2++) { #$slots_in_row{$l1}{$slot_index}++; $day_index = $l2+$week_events{$l1}{$week_event_id}{start_weekday}; push @{$week_slots{$l1}{$day_index}{$slot_index}{ids}}, $week_event_id; if ($l2==0) { # first slot gets the width $week_slots{$l1}{$day_index}{$slot_index}{width} = $week_events{$l1}{$week_event_id}{length};} else { # other slots get 0 for length (they get absorbed later) $week_slots{$l1}{$day_index}{$slot_index}{width} = 0;} $week_slots{$l1}{$day_index}{$slot_index}{depth} = 1; } #keep track of the maximum number of slots each week has if ($slot_index > $week_max_slots{$l1}) { $week_max_slots{$l1} = $slot_index; $max_day_events{$l1} = $slot_index; } } # give all blank slots width and depth of 1 for ($l2=0;$l2<7;$l2++) { for ($l3=1;$l3<$week_max_slots{$l1}+1;$l3++) { # for each slot if ($week_slots{$l1}{$l2}{$l3}{depth} eq "" || $week_slots{$l1}{$l2}{$l3}{width} eq "") { $week_slots{$l1}{$l2}{$l3}{width}=1; $week_slots{$l1}{$l2}{$l3}{depth}=1; } } } my $total_spacers=0; # insert spacer slots below multi-day events. for ($l3=1;$l3<$week_max_slots{$l1}+1;$l3++) { # for each slot my $inserted_spacers=0; for ($l2=0;$l2<7;$l2++) { if ($week_slots{$l1}{$l2}{$l3}{width} > 1) { #multi-day event, add spacers beneath # create spacer row if ($inserted_spacers == 0) { # move everything else down and increment the number of rows $week_max_slots{$l1}++; for ($l4=0;$l4<7;$l4++) { # insert blank slots for ($l5=$week_max_slots{$l1};$l5>$l3+1;$l5--) { # count backwards $week_slots{$l1}{$l4}{$l5} = &deep_copy($week_slots{$l1}{$l4}{$l5-1}); } $week_slots{$l1}{$l4}{$l3+1}{width} = 1; $week_slots{$l1}{$l4}{$l3+1}{depth} = 1; $week_slots{$l1}{$l4}{$l3+1}{spacer} = 0; $week_slots{$l1}{$l4}{$l3+1}{ids} = ""; } $inserted_spacers=1 } # insert spacers into previously created row. for ($l4=$l2;$l4<$l2+$week_slots{$l1}{$l2}{$l3}{width};$l4++) { $week_slots{$l1}{$l4}{$l3+1}{spacer}=1; #$debug_info .= "inserted spacer into row: ".($l3+1).", column $l4, event ".($week_slots{$l1}{$l4}{$l3}{ids}[0])."\n"; $total_spacers++; } #$debug_info .= "week $l1 day 0, slot 3: ".$week_slots{$l1}{0}{3}{ids}[0]."\n"; } } } #if ($l1 == 3) { #$debug_info .= "$total_spacers spacers inserted for week $l1.\n"; #$debug_info .= "week $l1 day 0, slot 3: ".$week_slots{$l1}{0}{3}{ids}[0]."\n"; #} #$debug_info .= "week $l1, event slots in row 1: $slots_in_row{$l1}{1}\n"; # calculate slots in each row: for ($l3=1;$l3<$week_max_slots{$l1};$l3++) { # for each slot if ((scalar @{$week_slots{$l1}{$l2}{$l3}{ids}}) > 0 ) { $slots_in_row{$l1}{$l3} += $week_slots{$l1}{$l2}{$l3}{width}; #$debug_info .= "event in row $l1. Incrementing slots_in_row {$l1} {$l3} to $slots_in_row{$l1}{$l3}\n"; } if ($week_slots{$l1}{$l2}{$l3}{spacer} == 1) { $slots_in_row{$l1}{$l3}++; #$debug_info .= "spacer in row $l1. Incrementing slots_in_row {$l1} {$l3} to $slots_in_row{$l1}{$l3}\n"; } } %max_day_needed_slots = (0=>$week_max_slots{$l1},1=>$week_max_slots{$l1},2=>$week_max_slots{$l1},3=>$week_max_slots{$l1},4=>$week_max_slots{$l1},5=>$week_max_slots{$l1},6=>$week_max_slots{$l1}); #$slots_in_row{$l1}{$slot_index}++; # extend event slots vertically. for ($l2=0;$l2<7;$l2++) { # for each day of the week for ($l3=1;$l3<$week_max_slots{$l1};$l3++) { # for each slot if (scalar @{$week_slots{$l1}{$l2}{$l3}{ids}} > 0 && $week_slots{$l1}{$l2}{$l3}{width} > 0) { # if this slot begins an event my $start_slot = $l3+1; for ($l4=$start_slot; $l4<$week_max_slots{$l1}+1; $l4++) { #$debug_info .= "checking slot $l4 below event slot ($l2, $l3)\n"; #if ($week_slots{$l1}{$l2}{$l4}{width} == 0) # {next;} if (scalar @{$week_slots{$l1}{$l2}{$l4}{ids}} > 0 && $week_slots{$l1}{$l2}{$l4}{width} eq $week_slots{$l1}{$l2}{$l3}{width}) { # another event below this one, with the same width. if ($l1 eq "3" && $debug) { $debug_info .= "week $l1 slot ($l2, $l3) extended vertically because of another event below with the same width.\n";} #$debug_info .= "week $l1 slot ($l2, $l3) extended vertically because of another event below with the same width.\n"; #$debug_info .= "same-width event slot below week $l1, slot ($l2, $l3)\n"; $week_slots{$l1}{$l2}{$l4}{width}=0; $week_slots{$l1}{$l2}{$l4}{depth}=0; $week_slots{$l1}{$l2}{$l3}{depth}++; push @{$week_slots{$l1}{$l2}{$l3}{ids}}, @{$week_slots{$l1}{$l2}{$l4}{ids}}; $max_day_needed_slots{$l2}--; #$slots_in_row{$l1}{$l4}--; } elsif ($week_slots{$l1}{$l2}{$l3}{width} == 1 && (scalar @{$week_slots{$l1}{$l2}{$l4}{ids}}) == 0 && $week_slots{$l1}{$l2}{$l4}{spacer} == 0) { # blank slot below 1-slot wide event slot if ($l1 eq "3" && $debug) { $debug_info .= "week $l1 slot ($l2, $l3) extended vertically because of a blank slot below.\n";} #$debug_info .= "week $l1 slot ($l2, $l3) extended vertically because of a blank slot below.\n"; #$debug_info .= "week $l1 slot ($l2, $l4) # ids:".scalar @{$week_slots{$l1}{$l2}{$l4}{ids}}."\n"; #$debug_info .= "blank slot below week $l1, slot ($l2, $l3)\n"; $week_slots{$l1}{$l2}{$l4}{width}=0; $week_slots{$l1}{$l2}{$l4}{depth}=0; $week_slots{$l1}{$l2}{$l3}{depth}++; $max_day_needed_slots{$l2}--; #$debug_info .= "week $l1, slot ($l2, $l3) depth: $week_slots{$l1}{$l2}{$l3}{depth} \n"; } else { $debug_info .= "week $l1 slot ($l2, $l4) occupied. Finished attempting to extend slot $l3\n" if ($debug); last; } } } } } #$debug_info .= "week $l1, event slots in row 1: $slots_in_row{$l1}{1}\n"; # extend blank slots vertically into other blank slots. for ($l2=0;$l2<7;$l2++) { # for each day of the week for ($l3=1;$l3<$week_max_slots{$l1};$l3++) { # for each slot next if (scalar @{$week_slots{$l1}{$l2}{$l3}{ids}} > 0); if ($week_slots{$l1}{$l2}{$l3}{width} > 0 && $week_slots{$l1}{$l2}{$l3}{spacer} == 0) { # if it's blank (but not a spacer) my $start_slot = $l3+1; for ($l4=$start_slot; $l4<$week_max_slots{$l1}+1; $l4++) { if ($week_slots{$l1}{$l2}{$l4}{width} == 1 && $week_slots{$l1}{$l2}{$l4}{spacer} == 0) { #$debug_info .= "blank slot below week $l1, slot ($l2, $l3)\n" if ($debug); $week_slots{$l1}{$l2}{$l4}{width}=0; $week_slots{$l1}{$l2}{$l4}{depth}=0; $week_slots{$l1}{$l2}{$l3}{depth}++; $max_day_needed_slots{$l2}--; } else { last; } } } } } #$debug_info .= "week $l1, event slots in row 1: $slots_in_row{$l1}{1}\n"; #$debug_info .= "week $l1 slot (4, 1) depth: $week_slots{$l1}{4}{1}{depth}\n"; # yet another pass. trim vertical depth and re-calculate max_slots # calculate trim my $trim = 0; #$debug_info .= "\n week $l1 max slots: $week_max_slots{$l1}\n"; #$debug_info .= "\nweek $l1, max_day_needed_slots: $max_day_needed_slots{0} $max_day_needed_slots{1} $max_day_needed_slots{2} $max_day_needed_slots{3} $max_day_needed_slots{4} $max_day_needed_slots{5} $max_day_needed_slots{6} $max_day_needed_slots{7}\n" if ($debug); $max_week_needed_slots = max(values %max_day_needed_slots); #$debug_info .= "week $l1 max_week_needed_slots: $max_week_needed_slots\n"; #if ($max_day_needed_slots >$max_week_needed_slots) # {$max_week_needed_slots = $max_day_needed_slots;} #$debug_info .= "max needed slots for week $l1, $max_week_needed_slots\n"; my $trim = $week_max_slots{$l1} - $max_week_needed_slots; # apply trim #$debug_info .= "trim for week $l1, $trim\n"; for ($l2=0;$l2<7;$l2++) { # for each day of the week for ($l3=$week_max_slots{$l1};$l3>0;$l3--) { # for each slot, counting backwards (upwards) if ($week_slots{$l1}{$l2}{$l3}{depth} > 0) { # blank or non-blank, with depth > 0 #$debug_info .= "trimming week $l1, slot ($l2, $l3) by $trim\n"; $week_slots{$l1}{$l2}{$l3}{depth} = $week_slots{$l1}{$l2}{$l3}{depth} - $trim; last; } } } $week_max_slots{$l1} = $week_max_slots{$l1} - $trim; } # repeat for next week # print day names if ($cal_month_idx == 0 || $show_month_breaks) { my @lowercase_day_names; foreach $day_name (@day_names) { push @lowercase_day_names, lc $day_name; } $return_text .=<<p1; <table class="calendar" summary=""> <tr> <td class="day_names $lowercase_day_names[0]">$weekday_sequence[0]</td> <td class="day_names $lowercase_day_names[1]">$weekday_sequence[1]</td> <td class="day_names $lowercase_day_names[2]">$weekday_sequence[2]</td> <td class="day_names $lowercase_day_names[3]">$weekday_sequence[3]</td> <td class="day_names $lowercase_day_names[4]">$weekday_sequence[4]</td> <td class="day_names $lowercase_day_names[5]">$weekday_sequence[5]</td> <td class="day_names $lowercase_day_names[6]">$weekday_sequence[6]</td> </tr> p1 } #cal_date keeps track of the date (in timestamp format) #as the calendar loop iterates through each day on the calendar page $cal_date = $cal_start_date; @cal_date_array = gmtime $cal_date; #locked and loaded, data structures assembled--now it's time to kick it, calendar-style. for ($l1=0;$cal_date_array[4] != $next_month; $l1++) { #each calendar has 5 or 6 weeks my $last_week=0; my $timestamp_next_week = $cal_date+604800; my @timestamp_next_week_array = gmtime $timestamp_next_week; $last_week = 1 if ($current_month != $timestamp_next_week_array[4]); my $week_date_index = $cal_date; # draw the table! for ($l3=0;$l3<$week_max_slots{$l1}+1;$l3++) { $return_text .="<tr id=\"week_$cal_date\">"; $week_date_index = $cal_date; for ($l2=0;$l2<7;$l2++) { # 7 days / week @cal_date_array = gmtime $week_date_index; my $td_class = "day ".lc($day_names[$l2]); # display date numbers differently, depending on whether they are in the current month or not my $cal_month_name = ""; if ($show_month_breaks || $cal_num_months == 1) { $td_class .= " other_month" if ($cal_date_array[4] != $current_month); } else { $td_class .= " other_month_multi" if ($use_other_month[$cal_date_array[4] % 2]); $cal_month_name = $months[$cal_date_array[4]] if ($cal_date_array[3] == 1); } #if ($l2 == $week_events{$l1}{$week_slots{$l1}{$l2}{$l3}{id}}{start_weekday} && $week_events{$l1}{$week_slots{$l1}{$l2}{$l3}{id}}{start_weekday} ne "") if ($l3 == 0) { #if it's the top blank slot, put the date in there. $return_text .=<<p1; <td class="$td_class" date="$week_date_index"> <div class="date">$cal_date_array[3] $cal_month_name</div> </td> p1 } elsif ($week_slots{$l1}{$l2}{$l3}{spacer} != 0) { # spacer slot my $spacer_class = "spacer"; if ($l3 == $week_max_slots{$l1}-1) { $spacer_class .= " bottom"; } $return_text .=<<p1; <td class="$td_class $spacer_class" style="$td_style" date="$week_date_index" colspan=1 rowspan=1> </td> p1 } elsif ($week_slots{$l1}{$l2}{$l3}{width} != 0) { # slot containing events $num_cols = $week_slots{$l1}{$l2}{$l3}{width}; $num_rows = $week_slots{$l1}{$l2}{$l3}{depth}; if (scalar @{$week_slots{$l1}{$l2}{$l3}{ids}} > 0) { $td_style="border-top-width:0px;border-bottom-width:0px;"; #($l2,$l3) $num_cols\lx$num_rows $return_text .=<<p1; <td class="$td_class" style="$td_style" date="$week_date_index" colspan="$num_cols" rowspan="$num_rows"> p1 foreach $event_id (@{$week_slots{$l1}{$l2}{$l3}{ids}}) { my $multi_day_event = ($week_slots{$l1}{$l2}{$l3}{width} > 1) ? 1:0; my $background_event = (&contains($events{$event_id}{cal_ids}, $current_calendar{id})) ? 0:1; $return_text .= &display_calendar_event($event_id, $multi_day_event, $background_event) } $return_text .=<<p1; </td> p1 } elsif ($week_slots{$l1}{$l2}{$l3}{ids} eq "" && $week_slots{$l1}{$l2}{$l3}{width} > 0) { # blank slot $td_style="border-top-width:0px;border-bottom-width:0px;"; #($l2,$l3) blank $num_cols\lx$num_rows $return_text .=<<p1; <td class="$td_class" style="$td_style" colspan=$num_cols rowspan=$num_rows date="$week_date_index"> </td> p1 } } $week_date_index += 86400; } # next day (first row) #right border $return_text .=<<p1; </tr> p1 } # event slot index # this little trick is the cat's pajamas. It's another row of # table cells that cause each calendar day to come down a little # bit below the lowest event. It makes the calendar look sharp. # Also, if the week has a small number of events, we expand the height of the bottom cell. # This makes all the calendar cells look square, which is the bee's knees. my $bottom_height_style = ""; if ($max_day_events{$l1} < 2) { my $height = (4-$max_day_events{$l1}) . "em"; # this algorithm was developed by guess & check #my $height = "100px"; # this algorithm was developed by guess & check $bottom_height_style = "line-height:$height;"; } $tr_class .= " last_week" if ( $last_week == 1 ); $return_text .=<<p1; <tr style="$bottom_height_style" class="$tr_class"> p1 $week_date_index = $cal_date; for ($l2=0;$l2<7;$l2++) { #each week has 7 days(!) my $td_class = ""; @cal_date_array = gmtime $week_date_index; $td_class .= "day ".lc($day_names[$l2])." cell_bottom"; $td_class .= " today" if ($cal_date_array[4] == $rightnow_month && $cal_date_array[3] == $rightnow_mday && $cal_date_array[5]+1900 == $rightnow_year); if ($show_month_breaks || $cal_num_months == 1) { $td_class .= " other_month" if ($cal_date_array[4] != $current_month); } else { $td_class .= " other_month_multi" if ($use_other_month[$cal_date_array[4] % 2]); } $return_text .=<<p1; <td class="$td_class" style="line-height:5px;border-top-width:0px;$bottom_height_style" date="$week_date_index"> </td> p1 $week_date_index+=86400; } $return_text .=<<p1; </tr> p1 $cal_date+=604800; @cal_date_array = gmtime $cal_date; } #close table tag for each month or only for last month on continuous_multimonth mode if ($show_month_breaks || $last_cal_month) { $return_text .=<<p1; </table> <br style="page-break-after:always;"/> p1 } #increment to the next month--the method used #here is the most painless way of making #this work the right way in all cases. $current_month++; $cal_month_idx++; if ($current_month == 12) { $current_month=0; $current_year++; } } return $return_text; } #********************end render_calendar subroutine********************** sub background_event_colorize { my ($event_id, $calendar_id) = @_; my %event = %{$events{$event_id}}; my %calendar = %{$calendars{$calendar_id}}; my $event_bgcolor = $event{bgcolor}; if ($calendars{$event{cal_ids}[0]}{calendar_events_color} ne "") { $event_bgcolor = $calendars{$event{cal_ids}[0]}{calendar_events_color}; } if ($calendar{background_events_display_style} eq "single_color") { $event_bgcolor = $calendar{background_events_color}; } return $event_bgcolor; } sub display_calendar_event { my ($event_id, $multi_day, $background_event) = @_; my %event = %{$events{$event_id}}; my $results = ""; my $event_bgcolor = $event{bgcolor}; # force white color if the background is dark my $textcolor_style = ""; my $r = hex substr $event_bgcolor,1,2; my $g = hex substr $event_bgcolor,3,2; my $b = hex substr $event_bgcolor,5,2; my $bright = ($r*299+$g*587+$b*114)/1000; $textcolor_style = "color:#fff" if ($bright < 128); $event_bgcolor = $current_calendar{calendar_events_color} if ($current_calendar{calendar_events_color} ne ""); my $event_box_class = "event_box"; # handle link my $event_link = "javascript:display_event('$event_id');"; $event_link = "$event{details}" if ($event{details_url} eq "1"); if ($background_event) { $event_box_class .= " background"; $event_bgcolor = &background_event_colorize($event_id, $current_calendar{id}); } my $series_text = ($event{series_id} eq "") ? "" : " series_id=\"$event{series_id}\""; # handle icon my $icon_text = ""; my $unit_icon_text = ""; if ($event{unit_number} ne "") { $unit_icon_text = $event{unit_number}." "; $unit_icon_text =~ s/(\d)/<img src="$graphics_url\/unit_number_patch_$1_16x10.gif" border="0" alt=\"\" vspace=0 hspace=0 style="vertical-align:middle;">/g; } my $link_style = ""; if ($event{icon} ne "blank" && $event{icon} ne "") { $icon_text = <<p1; <img align="bottom" src = "$icons_url/$event{icon}_16x16.gif" style="margin-left:-22px;margin-right:5px;margin-bottom:-5px;" alt=""> p1 chomp $icon_text; $link_style = "padding-left:29px"; } my $fade_style = ""; if ($calendar{background_events_display_style} eq "single_color") { $fade_style = "opacity:". 1- ( $calendar{background_events_fade_factor} / 100 ); } if ($multi_day == 0) { my $event_time = ""; if ($event{all_day_event} ne "1") { $event_time = &nice_time_range_format($event{start}, $event{end}); $event_time = "<span class=\"event_time\" style=\"$textcolor_style;\"> $event_time </span><br/>"; } my $temp_item_text = $calendar_item_template; $temp_item_text =~ s/###icon###/$icon_text$unit_icon_text/g; $temp_item_text =~ s/###title###/$event{title}/g; $temp_item_text =~ s/###id###/$event{id}/g; $temp_item_text =~ s/###time###/$event_time/g; my $calendar_title = $calendars{$event{cal_ids}[0]}{title}; $temp_item_text =~ s/###calendar title###/$calendar_title/g; $results .=<<p1; <a href="$event_link" class="$event_box_class" style="display:block;text-align:left;$link_style$fade_style;background-color:$event_bgcolor;$textcolor_style;cursor:pointer;cursor:hand;" event_id="$event_id"$series_text> $temp_item_text</a> p1 } else { # multi-day-event # handle the case where an event is < 24 hours and crosses midnight. my $nudge_edge=""; my $event_time = ""; if ($event{all_day_event} ne "1") { if ($event{end} - $event{start} < 86400) { my $offset = 25; my $width = 50; $nudge_edge = "width:$width%;position:relative;left:$offset%;"; } $event_time = &nice_time_range_format($event{start}, $event{end}); $event_time = "<span class=\"event_time\" style=\"$textcolor_style;\"> $event_time </span>"; } if ($event{icon} ne "blank" && $event{icon} ne "") { $icon_text = <<p1; <img src="$icons_url/$event{icon}_16x16.gif" style="vertical-align:middle;margin-right:5px;" alt=""> p1 chomp $icon_text; } my $temp_item_text = $calendar_item_template; $temp_item_text =~ s/###icon###/$icon_text$unit_icon_text/g; $temp_item_text =~ s/###title###/$event{title}/g; $temp_item_text =~ s/###time###/$event_time/g; my $calendar_title = $calendars{$event{cal_ids}[0]}{title}; $temp_item_text =~ s/###calendar title###/$calendar_title/g; $results .=<<p1; <a href="$event_link" class="$event_box_class" style="display:block;text-align:center;background-color:$event_bgcolor;$textcolor_style;cursor:pointer;cursor:hand;$nudge_edge" event_id="$event_id"$series_text> $temp_item_text</a> p1 } } sub display_list_event { my ($event_id, $background_event, $sameday) = @_; my %event = %{$events{$event_id}}; my $results=""; @event_start_timestamp_array = gmtime $event{start}; my $date_string; my $weekday_string; if ($event{days} == 1) { #single-day event $date_string="$months_abv[$event_start_timestamp_array[4]] $event_start_timestamp_array[3]"; $weekday_string = $day_names_abv[$event_start_timestamp_array[6]] } else { #multi-day event @event_end_timestamp_array = gmtime $event{end}; if ($event_start_timestamp_array[4] eq $event_end_timestamp_array[4]) { $date_string="$months_abv[$event_start_timestamp_array[4]] $event_start_timestamp_array[3]-$event_end_timestamp_array[3]"; } else { $date_string="$months_abv[$event_start_timestamp_array[4]] $event_start_timestamp_array[3] - $months_abv[$event_end_timestamp_array[4]] $event_end_timestamp_array[3]"; } $weekday_string = "$day_names_abv[$event_start_timestamp_array[6]]-$day_names_abv[$event_end_timestamp_array[6]]"; } # weekday abbreviations my $weekday_abv_string = $weekday_string; for ($l1=0;$l1<scalar @day_names;$l1++) { $weekday_abv_string =~ s/$day_names[$l1]/$day_names_abv[$l1]/g; } my $icon_text=""; my $unit_icon_text=""; if ($event{unit_number} ne "") { $icon_text = $event{unit_number}; $icon_text =~ s/(\d)/<img src="$graphics_url\/unit_number_patch_$1_16x10.gif" style=\"position:relative;top:5px;\" alt=\"\">/g; } if ($event{icon} eq "blank" || $event{icon} eq "") { $icon_text .= "$unit_icon_text"; } else { $icon_text .= "$unit_icon_text<img src = \"$icons_url/$event{icon}_16x16.gif\" style=\"position:relative;top:5px;\" alt=\"\">"; } my $event_bgcolor = $event{bgcolor}; if ($calendars{$event{cal_ids}[0]}{calendar_events_color} ne "") { $event_bgcolor = $calendars{$event{cal_ids}[0]}{calendar_events_color};} if ($background_event eq "1") { $event_bgcolor = &background_event_colorize($event_id, $current_calendar{id}); } # event time my $event_time = ""; if ($event{all_day_event} ne "1") { $event_time = &nice_time_range_format($event{start}, $event{end}); $event_time = "<span class=\"event_time\">$event_time</span>"; } my $event_link = "javascript:display_event('$event{id}');"; if ($event{details_url} eq "1") { $event_link = "$event{details}";} my $margin_top="7px"; $margin_top = "0" if ($sameday); my $temp_item_text = $list_item_template; $temp_item_text =~ s/###variable_margin###/$margin_top/g; $temp_item_text =~ s/###id###/$event_id/g; $temp_item_text =~ s/###bgcolor###/$event_bgcolor/g; $temp_item_text =~ s/###link###/$event_link/g; $temp_item_text =~ s/###details###/$event{details}/g; $temp_item_text =~ s/###icon###/$icon_text/g; $temp_item_text =~ s/###title###/$event{title}/g; $temp_item_text =~ s/###date###/$date_string/g; $temp_item_text =~ s/###weekday###/$weekday_string/g; $temp_item_text =~ s/###weekday_abv###/$weekday_abv_string/g; $temp_item_text =~ s/###time###/$event_time/g; $results .= $temp_item_text; $no_results .=<<p1; <li style="padding:0;margin:0;margin-top:$margin_top;"$event_context_menu_text> <span class="small_note" style="margin:0;border:0;vertical-align:middle;width:7em;white-space:nowrap;text-align:right;cursor:pointer;cursor:hand;" onclick="display_event('$event_id')" > $date_string </span> <a class="event_box" style="text-align:left;white-space:nowrap;background-color:$event_bgcolor;" href="$event_link"> $icon_text $event_time $event{title}</a> </li> p1 return $results; } sub render_list { my $return_text = ""; ($start_month, $start_year, $end_month, $end_year) = @_; #calculate where to start and end the list #format for timegm: timegm($sec,$min,$hour,$mday,$mon,$year); my $list_start_timestamp = timegm(0,0,0,1,$start_month,$start_year); my $list_end_timestamp = &find_end_of_month($end_month, $end_year); # loop through all the events. #Create an array of events which fall # within the current list view dates my @selected_cal_events; #and a funky data structure for the background calendars # each element of this hash will be an array. my $shared_cal_events={}; #empty hash foreach $event_id (keys %events) { if (&time_overlap($events{$event_id}{start},$events{$event_id}{end},$list_start_timestamp,$list_end_timestamp)) { my $event_in_current_calendar = 0; foreach $temp_cal_id (@{$events{$event_id}{cal_ids}}) { if ($temp_cal_id eq $current_cal_id) { push @selected_cal_events, $event_id;} foreach $background_cal_id (keys %{$current_calendar{local_background_calendars}}) { if ($temp_cal_id eq $background_cal_id) { if ($current_calendar{list_background_calendars_together} eq "yes") { push @selected_cal_events, $event_id; } else { push @{$shared_cal_events{$background_cal_id}}, $event_id if (!&contains(\@selected_cal_events, $event_id)); } } } } } } # initialize loop variables $current_month = $start_month; $current_year = $start_year; $return_text .=<<p1; p1 while ($current_year < $end_year || ($current_year == $end_year && $current_month <= $end_month)) { my $current_month_start_timestamp = timegm(0,0,0,1,$current_month,$current_year); my $current_month_end_timestamp = &find_end_of_month($current_month, $current_year); $return_text .=<<p1; <div class="list_month_box"> p1 if ($cal_num_months> 1) { $return_text .=<<p1; <h1> $months[$current_month] $current_year </h1> p1 } if ($current_calendar{list_background_calendars_together} eq "yes") { $return_text .=<<p1; <ul class="list_cal_box" style="list-style-type:none;float:left;text-align:left;"> <h4>$calendars{$current_cal_id}{title}</h4> p1 #display events for selected calendar my $previous_event_id; foreach $event_id (sort {$events{$a}{start} <=> $events{$b}{start}} @selected_cal_events) { my %event = %{$events{$event_id}}; if (&time_overlap($event{start},$event{end},$current_month_start_timestamp, $current_month_end_timestamp)) { my $background_event = ""; $background_event = "1" if ($event{cal_ids}[0] ne $current_cal_id); my @temp1 = gmtime($event{start}); my @temp2 = gmtime($events{$previous_event_id}{start}); my $sameday = ""; $sameday = "1" if ($temp1[3] == $temp2[3]); $return_text .= &display_list_event($event_id, $background_event, $sameday); $previous_event_id = $event_id; } } $return_text .=<<p1; </ul> p1 } else { $return_text .=<<p1; <ul class="list_cal_box" style="list-style-type:none;float:left;text-align:left;"> <h4>$calendars{$current_cal_id}{title}</h4> p1 #display events for selected calendar my $previous_event_id; foreach $event_id (sort {$events{$a}{start} <=> $events{$b}{start}} @selected_cal_events) { my %event = %{$events{$event_id}}; if (&time_overlap($event{start},$event{end},$current_month_start_timestamp, $current_month_end_timestamp)) { my @temp1 = gmtime($event{start}); my @temp2 = gmtime($events{$previous_event_id}{start}); my $sameday = ""; $sameday = "1" if ($temp1[3] == $temp2[3]); $return_text .= &display_list_event($event_id,0,$sameday); $previous_event_id = $event_id; } } $return_text .=<<p1; </ul> p1 foreach $background_cal_id (keys %{$current_calendar{local_background_calendars}}) { $return_text .=<<p1; <ul class="list_cal_box background" style="list-style-type:none;float:left;text-align:left;"> <li style="text-align:center;font-weight:bold;">$calendars{$background_cal_id}{title}</li> p1 #list events for that calendar my $previous_event_id; foreach $event_id (sort {$events{$a}{start} <=> $events{$b}{start}} @{$shared_cal_events{$background_cal_id}}) { %event = %{$events{$event_id}}; if (&time_overlap($events{$event_id}{start},$events{$event_id}{end},$current_month_start_timestamp, $current_month_end_timestamp)) { my @temp1 = gmtime($event{start}); my @temp2 = gmtime($events{$previous_event_id}{start}); my $sameday = ""; $sameday = "1" if ($temp1[3] == $temp2[3]); $return_text .= &display_list_event($event_id,1,$sameday); $previous_event_id = $event_id; } } $return_text .=<<p1; </ul> <br style="clear:both;"/> <!--needed because IE sucks--> p1 } } $return_text .=<<p1; <br style="clear:both;"/> <!-- because IE sucks--> <br style="clear:both;"/> <!-- because IE sucks--> </div> <br style="clear:both;"/> <!-- because IE sucks--> <br style="clear:both;"/> <!-- because IE sucks--> p1 #increment to the next month--the method used #here is the most painless way of making #this work the right way in all cases. $current_month +=1; if ($current_month == 12) { $current_month=0; $current_year++; } } $return_text .=<<p1; p1 return $return_text; } #********************end generate_list subroutine********************** sub generate_event_details_javascript { my ($events_start_timestamp, $events_end_timestamp) = @_; my $return_string=""; my $num_events = 0; my $num_remote_events = 0; my $event_defs=""; my $remote_event_defs=""; $index=0; #loop through the events, check to see if they fall #within the current calendar month foreach $event_id (keys %events) { my %event = %{$events{$event_id}}; if (&time_overlap($event{start},$event{end},$events_start_timestamp,$events_end_timestamp)) { if ($event_id =~ /^r/) { $num_remote_events++; $remote_event_defs .= <<p1; remote_event_details["$event_id"] = new Object; remote_event_details["$event_id"].url = "$event{remote_calendar}{url}?view_event=1&evt_id=$event{remote_event_id}"; p1 } } } $return_string .=<<p1; var remote_event_details = []; $remote_event_defs var event_details = []; $event_defs p1 return $return_string; } #********************end generate_event_details_javascript subroutine********************** sub pending_events_visible { if ( ! $options{'anonymous_events'} ) { return 0; } if ( ! $options{'sessions'} ) { return 0; } # display pending events for this calendar? if ($options{pending_events_display} eq '0' || $options{pending_events_display} eq '1' || ($options{pending_events_display} eq '2' && $logged_in_as_current_cal_user) || # logged-in user of this calendar ($options{pending_events_display} eq '3' && $logged_in_as_current_cal_user) || # logged-in user of this calendar ($options{pending_events_display} eq '3' && $logged_in_as_current_cal_admin) || # logged-in admin of this calendar $logged_in_as_root # root ) {return 1}; return undef; } sub forced_login { my $results = ""; $results .= <<p1; <form name="forced_login_form" id="update_cal_form" action="" method="POST"> <input type="hidden" name="active_tab" value="$active_tab"> <input type="hidden" name="cal_id" value="$current_cal_id"/> <div class="info_box" style="clear:both;"> <p class="cal_title" style="text-align:center;"> $lang{login} </p> <div class="leftcol" style=""> <label class="required_field" for="cal_password">$lang{cal_password}</label> </div> <div class="rightcol" style="white-space:nowrap;"> <input type="password" name="cal_password" id="cal_password" size=12 /> <span class="small_note" ><a style="vertical-align:top;" href="javascript:jQuery.planscalendar.display_help('current_password','$lang{cal_password}')">$lang{help_on_this}</a></span> </div> <div class="leftcol">   </div> <div class="rightcol" style="white-space:nowrap;"> <input type="submit" value="$lang{submit}"/> </div> <br style="clear:both;"/> </div> </form> p1 return $results; } sub generate_pending_events_area { # events & calendars pending approval, etc. my $results = ""; my $pending_events_text = ""; # display pending events for this calendar? if (&pending_events_visible()) { foreach $new_event_id (keys %new_events) { push @pending_events_to_display, $new_event_id; } $pending_events_text .= "<a id=\"pending_events_display_toggle_button\" href=\"javascript:void(0)\">"; $pending_events_text .= (scalar @pending_events_to_display)." "; if (scalar @pending_events_to_display == 1) { $pending_events_text .= $lang{pending_event1}; } else { $pending_events_text .= $lang{pending_event2}; } $pending_events_text .= "</a>"; } if ($pending_events_text ne "") { $results .= "$pending_events_text <div id=\"pending_events\">"; $results .= "</div>"; } return $results; } sub csv_file_palm { ($start_month, $start_year, $end_month, $end_year) = @_; #calculate where to start and end the list #format for timegm: timegm($sec,$min,$hour,$mday,$mon,$year); my $list_start_timestamp = timegm(0,0,0,1,$start_month,$start_year); my $list_end_timestamp = &find_end_of_month($end_month, $end_year); #@cal_month_start_date_array = gmtime $cal_month_start_date; # loop through all the events. #Create an array of events which fall # within the current list view dates my @selected_cal_events; #and a funky data structure for the background calendars # each element of this hash will be an array. my $shared_cal_events={}; #empty hash foreach $event_id (keys %events) { if (&time_overlap($events{$event_id}{start},$events{$event_id}{end},$list_start_timestamp,$list_end_timestamp)) { my $event_in_current_calendar = 0; foreach $temp_cal_id (@{$events{$event_id}{cal_ids}}) { if ($temp_cal_id eq $current_cal_id) { push @selected_cal_events, $event_id;} foreach $background_cal_id (keys %{$current_calendar{local_background_calendars}}) { if ($temp_cal_id eq $background_cal_id) { push @{$shared_cal_events{$background_cal_id}}, $event_id;} } } } } $html_output =<<p1; Cache-control: no-cache,no-store,private Content-disposition: filename="events.csv" Content-Type: text/csv; charset=$lang{charset}\n p1 #initialize loop variables #$current_timestamp = $list_start_timestamp; $current_month = $start_month; $current_year = $start_year; $html_output .=<<p1; CSV datebook: Category, Private, Description, Note, Event, Begin, End, Alarm, Advance, Advance Units, Repeat Type, Repeat Forever, Repeat End, Repeat Frequency, Repeat Day, Repeat Days, Week Start, Number of Exceptions, Exceptions p1 while ($current_year < $end_year || ($current_year == $end_year && $current_month <= $end_month)) { my $current_month_start_timestamp = timegm(0,0,0,1,$current_month,$current_year); my $current_month_end_timestamp = &find_end_of_month($current_month, $current_year); #display events for selected calendar foreach $event_id (sort {$events{$a}{start} <=> $events{$b}{start}} @selected_cal_events) { my %event = %{$events{$event_id}}; if (&time_overlap($event{start},$event{end},$current_month_start_timestamp, $current_month_end_timestamp)) { my $csv_subject = "$event{title}"; @event_start_timestamp_array = gmtime $event{start}; my $csv_start_date = ($event_start_timestamp_array[5]+1900)." ".($event_start_timestamp_array[4]+1)." ".($event_start_timestamp_array[3]); my $csv_start_time = "$event_start_timestamp_array[2]:$event_start_timestamp_array[1]"; @event_end_timestamp_array = gmtime $event{end}; my $csv_end_date = ($event_end_timestamp_array[5]+1900)." ".($event_end_timestamp_array[4]+1)." ".($event_end_timestamp_array[3]); my $csv_end_time = "$event_end_timestamp_array[2]:$event_end_timestamp_array[1]"; my $csv_description = $event{details}; $csv_description =~ s/"/""/g; $csv_description =~ s/\n/\\n/g; if ($event{days} != 1) { @event_end_timestamp_array = gmtime $event{end}; $csv_end_date = ($event_end_timestamp_array[5]+1900)." ".($event_end_timestamp_array[4]+1)." ".($event_end_timestamp_array[3]); } $html_output .=<<p1; "$calendars{$current_cal_id}{title}","0","$csv_subject","$csv_description","1","$csv_start_date $csv_start_time","$csv_end_date $csv_end_time","0","0","0","0","1","$csv_end_date","0","0","0000000","0","0","" p1 } } foreach $background_cal_id (keys %{$current_calendar{local_background_calendars}}) { #list events for that calendar foreach $event_id (sort {$events{$a}{start} <=> $events{$b}{start}} @{$shared_cal_events{$background_cal_id}}) { my %event = %{$events{$event_id}}; if (&time_overlap($events{$event_id}{start},$events{$event_id}{end},$current_month_start_timestamp, $current_month_end_timestamp)) { my $csv_subject = "$event{title}"; @event_start_timestamp_array = gmtime $event{start}; my $csv_start_date = ($event_start_timestamp_array[5]+1900)." ".($event_start_timestamp_array[4]+1)." ".($event_start_timestamp_array[3]); my $csv_start_time = "$event_start_timestamp_array[2]:$event_start_timestamp_array[1]"; @event_end_timestamp_array = gmtime $event{end}; my $csv_end_date = ($event_end_timestamp_array[5]+1900)." ".($event_end_timestamp_array[4]+1)." ".($event_end_timestamp_array[3]); my $csv_end_time = "$event_end_timestamp_array[2]:$event_end_timestamp_array[1]"; my $csv_description = $event{details}; $csv_description =~ s/"/""/g; $csv_description =~ s/\n/\\n/g; $csv_description =~ s/"/""/g; $csv_description =~ s/\n/\\n/g; if ($event{days} != 1) { @event_end_timestamp_array = gmtime $event{end}; $csv_end_date = ($event_end_timestamp_array[5]+1900)." ".($event_end_timestamp_array[4]+1)." ".($event_end_timestamp_array[3]); } $html_output .=<<p1; "$calendars{$background_cal_id}{title}","0","$csv_subject","$csv_description","1","$csv_start_date $csv_start_time","$csv_end_date $csv_end_time","0","0","0","0","1","$csv_end_date","0","0","0000000","0","0","" p1 } } } #increment to the next month--the method used #here is the most painless way of making #this work the right way in all cases. $current_month +=1; if ($current_month == 12) { $current_month=0; $current_year++; } } $html_output .= $debug_info; print $html_output; } sub csv_file { ($start_month, $start_year, $end_month, $end_year) = @_; #calculate where to start and end the list #format for timegm: timegm($sec,$min,$hour,$mday,$mon,$year); my $list_start_timestamp = timegm(0,0,0,1,$start_month,$start_year); my $list_end_timestamp = &find_end_of_month($end_month, $end_year); #@cal_month_start_date_array = gmtime $cal_month_start_date; # loop through all the events. #Create an array of events which fall # within the current list view dates my @selected_cal_events; #and a funky data structure for the background calendars # each element of this hash will be an array. my $shared_cal_events={}; #empty hash foreach $event_id (keys %events) { if (&time_overlap($events{$event_id}{start},$events{$event_id}{end},$list_start_timestamp,$list_end_timestamp)) { my $event_in_current_calendar = 0; foreach $temp_cal_id (@{$events{$event_id}{cal_ids}}) { if ($temp_cal_id eq $current_cal_id) { push @selected_cal_events, $event_id;} foreach $background_cal_id (keys %{$current_calendar{local_background_calendars}}) { if ($temp_cal_id eq $background_cal_id) { push @{$shared_cal_events{$background_cal_id}}, $event_id;} } } } } $html_output =<<p1; Cache-control: no-cache,no-store,private Content-disposition: filename="events.csv" Content-Type: text/plain; charset=$lang{charset}\n\n p1 #initialize loop variables #$current_timestamp = $list_start_timestamp; $current_month = $start_month; $current_year = $start_year; $html_output .=<<p1; "Subject","Start Date","Start Time","End Date","End Time","All day event","Reminder on/off","Reminder Date","Reminder Time","Meeting Organizer","Required Attendees","Optional Attendees","Meeting Resources","Billing Information","Categories","Description","Location","Mileage","Priority","Private","Sensitivity","Show time as" p1 while ($current_year < $end_year || ($current_year == $end_year && $current_month <= $end_month)) { my $current_month_start_timestamp = timegm(0,0,0,1,$current_month,$current_year); my $current_month_end_timestamp = &find_end_of_month($current_month, $current_year); #display events for selected calendar foreach $event_id (sort {$events{$a}{start} <=> $events{$b}{start}} @selected_cal_events) { my %event = %{$events{$event_id}}; if (&time_overlap($event{start},$event{end},$current_month_start_timestamp, $current_month_end_timestamp)) { my $csv_subject = "$event{title} ($calendars{$current_cal_id}{title})"; @event_start_timestamp_array = gmtime $event{start}; my $csv_start_date = ($event_start_timestamp_array[4]+1)."/$event_start_timestamp_array[3]/".($event_start_timestamp_array[5]+1900); my $csv_start_time = ($event_start_timestamp_array[2]).":$event_start_timestamp_array[1]:".($event_start_timestamp_array[0]); @event_end_timestamp_array = gmtime $event{end}; my $csv_end_date = ($event_end_timestamp_array[4]+1)."/$event_end_timestamp_array[3]/".($event_end_timestamp_array[5]+1900); my $csv_end_time = ($event_end_timestamp_array[2]).":$event_end_timestamp_array[1]:".($event_end_timestamp_array[0]); my $csv_description = $event{details}; $csv_description =~ s/"/""/g; $csv_description =~ s/\n/\\n/g; if ($event{days} != 1) { @event_end_timestamp_array = gmtime $event{end}; $csv_end_date = ($event_end_timestamp_array[4]+1)."/$event_end_timestamp_array[3]/".($event_end_timestamp_array[5]+1900); } $html_output .=<<p1; "$csv_subject","$csv_start_date","$csv_start_time",$csv_end_date,"$csv_end_time","True","True","$csv_start_date","12:00:00 AM",,,,,,,"$csv_description",,,"Normal","False","Normal","1" p1 } } foreach $background_cal_id (keys %{$current_calendar{local_background_calendars}}) { #list events for that calendar foreach $event_id (sort {$events{$a}{start} <=> $events{$b}{start}} @{$shared_cal_events{$background_cal_id}}) { my %event = %{$events{$event_id}}; if (&time_overlap($events{$event_id}{start},$events{$event_id}{end},$current_month_start_timestamp, $current_month_end_timestamp)) { my $csv_subject = "$event{title} ($calendars{$background_cal_id}{title})"; @event_start_timestamp_array = gmtime $event{start}; my $csv_start_date = ($event_start_timestamp_array[4]+1)."/$event_start_timestamp_array[3]/".($event_start_timestamp_array[5]+1900); my $csv_start_time = ($event_start_timestamp_array[2]).":$event_start_timestamp_array[1]:".($event_start_timestamp_array[0]); @event_end_timestamp_array = gmtime $event{end}; my $csv_end_date = ($event_end_timestamp_array[4]+1)."/$event_end_timestamp_array[3]/".($event_end_timestamp_array[5]+1900); my $csv_end_time = ($event_end_timestamp_array[2]).":$event_end_timestamp_array[1]:".($event_end_timestamp_array[0]); my $csv_description = $event{details}; $csv_description =~ s/"/""/g; $csv_description =~ s/\n/\\n/g; $csv_description =~ s/"/""/g; $csv_description =~ s/\n/\\n/g; if ($event{days} != 1) { @event_end_timestamp_array = gmtime $event{end}; $csv_end_date = ($event_end_timestamp_array[4]+1)."/$event_end_timestamp_array[3]/".($event_end_timestamp_array[5]+1900); } $html_output .=<<p1; "$csv_subject","$csv_start_date","$csv_start_time",$csv_end_date,"$csv_end_time","True","True","$csv_start_date","12:00:00 AM",,,,,,,"$csv_description",,,"Normal","False","Normal","1" p1 } } } #increment to the next month--the method used #here is the most painless way of making #this work the right way in all cases. $current_month +=1; if ($current_month == 12) { $current_month=0; $current_year++; } } $html_output .= $debug_info; print $html_output; } sub vcalendar_export_cal { ($start_month, $start_year, $end_month, $end_year) = @_; #calculate where to start and end the list #format for timegm: timegm($sec,$min,$hour,$mday,$mon,$year); my $list_start_timestamp = timegm(0,0,0,1,$start_month,$start_year); my $list_end_timestamp = &find_end_of_month($end_month, $end_year); # loop through all the events. #Create an array of events which fall # within the current list view dates my @selected_cal_events; #and a funky data structure for the background calendars # each element of this hash will be an array. my $shared_cal_events={}; #empty hash foreach $event_id (keys %events) { if (&time_overlap($events{$event_id}{start},$events{$event_id}{end},$list_start_timestamp,$list_end_timestamp)) { my $event_in_current_calendar = 0; foreach $temp_cal_id (@{$events{$event_id}{cal_ids}}) { if ($temp_cal_id eq $current_cal_id) { push @selected_cal_events, $event_id;} foreach $background_cal_id (keys %{$current_calendar{local_background_calendars}}) { if ($temp_cal_id eq $background_cal_id) { push @{$shared_cal_events{$background_cal_id}}, $event_id;} } } } } $html_output =<<p1; Cache-control: no-cache,no-store,private Content-disposition: filename="events.vcs" Content-Type: text/csv; charset=$lang{charset} BEGIN:VCALENDAR PRODID:-//Plans//EN VERSION:1.0 METHOD:PUBLISH p1 #initialize loop variables #$current_timestamp = $list_start_timestamp; $current_month = $start_month; $current_year = $start_year; while ($current_year < $end_year || ($current_year == $end_year && $current_month <= $end_month)) { my $current_month_start_timestamp = timegm(0,0,0,1,$current_month,$current_year); my $current_month_end_timestamp = &find_end_of_month($current_month, $current_year); #display events for selected calendar foreach $event_id (sort {$events{$a}{start} <=> $events{$b}{start}} @selected_cal_events) { my %event = %{$events{$event_id}}; if (&time_overlap($event{start},$event{end},$current_month_start_timestamp, $current_month_end_timestamp)) { $html_output .= &event2vcal(\%event)."\n"; } } foreach $background_cal_id (keys %{$current_calendar{local_background_calendars}}) { #list events for that calendar foreach $event_id (sort {$events{$a}{start} <=> $events{$b}{start}} @{$shared_cal_events{$background_cal_id}}) { my %event = %{$events{$event_id}}; if (&time_overlap($events{$event_id}{start},$events{$event_id}{end},$current_month_start_timestamp, $current_month_end_timestamp)) { $html_output .= &event2vcal(\%event)."\n"; } } } #increment to the next month--the method used #here is the most painless way of making #this work the right way in all cases. $current_month +=1; if ($current_month == 12) { $current_month=0; $current_year++; } } $html_output .=<<p1; END:VCALENDAR p1 $html_output .= $debug_info; print $html_output; } sub ascii_text_cal { ($start_month, $start_year, $end_month, $end_year) = @_; #calculate where to start and end the list #format for timegm: timegm($sec,$min,$hour,$mday,$mon,$year); my $list_start_timestamp = timegm(0,0,0,1,$start_month,$start_year); my $list_end_timestamp = &find_end_of_month($end_month, $end_year); # loop through all the events. #Create an array of events which fall # within the current list view dates my @selected_cal_events; #and a funky data structure for the background calendars # each element of this hash will be an array. my $shared_cal_events={}; #empty hash foreach $event_id (keys %events) { if (&time_overlap($events{$event_id}{start},$events{$event_id}{end},$list_start_timestamp,$list_end_timestamp)) { my $done = 0; foreach $temp_cal_id (@{$events{$event_id}{cal_ids}}) { if ($temp_cal_id eq $current_cal_id) { push @selected_cal_events, $event_id;} foreach $background_cal_id (keys %{$current_calendar{local_background_calendars}}) { if ($temp_cal_id eq $background_cal_id) { push @{$shared_cal_events{$background_cal_id}}, $event_id; $done = 1; last; } } if ($done == 1) {last;} } } } $html_output =<<p1; Cache-control: no-cache,no-store,private Content-Type: text/plain; charset=$lang{charset}\n\n p1 #initialize loop variables #$current_timestamp = $list_start_timestamp; $current_month = $start_month; $current_year = $start_year; while ($current_year < $end_year || ($current_year == $end_year && $current_month <= $end_month)) { my $current_month_start_timestamp = timegm(0,0,0,1,$current_month,$current_year); my $current_month_end_timestamp = &find_end_of_month($current_month,$current_year); $html_output .=<<p1; ******************************************* $months[$current_month] $current_year ******************************************* * $current_calendar{title} p1 #display events for selected calendar foreach $event_id (sort {$events{$a}{start} <=> $events{$b}{start}} @selected_cal_events) { %event = %{$events{$event_id}}; if (&time_overlap($event{start},$event{end},$current_month_start_timestamp, $current_month_end_timestamp)) { @event_start_timestamp_array = gmtime $event{start}; my $event_time = ""; if ($event{days} == 1) { #single-day event $date_string="$months_abv[$event_start_timestamp_array[4]] $event_start_timestamp_array[3] ".($event_start_timestamp_array[5]+1900); if ($event{all_day_event} ne "1") { $event_time = &nice_time_range_format($event{start}, $event{end}); $event_time = " ($event_time)"; } } else { #multi-day event $date_string = &nice_date_range_format($event{start}, $event{end}, "-")." ".($event_start_timestamp_array[5]+1900); } my $event_details = $event{details}; $event_details =~ s/\n[ \t\r\f]+/\n/g; chomp $event_details; #indent each line of the details $event_details =~ s/\n/\n /g; $html_output .=" $date_string$event_time: $event{title}\n"; if ($event_details ne "") { $html_output .= " $event_details\n"; } $html_output .= "\n"; } } foreach $background_cal_id (keys %{$current_calendar{local_background_calendars}}) { $html_output .=<<p1; * $calendars{$background_cal_id}{title} p1 #list events for that calendar foreach $event_id (sort {$events{$a}{start} <=> $events{$b}{start}} @{$shared_cal_events{$background_cal_id}}) { my %event = %{$events{$event_id}}; if (&time_overlap($event{start},$event{end},$current_month_start_timestamp, $current_month_end_timestamp)) { @event_start_timestamp_array = gmtime $event{start}; my $event_time = ""; if ($event{days} == 1) { #single-day event $date_string="$months_abv[$event_start_timestamp_array[4]] $event_start_timestamp_array[3] ".($event_start_timestamp_array[5]+1900); if ($event{all_day_event} ne "1") { $event_time = &nice_time_range_format($event{start}, $event{end}); $event_time = " ($event_time)"; } } else { #multi-day event $date_string = &nice_date_range_format($event{start}, $event{end}, "-")." ".($event_start_timestamp_array[5]+1900); } my $event_details = $event{details}; $event_details =~ s/\n[ \t\r\f]+/\n/g; chomp $event_details; #indent each line of the details $event_details =~ s/\n/\n /g; $html_output .=" $date_string$event_time: $event{title}\n"; if ($event_details ne "") { $html_output .= " $event_details\n"; } $html_output .= "\n"; } } } #increment to the next month--the method used #here is the most painless way of making #this work the right way in all cases. $current_month +=1; if ($current_month == 12) { $current_month=0; $current_year++; } } $html_output .= $debug_info; print $html_output; } #********************end ascii_text_cal subroutine********************** sub ascii_text_event { my %current_event = %{$events{$current_event_id}}; $html_output =<<p1; Cache-control: no-cache,no-store,private Content-Type: text/plain; charset=$lang{charset}\n\n p1 my @event_start_timestamp_array = gmtime $current_event{start}; my $date_string=""; my $event_time = ""; if ($current_event{days} == 1) { #single-day event $date_string="$months_abv[$event_start_timestamp_array[4]] $event_start_timestamp_array[3] ".($event_start_timestamp_array[5]+1900); if ($current_event{all_day_event} ne "1") { $event_time = &nice_time_range_format($current_event{start}, $current_event{end}); $event_time = "($event_time)"; } } else { #multi-day event $date_string = &nice_date_range_format($current_event{start}, $current_event{end}, "-")." ".($event_start_timestamp_array[5]+1900); } my $event_details = $current_event{details}; $event_details =~ s/\n\s+/\n/g; $event_details =~ s/\n{2,}/\n/g; chomp $event_details; #indent each line of the details $event_details =~ s/\n/\n /g; $html_output .=" $date_string$event_time: $current_event{title}\n"; if ($event_details ne "") { $html_output .= " $event_details\n\n"; } $html_output .= $debug_info; print $html_output; } #********************end ascii_text_event subroutine********************** sub icalendar_export_event { # only for 1 event # when exporting to outlook, plans uses the vcalendar standard. # (http://www.imc.org/rfc2445) # This standard is horribly supported by MS outlook (outlook 2000, at the # time of this writing. Outlook refuses to correctly interpret the # date-time strings in the following ways: # 1. If the date-time parameter does not specify a time (only a date), do you # think outlook sets its "all-day event" flag? Nope. It just assumes the # event occurs at 000000 hours (12 midnight). # 2. if no time zone is specified, the date-time string is supposed to be # interpreted as if it applied to the *current* timezone the user's computer # is in (according to the standard). Do you think outlook does this? Nope. # If no timezone is specified, outlook assumes the time zone is GMT. As if this # weren't enough, outlook "helpfully" adjusts the time to the user's time zone. # Depending on how far a user is from GMT, this may cause the day to change. # Since there's no way to predict what time zone a user is in, it is impossible # to compensate on the server side for outlook's stupidity. The workaround is # to generate event times for each event (even though this is misleading because # the events are all-day events), and force all the event times to 12 noon. # This puts them as far from the previous and next days as possible, giving the # least chance for outlook to screw up the day when it does its adjustment. my %current_event = %{$events{$current_event_id}}; my $ical_string = &event2ical(\%current_event); #Content-type: text/plain #Last-modified: Wed, 30 Jan 2002 22:43:12 GMT $html_output =<<p1; Content-type: text/x-vcalendar Content-disposition: filename="event.ics" BEGIN:VCALENDAR PRODID:-//Plans//EN VERSION:2.0 METHOD:PUBLISH $ical_string END:VCALENDAR p1 print $html_output; } #********************end vcalendar_export subroutine********************** sub vcalendar_export_event { # only for 1 event my %current_event = %{$events{$current_event_id}}; $vcal_string .= &event2vcal(\%current_event); #my $last_modified = &formatted_time($rightnow, "md mn yy hh:mm:ss GMT"); #Last-modified: $last_modified #Content-type: text/plain $html_output =<<p1; Content-type: text/vcs Content-disposition: filename="event.vcs" BEGIN:VCALENDAR PRODID:-//Plans//EN VERSION:1.0 METHOD:PUBLISH $vcal_string END:VCALENDAR p1 print $html_output; } #********************end vcalendar_export_event subroutine********************** sub calculate_recurring_events { my ($start_timestamp, $recurrence_parms_ref) = @_; my %recurrence_parms = %{$recurrence_parms_ref}; my @recurring_events_array = (); my @custom_months = @{$recurrence_parms{'custom_months'}}; my @timestamp_array = gmtime $start_timestamp; #my $start_timestamp = $start_timestamp+50; #calculate the weekday_in_month_count for the start timestamp # (is it the first tuesday? second saturday? This is required for # things to work right. $real_year = 1900 + $timestamp_array[5]; $temp_start_timestamp = timegm(0,0,0,1,$timestamp_array[4],$real_year); @temp_start_timestamp_array = gmtime($temp_start_timestamp); my $weekday_in_month_count = 0; #figure out what weekday of the month the start timestamp is for (;$temp_start_timestamp < $start_timestamp;$temp_start_timestamp+=86400) { @temp_start_timestamp_array = gmtime($temp_start_timestamp); if ($temp_start_timestamp_array[6] == $timestamp_array[6]) { $weekday_in_month_count++; } } #this must be done, or the week-of-month recurring dates will be hosed #@temp_array = gmtime $start_timestamp; #my $current_month = $temp_array[4]; my $last_week=0; # set the start timestamp to a day boundary. my $boundary_timestamp = timegm(0,0,0,$timestamp_array[3],$timestamp_array[4],$real_year); my $timed_event_diff = $start_timestamp - $boundary_timestamp; $start_timestamp = $boundary_timestamp; # The recurring event algorithm loops through each day in the timeframe # and tests its validity against the recurrence parameters. # These tests take the form of "assumed valid unless proven otherwise" # Doing it this way lets the various recurrence type tests operate # independently of each other, while looping only once through the timeframe for ($recur_timestamp = $start_timestamp; $recur_timestamp <= $recurrence_parms{'recur_end_timestamp'}; $recur_timestamp += 86400) { $last_week=0; my @recur_timestamp_array = gmtime $recur_timestamp; my $current_month = $recur_timestamp_array[4]; #look a week ahead, to see if the current weekday is the last one in the month my $recur_timestamp_next_week = $recur_timestamp+604800; my @recur_timestamp_next_week_array = gmtime $recur_timestamp_next_week; $last_week = 1 if ($current_month != $recur_timestamp_next_week_array[4]); $real_year = 1900 + $recur_timestamp_array[5]; $recur_timestamp_valid=1; if ($recurrence_parms{'recurrence_type'} eq "same_day_of_month") { $recur_timestamp_valid=0 if ($recur_timestamp_array[3] != $timestamp_array[3]); } elsif ($recurrence_parms{'recurrence_type'} eq "same_weekday") { if ($recur_timestamp_array[6] != $timestamp_array[6]) { $recur_timestamp_valid=0; } elsif ($recurrence_parms{'weekday_of_month_type'} eq "only_first_week") { $recur_timestamp_valid=0 if ($weekday_in_month_count != 0); } elsif ($recurrence_parms{'weekday_of_month_type'} eq "only_second_week") { $recur_timestamp_valid=0 if ($weekday_in_month_count != 1); } elsif ($recurrence_parms{'weekday_of_month_type'} eq "only_third_week") { $recur_timestamp_valid=0 if ($weekday_in_month_count != 2); } elsif ($recurrence_parms{'weekday_of_month_type'} eq "only_fourth_week") { $recur_timestamp_valid=0 if ($weekday_in_month_count != 3); } elsif ($recurrence_parms{'weekday_of_month_type'} eq "only_fifth_week") { $recur_timestamp_valid=0 if ($weekday_in_month_count != 4); } elsif ($recurrence_parms{'weekday_of_month_type'} eq "only_last_week") { if ($last_week != 1) { $recur_timestamp_valid=0; } else { #$debug_info .= "timestamp: $recur_timestamp\n"; #$debug_info .= "current month: $current_month\n"; #$debug_info .= "lookahead timestamp: $recur_timestamp_next_week\n"; #$debug_info .= "lookahead month: $recur_timestamp_next_week_array[4]\n"; } } } elsif ($recurrence_parms{'recurrence_type'} eq "every_x_days") { $recur_timestamp_valid=0 if (($recur_timestamp - $start_timestamp) % (86400 * $recurrence_parms{'every_x_days'}) !=0); } elsif ($recurrence_parms{'recurrence_type'} eq "every_x_weeks") { $recur_timestamp_valid=0 if (($recur_timestamp - $start_timestamp) % (86400 * 7 * $recurrence_parms{'every_x_weeks'}) !=0); } if ($recurrence_parms{'year_fit_type'} eq "custom_months") { my $month_valid=0; foreach $custom_month (@custom_months) { $month_valid=1 if ($custom_month == $recur_timestamp_array[4]); } $recur_timestamp_valid=0 if ($month_valid == 0); } if ($recur_timestamp_valid == 1) { push @recurring_events_array, $recur_timestamp+$timed_event_diff; my $real_year = $recur_timestamp_array[5]+1900; } #count how many of the event's weekdays we have come across in #each month. This is for validating events that occur on the #second tuesday, fifth monday, etc. if ($recur_timestamp_array[6] == $timestamp_array[6]) { $weekday_in_month_count++; } #reset week_in_month count if this is the last week in the month $weekday_in_month_count=0 if ($last_week == 1 && $recur_timestamp_array[6] == $timestamp_array[6]); } if (scalar @recurring_events_array == 0) { $debug_info .= "Error! No valid recurring event dates found!\n"; } #$debug_info .= "returning ".(scalar @recurring_events_array)." recurring events\n"; return \@recurring_events_array; } sub verify_date() { # $date is of the format similar to mm/dd/yyyy (or a permutation like dd/mm/yyyy) my ($date, $recurrence_parms_ref) = @_; my %recurrence_parms = %{$recurrence_parms_ref}; my $results=""; if ($date !~ /^(\w{1,2}\W\w{1,2}\W\w{2,4}|\w{1,2}\W\w{2,4}\W\w{1,2}|\w{2,4}\W\w{1,2}\W\w{1,2})$/) { $lang{date_verify_err0} =~ s/###date###/$date/; $lang{date_verify_err0} =~ s/###format###/$current_calendar{date_format}/; $results .= $lang{date_verify_err0}."\n"; } if ($recurrence_parms{'recurrence_type'} eq "every_x_days") { if ($recurrence_parms{'every_x_days'} == 0) { $results .= $lang{date_verify_err7}."\n"}; } elsif ($recurrence_parms{'recurrence_type'} eq "every_x_weeks") { if ($recurrence_parms{'every_x_weeks'} == 0) { $results .= $lang{date_verify_err8}."\n"}; } if ($date eq "") { $results .= $lang{date_verify_err1}."\n"; } my ($mon, $day, $year) = &format2mdy($date, $current_calendar{date_format}); return $results; } sub verify_time { my ($time) = @_; my $results=""; if($options{twentyfour_hour_format}) { if ($time !~ /(\d+):(\d+)/) { $lang{time_verify_err0} =~ s/\{0\}/$time/; $results .= $lang{time_verify_err0}; } else { my $hours = $1; my $minutes = $2; if ($hours > 23 || $hours < 0){ $lang{time_verify_err1} =~ s/\{0\}/$hours/; $results .= $lang{time_verify_err1}; } if ($minutes > 59 || $minutes < 0){ $lang{time_verify_err2} =~ s/\{0\}/$minutes/; $results .= $lang{time_verify_err2}; } } } else { if ($time !~ /(\d+):(\d+)\s*($lang{am}|$lang{pm})/) { $lang{time_verify_err0} =~ s/\{0\}/$time/; $results .= $lang{time_verify_err0}; } else { my $hours = $1; my $minutes = $2; my $ampm = $3; if ($hours > 12 || $hours < 0) { $lang{time_verify_err1} =~ s/\{0\}/$hours/; $results .= $lang{time_verify_err1}; } if ($minutes > 60 || $minutes < 0) { $lang{time_verify_err2} =~ s/\{0\}/$minutes/; $results .= $lang{time_verify_err2}; } } } return $results; } sub preview_date { my $results = {}; $results{'success'} = 1; $results{'messages'} = []; my @valid_dates; my %recurrence_parms; $recurrence_parms{'recurrence_type'} = $q->param('recurrence_type'); $recurrence_parms{'weekday_of_month_type'} = $q->param('weekday_of_month_type'); $recurrence_parms{'every_x_days'} = $q->param('every_x_days'); $recurrence_parms{'every_x_weeks'} = $q->param('every_x_weeks'); $recurrence_parms{'year_fit_type'} = $q->param('year_fit_type'); $recurrence_parms{'recur_end_date'} = $q->param('recur_end_date'); $recurrence_parms{'recur_end_timestamp'} = 0; my @custom_months = $q->param('custom_months'); $recurrence_parms{'custom_months'} = \@custom_months; my $recurring_event = $q->param('recurring_event'); my $event_start_date = $q->param('evt_start_date'); my $recur_end_date = $q->param('recur_end_date'); my $event_days = $q->param('evt_days'); my $event_start_time = $q->param('evt_start_time'); my $event_end_time = $q->param('evt_end_time'); my $all_day_event = $q->param('all_day_event'); my $date_valid = &verify_date($event_start_date); if ( $date_valid ne "" ) { $results{'success'} = 0; push @{$results{messages}}, $date_valid; } if ($event_days eq "") { $results{'success'} = 0; push @{$results{messages}}, $lang{date_verify_err2}; } if ($event_days =~ m/\D/ || $event_days <= 0) { $results{'success'} = 0; my $temp = $lang{date_verify_err3}; $temp =~ s/\$1/$event_days/; push @{$results{messages}}, $temp; } if ($recurring_event) { my $temp .= &verify_date($recurrence_parms{'recur_end_date'}, \%recurrence_parms); if ($temp ne "") { $results{'success'} = 0; push @{$results{messages}}, $lang{date_preview_for_recurring_end_date}. " " . $temp; } } if ($results{'success'} == 1) { my ($start_mon, $start_mday, $start_year) = &format2mdy($event_start_date, $current_calendar{date_format}); my ($recur_end_mon, $recur_end_mday, $recur_end_year) = &format2mdy($recurrence_parms{'recur_end_date'}, $current_calendar{date_format}); $start_mon--; $recur_end_mon--; my ($event_start_timestamp, $event_end_timestamp) = ×tamp_from_datetime($start_mday,$start_mon,$start_year,$event_days,$event_start_time,$event_end_time,$all_day_event); @timestamp_array = gmtime $event_start_timestamp; my $real_year = $timestamp_array[5]+1900; if ($recurring_event == 1) { #calculate end timestamp $recurrence_parms{'recur_end_timestamp'} = timegm(0,0,0,$recur_end_mday,$recur_end_mon,$recur_end_year); my @recurring_events_array = @{&calculate_recurring_events($event_start_timestamp, \%recurrence_parms)}; foreach $recurring_event_timestamp (@recurring_events_array) { $timestamp1 = $recurring_event_timestamp; $timestamp2 = $recurring_event_timestamp + 86400 * ($event_days-1); if ($timestamp1 == $timestamp2) {$timestamp2++;} $date_range = &nice_date_range_format($timestamp1, $timestamp2, "-"); push @valid_dates, $date_range; } } else { my $date = ""; $timestamp1 = $event_start_timestamp; $timestamp2 = $event_start_timestamp + 86400 * ($event_days-1); if ($timestamp1 == $timestamp2) {$timestamp2++;} $date_range = &nice_date_range_format($timestamp1, $timestamp2, "-"); push @valid_dates, $date_range; } } $results{success} = ($results{success} eq "1") ? JSON::PP::true() : JSON::PP::false(); $results{debug_info} = $debug_info; $results{valid_dates} = \@valid_dates;; $json_results = encode_json \%results; print <<p1; Cache-control: no-cache,no-store,private Content-Type: text/plain; charset=iso-8859-1\n $json_results p1 exit(0); } # end preview date subroutine sub view_event { my %current_event = %{$events{$current_event_id}}; my $event_info = &generate_event_details(\%current_event, $event_details_template); $debug_info =~ s/\n/<br\/>\n/g; my $html_output .=<<p1; Cache-control: no-cache,no-store,private Content-Type: text/html; charset=$lang{charset}\n <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta http-equiv="pragma" content="no-cache"> <link rel="stylesheet" href="$css_path" type="text/css" media=screen> p1 $html_output .= &get_js_includes( $theme_url ); $html_output .= <<p1; <title>$current_event{title} $event_info $debug_info p1 print $html_output; } # view_event sub view_pending_event { my ($pending_event_ref) = @_; my %pending_event = %{$pending_event_ref}; $event_details_template =~ s/###export event link###/$lang{event_details_export_disable}/g; $event_details_template =~ s/###edit event link###/$lang{event_details_edit_disable}/g; $event_details_template =~ s/###email reminder link###/$lang{event_email_reminder_disable}/g; my $event_info = &generate_event_details(\%pending_event, $event_details_template); $debug_info =~ s/\n/\n/g; my $html_output .=< p1 $html_output .= &get_js_includes( $theme_url ); $html_output .= <$current_event{title} $event_info $debug_info p1 print $html_output; } # view_pending_event sub set_email_reminder { my $results = {}; $results{'success'} = 1; $results{'messages'} = []; my %current_event = %{$events{$current_event_id}}; my $reminder_results = ""; my $to_address = $q->param('email_address'); my $extra_text = $q->param('extra_text'); $to_address =~ s/\s//g; $to_address =~ s/;/,/g; chomp $to_address; my $reminder_seconds = $q->param('reminder_seconds'); my $reminder_time = $q->param('reminder_time'); $reminder_time = lc $reminder_time; my $email_valid = &validate_emails($to_address); if ($email_valid ne "") { $reminder_results = $lang{email_reminder_invalid_address}; $reminder_results =~ s/###address###/$email_valid/g; push @{$results{messages}}, $reminder_results; $results{'success'} = 0; } else { my @event_reminder_ids = ($current_event{id}); my @future_event_reminder_ids; if ($q->param('all_in_series') ne "") { @event_reminder_ids = &get_events_in_series($current_event{series_id}); &normalize_timezone(); } foreach $event_reminder_id (@event_reminder_ids) { if ($events{$event_reminder_id}{start} > $rightnow) { push @future_event_reminder_ids, $event_reminder_id; } } foreach $event_reminder_id (@future_event_reminder_ids) { # assemble email reminder xml my $reminder_xml = ""; $reminder_xml .= &xml_store($event_reminder_id, "evt_id"). &xml_store($reminder_seconds, "before"). &xml_store($to_address, "email_address"). &xml_store($extra_text, "extra_text"). &xml_store("$script_url/$name", "script_url"). &xml_store($rightnow, "timestamp"); $reminder_xml = "$reminder_xml\n"; # write email reminder to file open (FH, ">>$options{email_reminders_datafile}") || ($debug_info .="
Unable to open email reminders data file $options{email_reminders_datafile} for writing
"); print FH $reminder_xml; close FH; } my $test_reminder_results = ""; if ($q->param('send_test_now') == 1) { my $reminder_text = $lang{email_reminder_test_text}; $reminder_text =~ s/###reminder_time###/$reminder_time/g; my $date_string = &nice_date_range_format($current_event{start}, $current_event{end}, " - "); my $event_time = ""; if ($current_event{all_day_event} ne "1") { $event_time = &nice_time_range_format($current_event{start}, $current_event{end}); } $reminder_text =~ s/###time###/$event_time/g; $reminder_text =~ s/###title###/$current_event{title}/g; $reminder_text =~ s/###date###/$date_string/g; $reminder_text =~ s/###extra text###/$extra_text/g; $reminder_text =~ s/###details###/$current_event{details}/g; $reminder_text =~ s/###link###/$script_url\/$name?view_event=1&evt_id=$current_event{id}/g; $test_reminder_results = &send_email_reminder(\%current_event, $to_address, $reminder_text); if ($test_reminder_results eq "1") { $test_reminder_results = $lang{email_reminder_test_success}; $test_reminder_results =~ s/###address###/$to_address/g; } else { $test_reminder_results = $lang{email_reminder_test_fail}; $test_reminder_results =~ s/###results###/$test_reminder_results/g; } } $lang{email_reminder_results1} =~ s/###address###/$to_address/g; $lang{email_reminder_results1} =~ s/###reminder time###/$reminder_time/g; if ($q->param('send_test_now') == 1) { $lang{email_reminder_results1} .= $lang{email_reminder_results3} } else { $lang{email_reminder_results1} .= $lang{email_reminder_results2} } push @{$results{messages}}, $lang{email_reminder_results1}; push @{$results{messages}}, $test_reminder_results;; } $results{success} = ($results{success} eq "1") ? JSON::PP::true() : JSON::PP::false(); $results{debug_info} = $debug_info; $json_results = encode_json \%results; print <param('remote_calendar_url'); $remote_calendar_url =~ s/\?.+//; $remote_calendar_url .= "/" if ($remote_calendar_url !~ /\.cgi$/); $remote_calendar_base_url = $remote_calendar_url; $remote_calendar_url .= "?remote_calendar_request=1&get_public_calendars=1"; my $xml_results = &get_remote_file($remote_calendar_url); my %remote_calendars = %{&xml2hash($xml_results)}; my @public_calendars; if (ref $remote_calendars{'xml'}{public_calendar} eq "ARRAY") { @public_calendars = @{$remote_calendars{'xml'}{public_calendar}}; } else { push @public_calendars, $remote_calendars{'xml'}{public_calendar}; } $results{public_calendars} = \@public_calendars; $results{url} = $remote_calendar_base_url; $results{remote_calendar_version} = $remote_calendars{'xml'}{plans_version}; $results{success} = ($results{success} eq "1") ? JSON::PP::true() : JSON::PP::false(); $results{debug_info} = $debug_info; $json_results = encode_json \%results; print <param('cal_id'); $results .=<$plans_version p1 # if the client requests a list of all calendars that are publically share-able. if ($q->param('get_public_calendars') eq "1") { foreach $cal_id (keys %calendars) { if ($calendars{$cal_id}{allow_remote_calendar_requests}) { my $temp=$calendars{$cal_id}{remote_calendar_requests_require_password}; $results .=<$cal_id$calendars{$cal_id}{title}$temp p1 } } } else { # return xml data for the events. my @current_cal_ids = (); my @temp = split (",",$current_cal_ids_string); my $cal_id_valid=1; foreach $cal_id (@temp) { if ($cal_id !~ /\D/) { push @current_cal_ids, $cal_id; } else { $cal_id_valid=0; $results .= "Invalid calendar ID: $cal_id"; last; } } if ($cal_id_valid) { foreach $current_cal_id (@current_cal_ids) { $results .=<$current_cal_id$calendars{$current_cal_id}{title}$calendars{$current_cal_id}{gmtime_diff} p1 } foreach $current_cal_id (@current_cal_ids) { foreach $event_id (keys %events) { my $event_in_current_calendar = 0; foreach $temp_cal_id (@{$events{$event_id}{cal_ids}}) { if ($temp_cal_id eq $current_cal_id) { $event_in_current_calendar = 1; last; } } next if ($event_in_current_calendar == 0); my $xml_data = &event2xml($events{$event_id}); $results .=<Script Name:
$name

p1 if ($options{email_mode} && !$writable{email_reminders_datafile}) { $results .=<Warning: The email reminders data file: $options{email_reminders_datafile} is not writable. This will cause some email functions to be disabled or not work correctly.
p1 } if ($options{data_storage_mode} == 0 && !$writable{calendars_file}) { $results .=<Warning: The calendars data file: $options{calendars_file} is not writable. The add/edit calendars tab won't appear unless this file is writable.
p1 } if ($options{data_storage_mode} == 0 && !$writable{pending_actions_file}) { $results .=<Warning: The pending actions data file: $options{pending_actions_file} is not writable. The add/edit calendars tab won't appear unless this file is writable.
p1 } if ($options{data_storage_mode} == 0 && !$writable{events_file}) { $results .=<Warning: The events data file: $options{events_file} is not writable. The add/edit events tab won't appear unless this file is writable.
p1 } my $cwd = `pwd`; $results .=<Plans version: $plans_version
script name: $name
script file path: $cwd
script url path: $script_url

Theme url: $theme_url
graphics url: $graphics_url
icons url: $icons_url

Options:
p1 # blank out options that we don't want public delete $options{db_name}; delete $options{db_hostname}; delete $options{db_username}; delete $options{db_password_file}; delete $options{mysql_password}; delete $options{salt}; foreach $option (keys %options) { $results .=<$option: $options{$option}
p1 } return $results; } # end diagnostic subroutine sub assemble_icon_menus() { # this function extracts a data structure for the icon menus from the xml definition my ($data)= @_; my @new_menuitems=(); my @menuitems = &xml_extract($data, "menuitem", 0); if (scalar @menuitems == 0) { $debug_info .= "Warning. There's a menu with no menuitems. This may be caused by an older version of Perl ( < 5.6).\n"; $debug_info .= "$data"; } else { foreach my $menuitem (@menuitems) { my $icon_name = $menuitem->{attributes}{"value"}; my $icon_description = $menuitem->{data}; $new_menuitems[$menuitem->{position}] = [$icon_name,$icon_description]; } } # then get the submenus my @submenus = &xml_extract($data, "menu", 0); foreach $submenu (@submenus) { my $temp = $submenu->{data}; my $temp2 = $submenu->{attributes}{name}; my @submenuitems = assemble_icon_menus($temp); $new_menuitems[$submenu->{position}] = [$temp2, \@submenuitems]; } return @new_menuitems; } #******************** end assemble_icon_menus ********************** sub generate_flat_icon_menus { my ($icons_list, $selected_icon) = @_; my $return_text=""; my $indent = " "; for ($l1=0;$l1<$index_number;$l1++) { $indent .= " "; } foreach $icon_ref (@{$icons_list}) { my $identifier = @{$icon_ref}[1]; if ($identifier =~ /ARRAY/) { # if it's a submenu $icon_menu_index_number++; my $submenu_name = @{$icon_ref}[0]; $return_text .= < p1 $return_text .= &generate_flat_icon_menus(@{$icon_ref}[1], $selected_icon); $return_text .= < p1 } else { # if it's a menu item my $icon_filename = @{$icon_ref}[0]; my $icon_name = @{$icon_ref}[1]; $icon_name = &decode($icon_name); if ($icon_filename eq $selected_icon) { $return_text .= <$icon_name p1 } else { $return_text .= <$icon_name p1 } } } return $return_text; } #******************** end generate_flat_icon_menus ********************** sub get_upcoming_events() { my $days_before = $q->param('days_before')+0; my $days_after = $q->param('days_after')+0; my $background_calendars_mode = $q->param('background_calendars_mode'); my $upcoming_events_id = $q->param('upcoming_events_id'); my $results = ""; my $start = $rightnow - $days_before*86400; my $end = $rightnow + $days_after*86400; # clear events %events = (); my @cal_ids = split(',',$q->param('cal_ids')); #$results .= &common_javascript(); my $date_format = lc $current_calendar{date_format}; $results .= "date_format = '$date_format';\n"; my %all_calendar_ids; foreach $calendar_id (@cal_ids) { next if ($calendars{$calendar_id}{id} eq ""); foreach $local_background_calendar_id (keys %{$calendars{$calendar_id}{local_background_calendars}}) { $all_calendar_ids{$local_background_calendar_id} = 1; } $all_calendar_ids{$calendar_id} = 1; } foreach $calendar_id (keys %all_calendar_ids) { $results .= "calendars['$calendar_id'] = " . &calendar2json($calendars{$calendar_id}) . ";\n"; } my @temp; if ($background_calendars_mode ne "none") { @temp = keys %all_calendar_ids; } else { @temp = @cal_ids; } &load_events($start, $end, \@temp); &normalize_timezone; $results .= "all_calendars = new Array('".(join "','", keys %all_calendar_ids)."');\n"; foreach $event_id (keys %events) { $results .= "upcoming_events['$event_id'] = " . &event2json($events{$event_id}) . ";\n"; $results .= "upcoming_events_order.push('$event_id'); \n"; } if ($debug_info ne "") { $debug_info = &javascript_cleanup($debug_info); $results .= "if ($upcoming_events_id.debug) debug('$debug_info');\n"; } $results .= "$upcoming_events_id.plans_theme_url='$theme_url';\n"; $results .= "$upcoming_events_id.show();\n"; my $nice_expires = $rightnow + $options{upcoming_events_cache_seconds}; $nice_expires = uc &formatted_time($nice_expires, "mn/md/yy hh:mm:ss AMPM"); my $cache_line = "Cache-control: no-cache,no-store,private"; $cache_line = "Cache-Control: max-age=$options{upcoming_events_cache_seconds};\nExpires: $nice_expires;" if ($options{upcoming_events_cache_seconds} > 0); my $html_output .=<$lang{export} $lang{these_events_to} p1 } sub add_edit_user() { my $results = {}; $results{'success'} = 0; $results{'messages'} = []; my $status = 1; my $id = $q->param('user_id'); my $name = $q->param('name'); my $notes = $q->param('notes'); my $password = $q->param('password'); my $new_password = $q->param('new_password'); my $repeat_password = $q->param('repeat_password'); my $delete_flag = $q->param('delete'); my $login_valid = 0; # validate password if ($logged_in_as_root) { # plans 'root' password $login_valid = 1; } elsif ($logged_in_as_current_cal_admin) { # current calendar admin $login_valid = 1; } if (!$login_valid) { if ($delete_flag eq "1") { push @{$results{messages}}, $lang{user_not_deleted}; } else { push @{$results{messages}}, $lang{user_not_added}; } } else { # delete? if ($delete_flag eq "1") { my $temp = $lang{user_deleted}; $temp =~ s/\$1/$users{$id}{name}/; push @{$results{messages}}, $temp; delete $user{calendars}{$calendar_id}; if (scalar keys %{$user{calendars}} == 0) { delete $users{$user_id}; } &update_user($user_id); &delete_user($id); $results{'success'} = 1; $results{'users'} = &generate_users_javascript; } else { # add or update? if ($id eq "") { # add $password = crypt($password, $options{salt}); my $new_id = $max_user_id+1; my %cal_refs; $cal_refs{$current_cal_id}{edit_events} = 1; $users{$new_id} = {id => $new_id, name => $name, notes => $notes, password => $password, calendars => \%cal_refs }; &add_user($new_id); $results{'success'} = 1; $results{'users'} = &generate_users_javascript; push @{$results{messages}}, $lang{user_added}; } else { # update $password = crypt($password, $options{salt}); my %cal_refs; $cal_refs{$current_cal_id}{edit_events} = 1; $users{$id} = {id => $id, name => $name, notes => $notes, password => $password, calendars => \%cal_refs }; &update_user($id); $results{'success'} = 1; $results{'users'} = &generate_users_javascript; push @{$results{messages}}, $lang{user_updated}; } } } if ($results{'success'} eq "1") { $results{calendars} = &calendars_as_array( \%calendars ); } $results{success} = ($results{success} eq "1") ? JSON::PP::true() : JSON::PP::false(); $results{debug_info} = $debug_info; $json_results = encode_json \%results; print <param('ical_url')); if (!$logged_in_as_root) { push @{$results{messages}}, 'You must be logged in to add an iCal!'; } else { # try getting the URL my $url_results = &get_remote_file($ical_url); $debug_info .= $url_results; if ($url_results eq "404 not found!" || $url_results =~ /cannot connect/i) { push @{$results{messages}}, 'iCal calendar not added! (invalid url)'; $results .= "messages = 'iCal calendar not added! (invalid url)';"; } else { my %new_calendar = &deep_copy(\%default_cal); my $new_cal_id = $max_cal_id + 1; $new_calendar{id} = $new_cal_id; $new_calendar{title} = "($ical_url)"; $new_calendar{type} = "ical"; $new_calendar{url} = $ical_url; $calendars{$new_cal_id} = &deep_copy(\%new_calendar); &add_calendars([$new_calendar{id}]); push @{$results{messages}}, 'iCal calendar ($ical_url) added!'; $results{'success'} = 1; } } if ($results{'success'} eq "1") { $results{calendars} = &calendars_as_array( \%calendars ); } $results{success} = ($results{success} eq "1") ? JSON::PP::true() : JSON::PP::false(); $results{debug_info} = $debug_info; $json_results = encode_json \%results; print <id; $results{'cookie_path'} = $cookie_path; } elsif( $q->param('logout') eq "1") { $results{'success'} = 1; push @{$results{messages}}, $lang{'logged_in2'}; } else { $results{'success'} = 0; push @{$results{messages}}, $lang{'logged_in3'}; } $results{success} = ($results{success} eq "1") ? JSON::PP::true() : JSON::PP::false(); $results{debug_info} = $debug_info; $json_results = encode_json \%results; print <param('approve'); my $delete = $q->param('delete'); my $results = {}; $results{'success'} = 1; $results{'messages'} = []; if ($logged_in_as_root || $logged_in_as_current_cal_admin) { $add_edit_event = "add"; my @events_to_approve = split(',', $q->param('approve')); my @events_to_delete = split(',', $q->param('delete')); foreach $event_to_approve (@events_to_approve) { &commit_event($new_events{$event_to_approve}); my $temp = $lang{pending_event_approved}; $temp =~ s/\$1/$event_to_approve/; push @{$results{messages}}, $temp; } foreach $event_to_delete (@events_to_delete) { my $temp = $lang{pending_event_deleted}; $temp =~ s/\$1/$event_to_delete/; push @{$results{messages}}, $temp; } push @events_to_delete, @events_to_approve; &delete_pending_actions(\@events_to_delete); $results .= "window.location='$script_url/$name?messages=".&encode($messages)."';"; #$results .= "alert(\"window.location=$script_url/$name&messages=".&encode($messages)."\");"; } else { $results{'success'} = 0; push @{$results{messages}}, $lang{invalid_password}; } $results{success} = ($results{success} eq "1") ? JSON::PP::true() : JSON::PP::false(); $results{debug_info} = $debug_info; if ($results{'success'} eq "1") { $results{calendars} = &calendars_as_array( \%calendars ); &load_actions(); my @pending_events = &pending_events_as_array(); $results{'pending_events'} = \@pending_events; $results{'pending_events_area_html'} = &generate_pending_events_area(); $results{'calendar_html'} = ®enerate_calendar_html(); #$results{new_events} = \%events; } $json_results = encode_json \%results; print < $lang{controls_calendar_label}
p1 my @selectable_calendars; if ($options{all_calendars_selectable}) { @selectable_calendars = keys %calendars; } else { @selectable_calendars = keys %{$current_calendar{selectable_calendars}}; unshift @selectable_calendars, $current_cal_id if (!&contains(\@selectable_calendars, $current_cal_id)); } if (scalar @selectable_calendars > 1) { $results .=< p1 #list each calendar for the user to select my %explicit_calendar_order; if ($options{calendar_select_order} ne "alpha" && $options{calendar_select_order} ne "") { my @cal_order_ids = split(',',$options{calendar_select_order}); my $cal_order_index = 0; foreach $selectable_calendar_id (@selectable_calendars) { $explicit_calendar_order{"$selectable_calendar_id"} = 9999999; } foreach $cal_order_id (@cal_order_ids) { next if ($cal_order_id eq ""); $explicit_calendar_order{"$cal_order_id"} = $cal_order_index; $cal_order_index++; } } foreach $selectable_calendar_id (sort { if ($options{calendar_select_order} eq "alpha") { return lc $calendars{$a}{title} cmp lc $calendars{$b}{title}; } elsif ($options{calendar_select_order} ne "") { return $explicit_calendar_order{"$a"} <=> $explicit_calendar_order{"$b"} || lc $calendars{$a}{title} cmp lc $calendars{$b}{title};} else { return $a <=> $b;} } @selectable_calendars) { my $selected =""; $selected =" selected" if ($selectable_calendar_id eq $current_calendar{id}); $selectable_calendar_id=~ s/\D//g; $results .=<$calendars{$selectable_calendar_id}{title} p1 } $results .=< p1 } else { $results .=<$current_calendar{title} p1 } $results .=<
$lang{controls_display_label}