Class: Tilia::VObject::Component::VCalendar
- Inherits:
-
Document
- Object
- Node
- Tilia::VObject::Component
- Document
- Tilia::VObject::Component::VCalendar
- Defined in:
- lib/tilia/v_object/component/v_calendar.rb
Overview
The VCalendar component.
This component adds functionality to a component, specific for a VCALENDAR.
Constant Summary
Constants inherited from Document
Document::ICALENDAR20, Document::UNKNOWN, Document::VCALENDAR10, Document::VCARD21, Document::VCARD30, Document::VCARD40
Constants inherited from Node
Node::PROFILE_CALDAV, Node::PROFILE_CARDDAV, Node::REPAIR
Instance Attribute Summary
Attributes inherited from Tilia::VObject::Component
Attributes inherited from Node
Instance Method Summary collapse
-
#base_component(component_name = nil) ⇒ Component?
Returns the first component that is not a VTIMEZONE, and does not have an RECURRENCE-ID.
-
#base_components(component_name = nil) ⇒ Array<Component>
Returns a list of all ‘base components’.
-
#by_uid(uid) ⇒ array
Returns all components with a specific UID value.
-
#document_type ⇒ Fixnum
Returns the current document type.
-
#expand(start, ending, time_zone = nil) ⇒ VCalendar
Expand all events in this VCalendar object and return a new VCalendar with the expanded events.
-
#validate(options = 0) ⇒ array
Validates the node for correctness.
-
#validation_rules ⇒ array
A simple list of validation rules.
Methods inherited from Document
#class_name_for_property_name, #class_name_for_property_value, #create, #create_component, #create_property, #initialize
Methods inherited from Tilia::VObject::Component
#[], #[]=, #add, #children, #components, #delete, #destroy, #initialize, #initialize_copy, #json_serialize, #key?, #remove, #select, #serialize, #to_s, #xml_serialize
Methods inherited from Node
#==, #[], #[]=, #delete, #destroy, #each, #initialize, #json_serialize, #key?, #serialize, #size, #xml_serialize
Constructor Details
This class inherits a constructor from Tilia::VObject::Document
Instance Method Details
#base_component(component_name = nil) ⇒ Component?
Returns the first component that is not a VTIMEZONE, and does not have an RECURRENCE-ID.
If there is no such component, null will be returned.
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/tilia/v_object/component/v_calendar.rb', line 185 def base_component(component_name = nil) is_base_component = lambda do |component| return false unless component.is_a?(Component) return false if component.name == 'VTIMEZONE' return false if component.key?('RECURRENCE-ID') true end if component_name select(component_name).each do |child| return child if is_base_component.call(child) end return nil end children.each do |child_group| child_group.each do |child| return child if is_base_component.call(child) end end nil end |
#base_components(component_name = nil) ⇒ Array<Component>
Returns a list of all ‘base components’. For instance, if an Event has a recurrence rule, and one instance is overridden, the overridden event will have the same UID, but will be excluded from this list.
VTIMEZONE components will always be excluded.
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
# File 'lib/tilia/v_object/component/v_calendar.rb', line 146 def base_components(component_name = nil) is_base_component = lambda do |component| return false unless component.is_a?(Component) return false if component.name == 'VTIMEZONE' return false if component.key?('RECURRENCE-ID') true end if component_name # Early exit return select(component_name).select is_base_component end components = [] children.each do |child_group| do_skip = false child_group.each do |child| unless child.is_a?(Component) # If one child is not a component, they all are so we skip # the entire group. do_skip = true break end components << child if is_base_component.call(child) end next if do_skip end components end |
#by_uid(uid) ⇒ array
Returns all components with a specific UID value.
422 423 424 425 426 427 428 429 430 431 432 |
# File 'lib/tilia/v_object/component/v_calendar.rb', line 422 def by_uid(uid) components.select do |item| item_uid = item.select('UID') if item_uid.empty? false else item_uid = item_uid.first.value uid == item_uid end end end |
#document_type ⇒ Fixnum
Returns the current document type.
133 134 135 |
# File 'lib/tilia/v_object/component/v_calendar.rb', line 133 def document_type ICALENDAR20 end |
#expand(start, ending, time_zone = nil) ⇒ VCalendar
Expand all events in this VCalendar object and return a new VCalendar with the expanded events.
If this calendar object, has events with recurrence rules, this method can be used to expand the event into multiple sub-events.
Each event will be stripped from it’s recurrence information, and only the instances of the event in the specified timerange will be left alone.
In addition, this method will cause timezone information to be stripped, and normalized to UTC.
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 |
# File 'lib/tilia/v_object/component/v_calendar.rb', line 228 def (start, ending, time_zone = nil) new_children = [] recurring_events = {} time_zone = ActiveSupport::TimeZone.new('UTC') unless time_zone strip_timezones = lambda do |component| component.children.each do |component_child| if component_child.is_a?(Property::ICalendar::DateTime) && component_child.time? dt = component_child.date_times(time_zone) # We only need to update the first timezone, because # setDateTimes will match all other timezones to the # first. dt[0] = dt[0].in_time_zone(ActiveSupport::TimeZone.new('UTC')) component_child.date_times = dt elsif component_child.is_a?(Component) strip_timezones.call(component_child) end end component end children.each do |child| if child.is_a?(Property) && child.name != 'PRODID' # We explictly want to ignore PRODID, because we want to # overwrite it with our own. new_children << child.clone elsif child.is_a?(Component) && child.name != 'VTIMEZONE' # We're also stripping all VTIMEZONE objects because we're # converting everything to UTC. if child.name == 'VEVENT' && (child.key?('RECURRENCE-ID') || child.key?('RRULE') || child.key?('RDATE')) # Handle these a bit later. uid = child['UID'].to_s fail InvalidDataException, 'Every VEVENT object must have a UID property' if uid.blank? if recurring_events.key?(uid) recurring_events[uid] << child.clone else recurring_events[uid] = [child.clone] end elsif child.name == 'VEVENT' && child.in_time_range?(start, ending) new_children << strip_timezones.call(child.clone) end end end recurring_events.each do |_uid, events| begin it = Recur::EventIterator.new(events, time_zone) rescue Recur::NoInstancesException # This event is recurring, but it doesn't have a single # instance. We are skipping this event from the output # entirely. next end it.fast_forward(start) while it.valid && it.dt_start < ending new_children << strip_timezones.call(it.event_object) if it.dt_end > start it.next end end self.class.new(new_children) end |
#validate(options = 0) ⇒ array
Validates the node for correctness.
The following options are supported:
Node::REPAIR - May attempt to automatically repair the problem.
Node::PROFILE_CARDDAV - Validate the vCard for CardDAV purposes.
Node::PROFILE_CALDAV - Validate the iCalendar for CalDAV purposes.
This method returns an array with detected problems. Every element has the following properties:
* level - problem level.
* message - A human-readable string describing the issue.
* node - A reference to the problematic node.
The level means:
1 - The issue was repaired (only happens if REPAIR was turned on).
2 - A warning.
3 - An error.
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 |
# File 'lib/tilia/v_object/component/v_calendar.rb', line 326 def validate( = 0) warnings = super() ver = self['VERSION'] if ver unless ver.to_s == '2.0' warnings << { 'level' => 3, 'message' => 'Only iCalendar version 2.0 as defined in rfc5545 is supported.', 'node' => self } end end uid_list = {} components_found = 0 component_types = [] children.each do |child| next unless child.is_a?(Component) components_found += 1 next unless ['VEVENT', 'VTODO', 'VJOURNAL'].include?(child.name) component_types << child.name uid = child['UID'].to_s is_master = child.key?('RECURRENCE-ID') ? 0 : 1 if uid_list.key?(uid) uid_list[uid]['count'] += 1 if is_master == 1 && uid_list[uid]['hasMaster'] > 0 warnings << { 'level' => 3, 'message' => "More than one master object was found for the object with UID #{uid}", 'node' => self } end uid_list[uid]['hasMaster'] += is_master else uid_list[uid] = { 'count' => 1, 'hasMaster' => is_master } end end if components_found == 0 warnings << { 'level' => 3, 'message' => 'An iCalendar object must have at least 1 component.', 'node' => self } end if & PROFILE_CALDAV > 0 if uid_list.size > 1 warnings << { 'level' => 3, 'message' => 'A calendar object on a CalDAV server may only have components with the same UID.', 'node' => self } end if component_types.size == 0 warnings << { 'level' => 3, 'message' => 'A calendar object on a CalDAV server must have at least 1 component (VTODO, VEVENT, VJOURNAL).', 'node' => self } end if component_types.uniq.size > 1 warnings << { 'level' => 3, 'message' => 'A calendar object on a CalDAV server may only have 1 type of component (VEVENT, VTODO or VJOURNAL).', 'node' => self } end if key?('METHOD') warnings << { 'level' => 3, 'message' => 'A calendar object on a CalDAV server MUST NOT have a METHOD property.', 'node' => self } end end warnings end |
#validation_rules ⇒ array
A simple list of validation rules.
This is simply a list of properties, and how many times they either must or must not appear.
Possible values per property:
* 0 - Must not appear.
* 1 - Must appear exactly once.
* + - Must appear at least once.
* * - Can appear any number of times.
* ? - May appear, but not more than once.
It is also possible to specify defaults and severity levels for violating the rule.
See the VEVENT implementation for getValidationRules for a more complex example.
315 316 317 318 319 320 321 322 323 |
# File 'lib/tilia/v_object/component/v_calendar.rb', line 315 def validation_rules { 'PRODID' => 1, 'VERSION' => 1, 'CALSCALE' => '?', 'METHOD' => '?' } end |