zimbra-soap-api 0.0.7.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.irbrc +6 -0
  4. data/Gemfile +6 -0
  5. data/README +21 -0
  6. data/Rakefile +1 -0
  7. data/lib/zimbra.rb +94 -0
  8. data/lib/zimbra/account.rb +94 -0
  9. data/lib/zimbra/acl.rb +63 -0
  10. data/lib/zimbra/alias.rb +4 -0
  11. data/lib/zimbra/appointment.rb +274 -0
  12. data/lib/zimbra/appointment/alarm.rb +101 -0
  13. data/lib/zimbra/appointment/attendee.rb +97 -0
  14. data/lib/zimbra/appointment/invite.rb +360 -0
  15. data/lib/zimbra/appointment/recur_exception.rb +83 -0
  16. data/lib/zimbra/appointment/recur_rule.rb +184 -0
  17. data/lib/zimbra/appointment/reply.rb +91 -0
  18. data/lib/zimbra/auth.rb +46 -0
  19. data/lib/zimbra/base.rb +217 -0
  20. data/lib/zimbra/calendar.rb +27 -0
  21. data/lib/zimbra/common_elements.rb +51 -0
  22. data/lib/zimbra/cos.rb +124 -0
  23. data/lib/zimbra/delegate_auth_token.rb +49 -0
  24. data/lib/zimbra/directory.rb +175 -0
  25. data/lib/zimbra/distribution_list.rb +147 -0
  26. data/lib/zimbra/domain.rb +63 -0
  27. data/lib/zimbra/ext/handsoap_curb_driver.rb +21 -0
  28. data/lib/zimbra/ext/hash.rb +72 -0
  29. data/lib/zimbra/ext/string.rb +10 -0
  30. data/lib/zimbra/extra/date_helpers.rb +111 -0
  31. data/lib/zimbra/folder.rb +100 -0
  32. data/lib/zimbra/handsoap_account_service.rb +44 -0
  33. data/lib/zimbra/handsoap_service.rb +75 -0
  34. data/lib/zimbra/version.rb +3 -0
  35. data/spec/fixtures/xml_api_requests/appointments/create.xml +84 -0
  36. data/spec/fixtures/xml_api_responses/alarms/15_minutes_before.xml +26 -0
  37. data/spec/fixtures/xml_api_responses/alarms/using_all_intervals.xml +26 -0
  38. data/spec/fixtures/xml_api_responses/appointments/appointment_response_1.xml +40 -0
  39. data/spec/fixtures/xml_api_responses/attendees/one_attendee_and_one_reply.xml +27 -0
  40. data/spec/fixtures/xml_api_responses/attendees/three_attendees_response_1.xml +30 -0
  41. data/spec/fixtures/xml_api_responses/multiple_invites/recurring_with_exceptions.xml +109 -0
  42. data/spec/fixtures/xml_api_responses/recur_rules/day_27_of_every_2_months.xml +27 -0
  43. data/spec/fixtures/xml_api_responses/recur_rules/every_2_days.xml +26 -0
  44. data/spec/fixtures/xml_api_responses/recur_rules/every_3_weeks_on_tuesday_and_friday.xml +30 -0
  45. data/spec/fixtures/xml_api_responses/recur_rules/every_day_50_instances.xml +27 -0
  46. data/spec/fixtures/xml_api_responses/recur_rules/every_monday_wednesday_friday.xml +31 -0
  47. data/spec/fixtures/xml_api_responses/recur_rules/every_tuesday.xml +29 -0
  48. data/spec/fixtures/xml_api_responses/recur_rules/every_weekday_with_end_date.xml +34 -0
  49. data/spec/fixtures/xml_api_responses/recur_rules/every_year_on_february_2.xml +28 -0
  50. data/spec/fixtures/xml_api_responses/recur_rules/first_day_of_every_month.xml +36 -0
  51. data/spec/fixtures/xml_api_responses/recur_rules/first_monday_of_every_february.xml +31 -0
  52. data/spec/fixtures/xml_api_responses/recur_rules/first_weekend_day_of_every_month.xml +31 -0
  53. data/spec/fixtures/xml_api_responses/recur_rules/last_day_of_every_month.xml +36 -0
  54. data/spec/fixtures/xml_api_responses/recur_rules/second_day_of_every_2_months.xml +36 -0
  55. data/spec/fixtures/xml_api_responses/recur_rules/second_wednesday_of_every_month.xml +29 -0
  56. data/spec/fixtures/xml_api_responses/recur_rules/weekly_with_an_exception.xml +44 -0
  57. data/spec/spec_helper.rb +32 -0
  58. data/spec/zimbra/acl_spec.rb +11 -0
  59. data/spec/zimbra/appointment/alarm_spec.rb +33 -0
  60. data/spec/zimbra/appointment/invite_spec.rb +62 -0
  61. data/spec/zimbra/appointment/recur_rule_spec.rb +307 -0
  62. data/spec/zimbra/appointment_spec.rb +175 -0
  63. data/spec/zimbra/common_elements_spec.rb +33 -0
  64. data/spec/zimbra/distribution_list_spec.rb +54 -0
  65. data/zimbra.gemspec +28 -0
  66. metadata +197 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e2c290f23c8fc11e17f8246a10d6272a38daedc3
