zimbra 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
- data/.irbrc +6 -0
- data/Gemfile +6 -0
- data/README +3 -2
- data/Rakefile +1 -0
- data/lib/zimbra.rb +31 -6
- data/lib/zimbra/appointment.rb +274 -0
- data/lib/zimbra/appointment/alarm.rb +101 -0
- data/lib/zimbra/appointment/attendee.rb +97 -0
- data/lib/zimbra/appointment/invite.rb +360 -0
- data/lib/zimbra/appointment/recur_exception.rb +83 -0
- data/lib/zimbra/appointment/recur_rule.rb +184 -0
- data/lib/zimbra/appointment/reply.rb +91 -0
- data/lib/zimbra/calendar.rb +27 -0
- data/lib/zimbra/delegate_auth_token.rb +49 -0
- data/lib/zimbra/ext/hash.rb +72 -0
- data/lib/zimbra/extra/date_helpers.rb +111 -0
- data/lib/zimbra/folder.rb +100 -0
- data/lib/zimbra/handsoap_account_service.rb +44 -0
- data/lib/zimbra/handsoap_service.rb +1 -1
- data/lib/zimbra/version.rb +3 -0
- data/spec/fixtures/xml_api_requests/appointments/create.xml +84 -0
- data/spec/fixtures/xml_api_responses/alarms/15_minutes_before.xml +26 -0
- data/spec/fixtures/xml_api_responses/alarms/using_all_intervals.xml +26 -0
- data/spec/fixtures/xml_api_responses/appointments/appointment_response_1.xml +40 -0
- data/spec/fixtures/xml_api_responses/attendees/one_attendee_and_one_reply.xml +27 -0
- data/spec/fixtures/xml_api_responses/attendees/three_attendees_response_1.xml +30 -0
- data/spec/fixtures/xml_api_responses/multiple_invites/recurring_with_exceptions.xml +109 -0
- data/spec/fixtures/xml_api_responses/recur_rules/day_27_of_every_2_months.xml +27 -0
- data/spec/fixtures/xml_api_responses/recur_rules/every_2_days.xml +26 -0
- data/spec/fixtures/xml_api_responses/recur_rules/every_3_weeks_on_tuesday_and_friday.xml +30 -0
- data/spec/fixtures/xml_api_responses/recur_rules/every_day_50_instances.xml +27 -0
- data/spec/fixtures/xml_api_responses/recur_rules/every_monday_wednesday_friday.xml +31 -0
- data/spec/fixtures/xml_api_responses/recur_rules/every_tuesday.xml +29 -0
- data/spec/fixtures/xml_api_responses/recur_rules/every_weekday_with_end_date.xml +34 -0
- data/spec/fixtures/xml_api_responses/recur_rules/every_year_on_february_2.xml +28 -0
- data/spec/fixtures/xml_api_responses/recur_rules/first_day_of_every_month.xml +36 -0
- data/spec/fixtures/xml_api_responses/recur_rules/first_monday_of_every_february.xml +31 -0
- data/spec/fixtures/xml_api_responses/recur_rules/first_weekend_day_of_every_month.xml +31 -0
- data/spec/fixtures/xml_api_responses/recur_rules/last_day_of_every_month.xml +36 -0
- data/spec/fixtures/xml_api_responses/recur_rules/second_day_of_every_2_months.xml +36 -0
- data/spec/fixtures/xml_api_responses/recur_rules/second_wednesday_of_every_month.xml +29 -0
- data/spec/fixtures/xml_api_responses/recur_rules/weekly_with_an_exception.xml +44 -0
- data/spec/spec_helper.rb +32 -0
- data/spec/zimbra/acl_spec.rb +11 -0
- data/spec/zimbra/appointment/alarm_spec.rb +33 -0
- data/spec/zimbra/appointment/invite_spec.rb +62 -0
- data/spec/zimbra/appointment/recur_rule_spec.rb +307 -0
- data/spec/zimbra/appointment_spec.rb +175 -0
- data/spec/zimbra/common_elements_spec.rb +33 -0
- data/spec/zimbra/distribution_list_spec.rb +54 -0
- data/zimbra.gemspec +28 -0
- metadata +165 -68
data/.gitignore
ADDED
data/.irbrc
ADDED
data/Gemfile
ADDED
data/README
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
EXAMPLE USAGE
|
2
2
|
=============
|
3
3
|
|
4
|
-
Zimbra.
|
4
|
+
Zimbra.admin_api_url = 'https://mail.server.test:7071/service/admin/soap'
|
5
|
+
Zimbra.account_api_url = 'https://mail.server.test/service/soap'
|
5
6
|
Zimbra.login('admin@example.com','secret')
|
6
7
|
|
7
8
|
d = Zimbra::Domain.create('luser.com')
|
@@ -10,4 +11,4 @@ dl = Zimbra::DistributionList.create('info@luser.com')
|
|
10
11
|
dl.admin_group = true
|
11
12
|
dl.save
|
12
13
|
|
13
|
-
|
14
|
+
Zimbra.account_login('user@example.com')
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/zimbra.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
$:.unshift(File.join(File.dirname(__FILE__)))
|
2
2
|
require 'zimbra/handsoap_service'
|
3
|
+
require 'zimbra/handsoap_account_service'
|
3
4
|
require 'zimbra/auth'
|
4
5
|
require 'zimbra/cos'
|
5
6
|
require 'zimbra/domain'
|
@@ -7,20 +8,34 @@ require 'zimbra/distribution_list'
|
|
7
8
|
require 'zimbra/account'
|
8
9
|
require 'zimbra/acl'
|
9
10
|
require 'zimbra/common_elements'
|
11
|
+
require 'zimbra/delegate_auth_token'
|
12
|
+
require 'zimbra/folder'
|
13
|
+
require 'zimbra/calendar'
|
14
|
+
require 'zimbra/appointment'
|
15
|
+
require 'zimbra/ext/hash'
|
16
|
+
require 'zimbra/extra/date_helpers'
|
10
17
|
|
11
18
|
# Manages a Zimbra SOAP session. Offers ability to set the endpoint URL, log in, and enable debugging.
|
12
19
|
module Zimbra
|
13
20
|
class << self
|
14
21
|
|
15
22
|
# The URL that will be used to contact the Zimbra SOAP service
|
16
|
-
def
|
17
|
-
@@
|
23
|
+
def admin_api_url
|
24
|
+
@@admin_api_url
|
18
25
|
end
|
19
26
|
# Sets the URL of the Zimbra SOAP service
|
20
|
-
def
|
21
|
-
@@
|
27
|
+
def admin_api_url=(url)
|
28
|
+
@@admin_api_url = url
|
22
29
|
end
|
23
|
-
|
30
|
+
|
31
|
+
def account_api_url
|
32
|
+
@@account_api_url
|
33
|
+
end
|
34
|
+
|
35
|
+
def account_api_url=(url)
|
36
|
+
@@account_api_url = url
|
37
|
+
end
|
38
|
+
|
24
39
|
# Turn debugging on/off. Outputs full SOAP conversations to stdout.
|
25
40
|
# Zimbra.debug = true
|
26
41
|
# Zimbra.debug = false
|
@@ -38,6 +53,10 @@ module Zimbra
|
|
38
53
|
def auth_token
|
39
54
|
@@auth_token
|
40
55
|
end
|
56
|
+
|
57
|
+
def account_auth_token
|
58
|
+
@@account_auth_token
|
59
|
+
end
|
41
60
|
|
42
61
|
# Log into the zimbra SOAP service. This is required before any other action is performed
|
43
62
|
# If a login has already been performed, another login will not be attempted
|
@@ -48,8 +67,14 @@ module Zimbra
|
|
48
67
|
|
49
68
|
# re-log into the zimbra SOAP service
|
50
69
|
def reset_login(username, password)
|
51
|
-
puts "Logging into zimbra as #{username}"
|
52
70
|
@@auth_token = Auth.login(username, password)
|
53
71
|
end
|
72
|
+
|
73
|
+
def account_login(username)
|
74
|
+
delegate_auth_token = DelegateAuthToken.for_account_name(username)
|
75
|
+
return false unless delegate_auth_token
|
76
|
+
@@account_auth_token = delegate_auth_token.token
|
77
|
+
true
|
78
|
+
end
|
54
79
|
end
|
55
80
|
end
|
@@ -0,0 +1,274 @@
|
|
1
|
+
# zmsoap -z -m mail03@greenviewdata.com SearchRequest @types="appointment" @query="inid:10"
|
2
|
+
# http://files.zimbra.com/docs/soap_api/8.0.4/soap-docs-804/api-reference/zimbraMail/Search.html
|
3
|
+
# GetRecurRequest
|
4
|
+
|
5
|
+
module Zimbra
|
6
|
+
class Appointment
|
7
|
+
autoload :RecurRule, 'zimbra/appointment/recur_rule'
|
8
|
+
autoload :Alarm, 'zimbra/appointment/alarm'
|
9
|
+
autoload :Attendee, 'zimbra/appointment/attendee'
|
10
|
+
autoload :Reply, 'zimbra/appointment/reply'
|
11
|
+
autoload :Invite, 'zimbra/appointment/invite'
|
12
|
+
autoload :RecurException, 'zimbra/appointment/recur_exception'
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def find_all_by_calendar_id(calendar_id)
|
16
|
+
AppointmentService.find_all_by_calendar_id(calendar_id).collect { |attrs| new_from_zimbra_attributes(attrs.merge(:loaded_from_search => true)) }
|
17
|
+
end
|
18
|
+
|
19
|
+
def find_all_by_calendar_id_since(calendar_id, since_date)
|
20
|
+
AppointmentService.find_all_by_calendar_id_since(calendar_id, since_date).collect { |attrs| new_from_zimbra_attributes(attrs.merge(:loaded_from_search => true)) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def find(appointment_id)
|
24
|
+
attrs = AppointmentService.find(appointment_id)
|
25
|
+
return nil unless attrs
|
26
|
+
new_from_zimbra_attributes(attrs)
|
27
|
+
end
|
28
|
+
|
29
|
+
def new_from_zimbra_attributes(zimbra_attributes)
|
30
|
+
new(parse_zimbra_attributes(zimbra_attributes))
|
31
|
+
end
|
32
|
+
|
33
|
+
def parse_zimbra_attributes(zimbra_attributes)
|
34
|
+
zimbra_attributes = Zimbra::Hash.symbolize_keys(zimbra_attributes.dup, true)
|
35
|
+
|
36
|
+
return {} unless zimbra_attributes.has_key?(:appt) && zimbra_attributes[:appt].has_key?(:attributes)
|
37
|
+
|
38
|
+
{
|
39
|
+
:id => zimbra_attributes[:appt][:attributes][:id],
|
40
|
+
:uid => zimbra_attributes[:appt][:attributes][:uid],
|
41
|
+
:revision => zimbra_attributes[:appt][:attributes][:rev],
|
42
|
+
:calendar_id => zimbra_attributes[:appt][:attributes][:l],
|
43
|
+
:size => zimbra_attributes[:appt][:attributes][:s],
|
44
|
+
:replies => zimbra_attributes[:appt][:replies],
|
45
|
+
:invites_zimbra_attributes => zimbra_attributes[:appt][:inv],
|
46
|
+
:date => zimbra_attributes[:appt][:attributes][:d],
|
47
|
+
:loaded_from_search => zimbra_attributes[:loaded_from_search]
|
48
|
+
}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
ATTRS = [
|
53
|
+
:id, :uid, :date, :revision, :size, :calendar_id,
|
54
|
+
:replies, :invites, :invites_zimbra_attributes, :invites_attributes
|
55
|
+
] unless const_defined?(:ATTRS)
|
56
|
+
|
57
|
+
attr_accessor *ATTRS
|
58
|
+
attr_reader :loaded_from_search
|
59
|
+
|
60
|
+
def initialize(args = {})
|
61
|
+
self.attributes = args
|
62
|
+
@loaded_from_search = args[:loaded_from_search] || false
|
63
|
+
end
|
64
|
+
|
65
|
+
def attributes=(args = {})
|
66
|
+
ATTRS.each do |attr_name|
|
67
|
+
self.send(:"#{attr_name}=", args[attr_name]) if args.has_key?(attr_name)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def reload
|
72
|
+
raw_attributes = AppointmentService.find(id)
|
73
|
+
self.attributes = Zimbra::Appointment.parse_zimbra_attributes(raw_attributes)
|
74
|
+
@loaded_from_search = false
|
75
|
+
end
|
76
|
+
|
77
|
+
def replies
|
78
|
+
reload if loaded_from_search
|
79
|
+
@replies
|
80
|
+
end
|
81
|
+
|
82
|
+
def replies=(replies_attributes)
|
83
|
+
return @replies = [] unless replies_attributes
|
84
|
+
|
85
|
+
replies_attributes = replies_attributes[:reply].is_a?(Array) ? replies_attributes[:reply] : [ replies_attributes[:reply] ]
|
86
|
+
@replies = replies_attributes.collect { |attrs| Zimbra::Appointment::Reply.new_from_zimbra_attributes(attrs[:attributes]) }
|
87
|
+
end
|
88
|
+
|
89
|
+
def invites
|
90
|
+
reload if loaded_from_search
|
91
|
+
@invites
|
92
|
+
end
|
93
|
+
|
94
|
+
def invites_attributes=(attributes)
|
95
|
+
return @invites = nil unless attributes
|
96
|
+
|
97
|
+
attributes = attributes.is_a?(Array) ? attributes : [ attributes ]
|
98
|
+
@invites = attributes.collect { |attrs| Zimbra::Appointment::Invite.new(attrs.merge( { :appointment => self } )) }
|
99
|
+
end
|
100
|
+
|
101
|
+
def invites_zimbra_attributes=(attributes)
|
102
|
+
return @invites = nil unless attributes
|
103
|
+
|
104
|
+
attributes = attributes.is_a?(Array) ? attributes : [ attributes ]
|
105
|
+
@invites = attributes.collect { |attrs| Zimbra::Appointment::Invite.new_from_zimbra_attributes(attrs.merge( { :appointment => self } )) }
|
106
|
+
end
|
107
|
+
|
108
|
+
def date=(val)
|
109
|
+
if val.is_a?(Integer)
|
110
|
+
@date = parse_date_in_seconds(val)
|
111
|
+
else
|
112
|
+
@date = val
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def create_xml(document, invite_id = nil)
|
117
|
+
document.add "m" do |mime|
|
118
|
+
mime.set_attr "l", calendar_id
|
119
|
+
|
120
|
+
invites.each do |invite|
|
121
|
+
next unless invite_id.nil? || invite_id == invite.id
|
122
|
+
|
123
|
+
mime.add "inv" do |invite_element|
|
124
|
+
invite.create_xml(invite_element)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
document
|
130
|
+
end
|
131
|
+
|
132
|
+
def destroy
|
133
|
+
invites.each do |invite|
|
134
|
+
AppointmentService.cancel(self, invite.id)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def save
|
139
|
+
if new_record?
|
140
|
+
response = Zimbra::AppointmentService.create(self)
|
141
|
+
invites.first.id = response[:invite_id]
|
142
|
+
@id = response[:id]
|
143
|
+
else
|
144
|
+
invites.each do |invite|
|
145
|
+
Zimbra::AppointmentService.update(self, invite.id)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def new_record?
|
151
|
+
id.nil?
|
152
|
+
end
|
153
|
+
|
154
|
+
def id_with_invite_id
|
155
|
+
"#{id}-#{invites.first.id}"
|
156
|
+
end
|
157
|
+
|
158
|
+
def last_instance_time
|
159
|
+
instance_times = Zimbra::AppointmentService.find_all_instances_of_an_appointment(self)
|
160
|
+
return nil unless instance_times && instance_times.count > 0
|
161
|
+
instance_times.max
|
162
|
+
end
|
163
|
+
|
164
|
+
private
|
165
|
+
|
166
|
+
def parse_date_in_seconds(seconds)
|
167
|
+
Time.at(seconds / 1000)
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
|
172
|
+
class AppointmentService < HandsoapAccountService
|
173
|
+
def find_all_by_calendar_id(calendar_id)
|
174
|
+
xml = invoke("n2:SearchRequest") do |message|
|
175
|
+
Builder.find_all_with_query(message, "inid:#{calendar_id}")
|
176
|
+
end
|
177
|
+
Parser.get_search_response(xml)
|
178
|
+
end
|
179
|
+
|
180
|
+
def find_all_instances_of_an_appointment(appointment)
|
181
|
+
xml = invoke("n2:SearchRequest") do |message|
|
182
|
+
message.set_attr 'query', "date:#{appointment.date.to_i * 1000}"
|
183
|
+
message.set_attr 'types', 'appointment'
|
184
|
+
message.set_attr 'calExpandInstStart', '1'
|
185
|
+
message.set_attr 'calExpandInstEnd', (Time.now + (86400 * 365 * 10)).to_i * 1000
|
186
|
+
end
|
187
|
+
response_hash = Zimbra::Hash.from_xml(xml.document.to_s)
|
188
|
+
response_hash = response_hash[:Envelope][:Body][:SearchResponse]
|
189
|
+
appointments = response_hash[:appt].is_a?(Array) ? response_hash[:appt] : [response_hash[:appt]]
|
190
|
+
appt_hash = appointments.find { |appt| appt[:attributes][:id] == appointment.id }
|
191
|
+
instances = appt_hash[:inst].is_a?(Array) ? appt_hash[:inst] : [appt_hash[:inst]]
|
192
|
+
instances.collect { |inst| Time.at(inst[:attributes][:s] / 1000) }
|
193
|
+
end
|
194
|
+
|
195
|
+
def find_all_by_calendar_id_since(calendar_id, since_date)
|
196
|
+
xml = invoke("n2:SearchRequest") do |message|
|
197
|
+
Builder.find_all_with_query(message, "inid:#{calendar_id} AND date:>#{since_date.to_i}")
|
198
|
+
end
|
199
|
+
Parser.get_search_response(xml)
|
200
|
+
end
|
201
|
+
|
202
|
+
def find(appointment_id)
|
203
|
+
xml = invoke("n2:GetAppointmentRequest") do |message|
|
204
|
+
Builder.find_by_id(message, appointment_id)
|
205
|
+
end
|
206
|
+
return nil unless xml
|
207
|
+
Parser.appointment_response(xml/"//n2:appt")
|
208
|
+
end
|
209
|
+
|
210
|
+
def create(appointment)
|
211
|
+
xml = invoke("n2:CreateAppointmentRequest") do |message|
|
212
|
+
Builder.create(message, appointment)
|
213
|
+
end
|
214
|
+
response_hash = Zimbra::Hash.from_xml(xml.document.to_s)
|
215
|
+
id = response_hash[:Envelope][:Body][:CreateAppointmentResponse][:attributes][:apptId] rescue nil
|
216
|
+
invite_id = response_hash[:Envelope][:Body][:CreateAppointmentResponse][:attributes][:invId].gsub(/#{id}\-/, '').to_i rescue nil
|
217
|
+
{ :id => id, :invite_id => invite_id }
|
218
|
+
end
|
219
|
+
|
220
|
+
def update(appointment, invite_id)
|
221
|
+
xml = invoke("n2:ModifyAppointmentRequest") do |message|
|
222
|
+
Builder.update(message, appointment, invite_id)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def cancel(appointment, invite_id)
|
227
|
+
xml = invoke("n2:CancelAppointmentRequest") do |message|
|
228
|
+
Builder.cancel(message, appointment.id, invite_id)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
class Builder
|
233
|
+
class << self
|
234
|
+
def find_all_with_query(message, query)
|
235
|
+
message.set_attr 'query', query
|
236
|
+
message.set_attr 'types', 'appointment'
|
237
|
+
end
|
238
|
+
|
239
|
+
def find_by_id(message, id)
|
240
|
+
message.set_attr 'id', id
|
241
|
+
end
|
242
|
+
|
243
|
+
def create(message, appointment)
|
244
|
+
appointment.create_xml(message)
|
245
|
+
end
|
246
|
+
|
247
|
+
def update(message, appointment, invite_id)
|
248
|
+
message.set_attr 'id', "#{appointment.id}-#{invite_id}"
|
249
|
+
appointment.create_xml(message, invite_id)
|
250
|
+
end
|
251
|
+
|
252
|
+
def cancel(message, appointment_id, invite_id)
|
253
|
+
message.set_attr 'id', "#{appointment_id}-#{invite_id}"
|
254
|
+
message.set_attr 'comp', 0
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
class Parser
|
260
|
+
class << self
|
261
|
+
def get_search_response(response)
|
262
|
+
(response/"//n2:appt").collect do |node|
|
263
|
+
Zimbra::Hash.from_xml(node.to_xml)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
def appointment_response(node)
|
268
|
+
# It's much easier to deal with this as a hash
|
269
|
+
Zimbra::Hash.from_xml(node.to_xml)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module Zimbra
|
2
|
+
class Appointment
|
3
|
+
class Alarm
|
4
|
+
class << self
|
5
|
+
def new_from_zimbra_attributes(zimbra_attributes)
|
6
|
+
new(parse_zimbra_attributes(zimbra_attributes))
|
7
|
+
end
|
8
|
+
|
9
|
+
# <alarm action="DISPLAY">
|
10
|
+
# <trigger>
|
11
|
+
# <rel neg="1" m="5" related="START"/>
|
12
|
+
# </trigger>
|
13
|
+
# <desc/>
|
14
|
+
# </alarm>
|
15
|
+
def parse_zimbra_attributes(zimbra_attributes)
|
16
|
+
attrs = { appointment_invite: zimbra_attributes[:appointment_invite] }
|
17
|
+
zimbra_attributes = Zimbra::Hash.symbolize_keys(zimbra_attributes.dup, true)
|
18
|
+
zimbra_attributes = zimbra_attributes[:trigger][:rel][:attributes]
|
19
|
+
|
20
|
+
duration_negative = (zimbra_attributes[:neg] && zimbra_attributes[:neg] == 1) ? true : false
|
21
|
+
|
22
|
+
attrs.merge({
|
23
|
+
duration_negative: duration_negative,
|
24
|
+
weeks: zimbra_attributes[:w],
|
25
|
+
days: zimbra_attributes[:d],
|
26
|
+
hours: zimbra_attributes[:h],
|
27
|
+
minutes: zimbra_attributes[:m],
|
28
|
+
seconds: zimbra_attributes[:s],
|
29
|
+
when: zimbra_attributes[:related] == "START" ? :start : :end,
|
30
|
+
repeat_count: zimbra_attributes[:count],
|
31
|
+
})
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
ATTRS = [:appointment_invite, :duration_negative, :weeks, :days, :hours, :minutes, :seconds, :when, :repeat_count] unless const_defined?(:ATTRS)
|
36
|
+
|
37
|
+
attr_accessor *ATTRS
|
38
|
+
|
39
|
+
def initialize(args = {})
|
40
|
+
@duration_negative = true
|
41
|
+
@when = :start
|
42
|
+
self.attributes = args
|
43
|
+
end
|
44
|
+
|
45
|
+
# take attributes by the xml name or our more descriptive name
|
46
|
+
def attributes=(args = {})
|
47
|
+
ATTRS.each do |attr_name|
|
48
|
+
if args.has_key?(attr_name)
|
49
|
+
self.send(:"#{attr_name}=", args[attr_name])
|
50
|
+
elsif args.has_key?(attr_name.to_s)
|
51
|
+
self.send(:"#{attr_name}=", args[attr_name.to_s])
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_hash(options = {})
|
57
|
+
hash = ATTRS.inject({}) do |attr_hash, attr_name|
|
58
|
+
attr_hash[attr_name] = self.send(:"#{attr_name}")
|
59
|
+
attr_hash
|
60
|
+
end
|
61
|
+
hash.reject! { |key, value| options[:except].include?(key.to_sym) || options[:except].include?(key.to_s) } if options[:except]
|
62
|
+
hash.reject! { |key, value| !options[:only].include?(key.to_sym) && !options[:only].include?(key.to_s) } if options[:only]
|
63
|
+
hash
|
64
|
+
end
|
65
|
+
|
66
|
+
def create_xml(document)
|
67
|
+
document.add "trigger" do |trigger_element|
|
68
|
+
trigger_element.add "rel" do |rel_element|
|
69
|
+
rel_element.set_attr "neg", duration_negative ? 1 : 0
|
70
|
+
rel_element.set_attr "w", weeks if weeks && weeks > 0
|
71
|
+
rel_element.set_attr "d", days if days && days > 0
|
72
|
+
rel_element.set_attr "h", hours if hours && hours > 0
|
73
|
+
rel_element.set_attr "m", minutes if minutes && minutes > 0
|
74
|
+
rel_element.set_attr "s", seconds if seconds && seconds > 0
|
75
|
+
rel_element.set_attr "related", self.when.to_s.upcase
|
76
|
+
rel_element.set_attr "count", repeat_count if repeat_count && repeat_count > 0
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def date_time_of_alarm
|
82
|
+
return nil if appointment_invite.nil?
|
83
|
+
|
84
|
+
date_to_calc_from = if self.when == :start
|
85
|
+
appointment_invite.start_date_time
|
86
|
+
else
|
87
|
+
appointment_invite.end_date_time
|
88
|
+
end
|
89
|
+
|
90
|
+
total_seconds = seconds || 0
|
91
|
+
total_seconds += minutes * 60 if minutes
|
92
|
+
total_seconds += hours * 3600 if hours
|
93
|
+
total_seconds += days * 86400 if days
|
94
|
+
total_seconds += weeks * 86400 * 7 if weeks
|
95
|
+
total_seconds *= -1 if duration_negative
|
96
|
+
|
97
|
+
date_to_calc_from + total_seconds
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|