Class ::xo::ical::VCALITEM (public)
::nx::Class ::xo::ical::VCALITEM
Defined in packages/xotcl-core/tcl/ical-procs.tcl
VCALITEM is the superclass of a VTODO and a VEVENT, intended as an abstract class.
- Testcases:
- No testcase defined.
Source code: :property -accessor public creation_date :property -accessor public last_modified :property -accessor public dtstart :property -accessor public dtstamp :property -accessor public uid :property -accessor public priority :property -accessor public summary :property -accessor public url :property -accessor public description :property -accessor public location :property -accessor public geo :property -accessor public status :property -accessor public {is_day_item false} :property -accessor public last-modified ;# TODO: who (which client) needs this? not a standard ical attribute :public method get {property {default ""}} { # # Return a certain property of an ical items. In case, the # item has no such property, return the default value. # if {[info exists :$property]} { return [set :$property] } return $default } :method tag {-tag -param -conv -value slot:required} { # # Generate a single (line) entry in ical format starting with # the specified tag. If no "tag" is specified, the tag name is # uppercase of the slot. The value might be specified literally # or it can be the name of an instance variable (named by the # "slot" attribute". # # The functions returns empty (produces no output) when either # the value is empty or no instance variable exists. # Optionally, the value can be converted. # # #ns_log notice "::xo::ical::VCALITEM tag [self args]" if {![info exists tag]} { set tag [string toupper $slot] } # # if we get a param, we expect it to include delimiters, such # as the leading semicolon # if {[info exists param]} { append tag $param } if {![info exists value]} { if {[info exists :$slot]} { set value [set :$slot] } else { # # If we have no value and no instance variable set, do not # output this slot. # return "" } } # # When a converter is given, apply it # if {[info exists conv]} { set value [::xo::ical $conv $value] } if {$value ne ""} { set result [::xo::ical reflow_content_line "$tag:$value"] append result \r\n #ns_log notice "::xo::ical::VCALITEM tag [self args] -> len: [string length $result]" } else { set result "" } return $result } :method start_end {} { # # Output either a DAY-EVENT (denoted by a single DTSTART with # appropriate VALUE format) or a state and end timestamps. # if {${:is_day_item}} { append result "DTSTART;" [::xo::ical tcl_time_to_local_day ${:dtstart}] \r\n } else { append result [:tag -conv tcl_time_to_utc dtstart] [:tag -conv tcl_time_to_utc dtend] } } :public method as_ical {} { # # Output a VCALITEM (VTODO or VEVENT) "object" in ical notation. # set item_type [namespace tail [:info class]] append t "BEGIN:$item_type\r\n" [:ical_body] "END:$item_type\r\n" return $t } :public method ical_body {} { # # The method ical_body returns the ical-formatted content of the # variables. All variables of VEVENTs and VTODOs are listed below, # since the names are distinct, and no methods are used. # # So far there is no handling for the repetition fields (which # might occur more than once). An option would be to handle these # as lists. # # # All date/timestamps are provided either by # the ANSI date (from postgres) or by a date # which can be processed via clock scan # if {![info exists :dtstamp]} {set :dtstamp ${:creation_date}} if {![info exists :last_modified]} {set :last_modified ${:dtstamp}} set tcl_stamp [::xo::db::tcl_date ${:dtstamp} tz] set tcl_creation_date [::xo::db::tcl_date ${:creation_date} tz] set tcl_last_modified [::xo::db::tcl_date ${:last_modified} tz] # status values: # VEVENT: TENTATIVE, CONFIRMED, CANCELLED # VTODO: NEEDS-ACTION, COMPLETED, IN-PROCESS, CANCELLED # VJOURNAL: DRAFT, FINAL, CANCELLED append t [:tag -conv tcl_time_to_utc -value $tcl_creation_date created] [:tag -conv tcl_time_to_utc -value $tcl_last_modified last-modified] [:tag -conv tcl_time_to_utc -value $tcl_stamp dtstamp] [:start_end] [:tag -conv tcl_time_to_utc completed] [:tag -conv tcl_time_to_utc percent-complete] [:tag transp] [:tag uid] [:tag url] [:tag geo] [:tag priority] [:tag sequence] [:tag CLASS] [:tag -conv text_to_ical location] [:tag status] [:tag -conv text_to_ical description] [:tag -conv text_to_ical summary] [:tag -conv tcl_time_to_utc due] if {[info exists :formatted_recurrences]} { append t ${:formatted_recurrences} } return $t } :public method "parse RRULE" {recurrule} { # # parse recurrence rule provided in cal syntax. This method # assumes that the instance variable dtstart is already set, # before this method is called. # set r_freq "" set every_n 1 set r_error 0 set r_until "" set days_of_week "" set r_count 0 foreach rval [split $recurrule ";"] { if { [regexp {^FREQ\=+(.*)$} $rval _ freqval] } { switch $freqval { DAILY { set r_freq "day" } WEEKLY { set r_freq "week" } MONTHLY { set r_freq "month_by_day"} YEARLY { set r_freq "year"} default { set r_error 1 } } } elseif { [regexp {^COUNT=(.*)$} $rval _ countval] } { set r_count $countval } elseif { [regexp {^UNTIL=([0-9]+)(T([0-9]+)Z?)?$} $rval _ untildate untiltime] } { if {$untiltime eq ""} { set untiltime 000000 } append r_until "[string range $untildate 0 3]-[string range $untildate 4 5]-[string range $untildate 6 7]" " " "[string range $untiltime 0 1]:[string range $untiltime 2 3]" } elseif { [regexp {^INTERVAL\=+(.*)$} $rval _ intval] } { set every_n $intval } elseif { [regexp {^BYDAY\=+(.*)$} $rval _ bydayval] } { # # Build days_of_week list # foreach dayval [split $bydayval ","] { switch $dayval { SU { lappend days_of_week "0" } MO { lappend days_of_week "1" } TU { lappend days_of_week "2" } WE { lappend days_of_week "3" } TH { lappend days_of_week "4" } FR { lappend days_of_week "5" } SA { lappend days_of_week "6" } } } } elseif { [regexp {^BYMONTHDAY\=+(.*)$} $rval _ bymonthdayval] } { set r_freq "month_by_date" } else { # other rules don't work with OpenACS recurrence model :debug "ignore recurrence rule <$rval> of <$recurrule>" } #check we can make this rule, else ignore } # # We should have now 'r_freq' computed. If 'r_until' is not # provided, calculate it based on 'r_count' (COUNT is not # directly supported by OpenACS). If both UNTIL and COUNT are # not set, it is an unlimited event and skipped. # if { $r_until eq "" && $r_freq ne "" && $r_count > 0 } { # current date + r_count * r_freq * every_n (/ num_days) # set num seconds per frequency switch $r_freq { day { set r_freq_amount 86400 } week { set r_freq_amount 604800 } month_by_day { set r_freq_amount 2419200 } month_by_date { set r_freq_amount 2678400 } year { set r_freq_amount 31449600 } } # start date is count=1, so adjust count set r_count [expr {$r_count - 1}] set r_extra [expr {$r_count * $r_freq_amount * $every_n}] if { $r_freq eq "week" && [llength $days_of_week] > 0} { set r_extra [expr {$r_extra / [llength $days_of_week]}] } set r_until [::xo::ical clock_to_oacstime [expr {[clock scan ${:dtstart}] + $r_extra}]] } # # If we have no errors, and 'r_freq' is computed, then keep the # computed values in form of a parameter list for # calendar::item::add_recurrence # if { !$r_error && $r_freq ne ""} { set :recurrence_options [list -interval_type $r_freq -every_n $every_n] if {$days_of_week ne ""} { lappend :recurrence_options -days_of_week $days_of_week } if {$r_until ne ""} { lappend :recurrence_options -recur_until $r_until } } } :public method add_recurrence {-cal_item_id:integer} { # # Call calendar::item::add_recurrence with the options # calculated by "parse RRULE" # if { [info exists :recurrence_options] } { calendar::item::add_recurrence -cal_item_id $cal_item_id {*}${:recurrence_options} } } :public method edit_recurrence { -cal_item_id:integer -recurrence_id:integer,0..1 } { # # We might or might not have a recurrence on the old entry. # In case we have one, the old one might have started earlier, # so we try to terminate it. :debug "do we have a recurrence? [info exists :recurrence_options]" if { [info exists :recurrence_options] } { if {$recurrence_id ne ""} { # # The current implementation of calendar::item::edit_recurrence is # just built for the interface of the calendar package, which does # not allow one to change multiple attributes. We have to assume, that a # calendar client allows this. # # When the old start date is the same as the new start date, # we could delete the recurrence and add a new one. # # calendar::item::delete_recurrence -recurrence_id $recurrence_id lassign [::xo::dc list -prepare integer get_old_start_date_and_event_id { select start_date, from acs_events e, timespans t, time_intervals i where recurrence_id = :recurrence_id and e.timespan_id = t.timespan_id and i.interval_id = t.interval_id order by 1 limit 1 }] old_start_date old_event_id :debug "recurrence_id $recurrence_id old_event_id $old_event_id cal_item_id $cal_item_id old_start_date $old_start_date new start_date ${:dtstart} opts ${:recurrence_options}" ::xo::db::sql::acs_event recurrence_timespan_edit -event_id $old_event_id -start_date $old_start_date -end_date ${:dtstart} } calendar::item::add_recurrence -cal_item_id $cal_item_id {*}${:recurrence_options} } } # # End of class definition of ::xo::ical::VCALITEM #XQL Not present: Generic, PostgreSQL, Oracle