4
+ data.tar.gz: cdc7b8ed6e55a96aeb8f7b0d83164ba014742c4b
5
+ SHA512:
6
+ metadata.gz: f180678a46a734e9fdf2d662d797a3f8a063be93519da28302c601527463cb35175f5495c6546092db486ada7928ec344638465d0f5923dd2b9a39f2072e5a34
7
+ data.tar.gz: 60eb433cb481fb18414316d3c0d00bef8643abaab3c2559b31aeb4e51e54e8fc1d95690a85b076464f5cd578c605ea8a1ac67ac12f4e5cccbb3f8769407ce5bf
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.irbrc ADDED
@@ -0,0 +1,6 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
2
+ require 'zimbra.rb'
3
+
4
+ puts "* Loaded Zimbra Gem"
5
+
6
+
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ end
data/README ADDED
@@ -0,0 +1,21 @@
1
+ EXAMPLE USAGE
2
+ =============
3
+
4
+ Zimbra.admin_api_url = 'https://mail.server.test:7071/service/admin/soap'
5
+ Zimbra.account_api_url = 'https://mail.server.test/service/soap'
6
+ Zimbra.login('admin@example.com','secret')
7
+
8
+ d = Zimbra::Domain.create('luser.com')
9
+
10
+ dl = Zimbra::DistributionList.create('info@luser.com')
11
+ dl.admin_group = true
12
+ dl.save
13
+
14
+ Zimbra.account_login('user@example.com')
15
+
16
+ calendar = Zimbra::Calendar.all.first
17
+ appointment = calendar.appointments.first
18
+
19
+ # Get Account info
20
+ account = Zimbra::Account.find_by_name("pbruna@example.com")
21
+ account.get_attributes ["displayName", "sn", "zimbraMailAlias"]
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,94 @@
1
+ $:.unshift(File.join(File.dirname(__FILE__)))
2
+ require 'zimbra/handsoap_service'
3
+ require 'zimbra/handsoap_account_service'
4
+ require 'zimbra/base'
5
+ require 'zimbra/auth'
6
+ require 'zimbra/cos'
7
+ require 'zimbra/domain'
8
+ require 'zimbra/distribution_list'
9
+ require 'zimbra/account'
10
+ require 'zimbra/acl'
11
+ require 'zimbra/common_elements'
12
+ require 'zimbra/delegate_auth_token'
13
+ require 'zimbra/folder'
14
+ require 'zimbra/calendar'
15
+ require 'zimbra/appointment'
16
+ require 'zimbra/directory'
17
+ require 'zimbra/alias'
18
+ require 'zimbra/ext/hash'
19
+ require 'zimbra/ext/string'
20
+ require 'zimbra/ext/handsoap_curb_driver'
21
+ require 'zimbra/extra/date_helpers'
22
+
23
+ # Manages a Zimbra SOAP session. Offers ability to set the endpoint URL, log in, and enable debugging.
24
+ module Zimbra
25
+ class << self
26
+
27
+ # The URL that will be used to contact the Zimbra SOAP service
28
+ def admin_api_url
29
+ @@admin_api_url
30
+ end
31
+ # Sets the URL of the Zimbra SOAP service
32
+ def admin_api_url=(url)
33
+ @@admin_api_url = url
34
+ end
35
+
36
+ def account_api_url
37
+ @@account_api_url
38
+ end
39
+
40
+ def account_api_url=(url)
41
+ @@account_api_url = url
42
+ end
43
+
44
+ # Turn debugging on/off. Outputs full SOAP conversations to stdout.
45
+ # Zimbra.debug = true
46
+ # Zimbra.debug = false
47
+ def debug=(val)
48
+ Handsoap::Service.logger = (val ? $stdout : nil)
49
+ @@debug = val
50
+ end
51
+
52
+ # Whether debugging is enabled
53
+ def debug
54
+ @@debug ||= false
55
+ end
56
+
57
+ # Checking if we can update the token
58
+ def auth_token=(token)
59
+ @@auth_token = token
60
+ end
61
+
62
+ # Authorization token - obtained after successful login
63
+ def auth_token
64
+ @@auth_token
65
+ end
66
+
67
+ def session_lifetime
68
+ @@session_lifetime
69
+ end
70
+
71
+ def account_auth_token
72
+ @@account_auth_token
73
+ end
74
+
75
+ # Log into the zimbra SOAP service. This is required before any other action is performed
76
+ # If a login has already been performed, another login will not be attempted
77
+ def login(username, password)
78
+ return @@auth_token if defined?(@@auth_token) && @@auth_token
79
+ reset_login(username, password)
80
+ end
81
+
82
+ # re-log into the zimbra SOAP service
83
+ def reset_login(username, password)
84
+ @@auth_token, @@session_lifetime = Auth.login(username, password)
85
+ end
86
+
87
+ def account_login(username)
88
+ delegate_auth_token = DelegateAuthToken.for_account_name(username)
89
+ return false unless delegate_auth_token
90
+ @@account_auth_token = delegate_auth_token.token
91
+ true
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,94 @@
1
+ module Zimbra
2
+ class Account < Zimbra::Base
3
+ class << self
4
+
5
+ def create(name, password, attributes = {})
6
+ AccountService.create(name, password, attributes)
7
+ end
8
+
9
+ def acl_name
10
+ 'usr'
11
+ end
12
+
13
+ end
14
+
15
+ attr_accessor :cos_id
16
+
17
+ def initialize(id, name, zimbra_attrs = {}, node = nil)
18
+ super
19
+ self.cos_id = zimbra_attrs['zimbraCOSId']
20
+ self.delegated_admin = zimbra_attrs['zimbraIsDelegatedAdminAccount']
21
+ end
22
+
23
+ def delegated_admin=(val)
24
+ @delegated_admin = Zimbra::Boolean.read(val)
25
+ end
26
+
27
+ def delegated_admin?
28
+ @delegated_admin
29
+ end
30
+
31
+ def save
32
+ AccountService.modify(self)
33
+ end
34
+
35
+ def set_password(new_password)
36
+ AccountService.set_password(id, new_password)
37
+ end
38
+
39
+ def add_alias(alias_name)
40
+ AccountService.add_alias(self, alias_name)
41
+ end
42
+ end
43
+
44
+ # Doc Placeholder
45
+ class AccountService < HandsoapService
46
+ def create(name, password, attributes)
47
+ xml = invoke("n2:CreateAccountRequest") do |message|
48
+ Builder.create(message, name, password, attributes)
49
+ end
50
+ class_name = Zimbra::Account.class_name
51
+ Zimbra::BaseService::Parser.response(class_name, xml/"//n2:account")
52
+ end
53
+
54
+ def set_password(id, new_password)
55
+ xml = invoke('n2:SetPasswordRequest') do |message|
56
+ Builder.set_password(message, id, new_password)
57
+ end
58
+ true
59
+ end
60
+
61
+ def add_alias(account,alias_name)
62
+ xml = invoke('n2:AddAccountAliasRequest') do |message|
63
+ Builder.add_alias(message,account.id,alias_name)
64
+ end
65
+ end
66
+
67
+ # Doc Placeholder
68
+ class Builder
69
+ class << self
70
+ def create(message, name, password, attributes)
71
+ message.add 'name', name
72
+ message.add 'password', password
73
+ attributes.each do |k,v|
74
+ A.inject(message, k, v)
75
+ end
76
+ end
77
+
78
+ def set_password(message, id, new_password)
79
+ message.set_attr 'id', id
80
+ message.set_attr 'newPassword', new_password
81
+ end
82
+
83
+ def add_alias(message,id,alias_name)
84
+ message.add 'id', id
85
+ message.add 'alias', alias_name
86
+ end
87
+ end
88
+ end
89
+ class Parser
90
+ class << self
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,63 @@
1
+ module Zimbra
2
+ class ACL
3
+ class TargetObjectNotFound < StandardError; end
4
+
5
+ TARGET_CLASSES = [Zimbra::Domain, Zimbra::DistributionList, Zimbra::Cos, Zimbra::Account]
6
+ TARGET_MAPPINGS = TARGET_CLASSES.inject({}) do |hsh, klass|
7
+ hsh[klass.acl_name] = klass
8
+ hsh[klass] = klass.acl_name
9
+ hsh
10
+ end
11
+
12
+ class << self
13
+ def delete_all(xmldoc)
14
+ A.inject(xmldoc, 'zimbraACE', '', 'c' => '1')
15
+ end
16
+
17
+ def read(node)
18
+ list = A.read(node, 'zimbraACE')
19
+ return nil if list.nil?
20
+ list = [list] unless list.respond_to?(:map)
21
+ acls = list.map do |ace|
22
+ from_s(ace)
23
+ end
24
+ end
25
+
26
+ def from_zimbra(node)
27
+ from_s(node.to_s)
28
+ end
29
+
30
+ def from_s(value)
31
+ grantee_id, grantee_name, name = value.split(' ')
32
+ grantee_class = TARGET_MAPPINGS[grantee_name]
33
+ return "Target object not found for acl #{value}" if target_class.nil?
34
+ new(grantee_id: grantee_id, grantee_class: grantee_class, name: name)
35
+ end
36
+ end
37
+
38
+ attr_accessor :grantee_id, :grantee_class, :name, :grantee_name
39
+
40
+ def initialize(options = {})
41
+ if options[:grantee]
42
+ self.grantee_id = options[:grantee].id
43
+ self.grantee_class = options[:grantee].class
44
+ self.grantee_name = options[:grantee].grantee_name unless options[:grantee].grantee_name.nil?
45
+ else
46
+ self.grantee_id = options[:grantee_id]
47
+ self.grantee_class = options[:grantee_class]
48
+ self.grantee_name = options[:grantee_name] unless options[:grantee_name].nil?
49
+ end
50
+ self.name = options[:name]
51
+ end
52
+
53
+ def to_zimbra_acl_value
54
+ id = grantee_id
55
+ type = grantee_class.acl_name
56
+ "#{id} #{type} #{name}"
57
+ end
58
+
59
+ def apply(xmldoc)
60
+ A.inject(xmldoc, 'zimbraACE', to_zimbra_acl_value, 'c' => '1')
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,4 @@
1
+ module Zimbra
2
+ class Alias
3
+ end
4
+ 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