zimbra 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,83 @@
|
|
1
|
+
module Zimbra
|
2
|
+
class Appointment
|
3
|
+
class RecurException
|
4
|
+
class << self
|
5
|
+
def new_from_zimbra_attributes(zimbra_attributes)
|
6
|
+
return nil unless zimbra_attributes
|
7
|
+
new(parse_zimbra_attributes(zimbra_attributes))
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse_zimbra_attributes(zimbra_attributes)
|
11
|
+
zimbra_attributes = Zimbra::Hash.symbolize_keys(zimbra_attributes.dup, true)
|
12
|
+
|
13
|
+
{
|
14
|
+
:recurrence_id => zimbra_attributes[:d],
|
15
|
+
:timezone => zimbra_attributes[:tz],
|
16
|
+
:range_type => zimbra_attributes[:rangeType]
|
17
|
+
}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
ATTRS = [
|
22
|
+
:recurrence_id,
|
23
|
+
:timezone,
|
24
|
+
:range_type
|
25
|
+
] unless const_defined?(:ATTRS)
|
26
|
+
|
27
|
+
attr_accessor *ATTRS
|
28
|
+
|
29
|
+
def range_type=(val)
|
30
|
+
@range_type = parse_range_type(val)
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(args = {})
|
34
|
+
self.attributes = args
|
35
|
+
end
|
36
|
+
|
37
|
+
# take attributes by the xml name or our more descriptive name
|
38
|
+
def attributes=(args = {})
|
39
|
+
ATTRS.each do |attr_name|
|
40
|
+
self.send(:"#{attr_name}=", (args[attr_name] || args[attr_name.to_s]))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_hash(options = {})
|
45
|
+
hash = ATTRS.inject({}) do |attr_hash, attr_name|
|
46
|
+
attr_hash[attr_name] = self.send(:"#{attr_name}")
|
47
|
+
attr_hash
|
48
|
+
end
|
49
|
+
hash.reject! { |key, value| options[:except].include?(key.to_sym) || options[:except].include?(key.to_s) } if options[:except]
|
50
|
+
hash.reject! { |key, value| !options[:only].include?(key.to_sym) && !options[:only].include?(key.to_s) } if options[:only]
|
51
|
+
hash
|
52
|
+
end
|
53
|
+
|
54
|
+
def range_type_to_zimbra
|
55
|
+
possible_range_type_values.find { |k, v| v == range_type }.first rescue range_type
|
56
|
+
end
|
57
|
+
|
58
|
+
def create_xml(document)
|
59
|
+
document.add "exceptId" do |except_element|
|
60
|
+
except_element.set_attr "d", recurrence_id
|
61
|
+
except_element.set_attr "tz", timezone
|
62
|
+
except_element.set_attr "rangeType", range_type_to_zimbra if range_type
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def possible_range_type_values
|
69
|
+
@possible_range_type_values ||= {
|
70
|
+
1 => :none,
|
71
|
+
2 => :this_and_future,
|
72
|
+
3 => :this_and_prior
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
def parse_range_type(val)
|
77
|
+
int_val = val.to_int rescue nil
|
78
|
+
|
79
|
+
possible_range_type_values[int_val] || val
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
module Zimbra
|
2
|
+
class Appointment
|
3
|
+
class RecurRule
|
4
|
+
class << self
|
5
|
+
def new_from_zimbra_attributes(zimbra_attributes)
|
6
|
+
return nil unless zimbra_attributes
|
7
|
+
new(parse_zimbra_attributes(zimbra_attributes))
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse_zimbra_attributes(zimbra_attributes)
|
11
|
+
attrs = {}
|
12
|
+
|
13
|
+
zimbra_attributes = Zimbra::Hash.symbolize_keys(zimbra_attributes.dup, true)
|
14
|
+
zimbra_attributes = zimbra_attributes[:add][:rule]
|
15
|
+
|
16
|
+
attrs[:frequency] = zimbra_attributes[:attributes][:freq] if zimbra_attributes[:attributes]
|
17
|
+
attrs[:until_date] = zimbra_attributes[:until][:attributes][:d] if zimbra_attributes[:until]
|
18
|
+
attrs[:interval] = zimbra_attributes[:interval][:attributes][:ival] if zimbra_attributes[:interval]
|
19
|
+
attrs[:count] = zimbra_attributes[:count][:attributes][:num] if zimbra_attributes[:count]
|
20
|
+
|
21
|
+
if zimbra_attributes[:bysetpos]
|
22
|
+
attrs[:by_set_position] = zimbra_attributes[:bysetpos][:attributes][:poslist]
|
23
|
+
attrs[:by_set_position] = [attrs[:by_set_position]] unless attrs[:by_set_position].is_a?(Array)
|
24
|
+
end
|
25
|
+
|
26
|
+
if zimbra_attributes[:byday] && zimbra_attributes[:byday][:wkday] && zimbra_attributes[:byday][:wkday].is_a?(Array)
|
27
|
+
attrs[:by_day] = zimbra_attributes[:byday][:wkday].collect do |wkday|
|
28
|
+
wkday = Zimbra::Hash.symbolize_keys(wkday, true)
|
29
|
+
wkday_hash = { day: wkday[:attributes][:day] }
|
30
|
+
wkday_hash[:week_number] = wkday[:attributes][:ordwk] if wkday[:attributes][:ordwk]
|
31
|
+
wkday_hash
|
32
|
+
end
|
33
|
+
elsif zimbra_attributes[:byday] && zimbra_attributes[:byday][:wkday]
|
34
|
+
day_hash = { day: zimbra_attributes[:byday][:wkday][:attributes][:day] }
|
35
|
+
day_hash[:week_number] = zimbra_attributes[:byday][:wkday][:attributes][:ordwk] if zimbra_attributes[:byday][:wkday][:attributes][:ordwk]
|
36
|
+
attrs[:by_day] = [day_hash]
|
37
|
+
end
|
38
|
+
|
39
|
+
if zimbra_attributes[:bymonth]
|
40
|
+
attrs[:by_month] = zimbra_attributes[:bymonth][:attributes][:molist]
|
41
|
+
attrs[:by_month] = [attrs[:by_month]] unless attrs[:by_month].is_a?(Array)
|
42
|
+
end
|
43
|
+
|
44
|
+
if zimbra_attributes[:bymonthday]
|
45
|
+
attrs[:by_month_day] = zimbra_attributes[:bymonthday][:attributes][:modaylist]
|
46
|
+
attrs[:by_month_day] = [attrs[:by_month_day]] unless attrs[:by_month_day].is_a?(Array)
|
47
|
+
end
|
48
|
+
|
49
|
+
attrs
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
ATTRS = [
|
54
|
+
:frequency,
|
55
|
+
:interval,
|
56
|
+
:by_day,
|
57
|
+
:by_month,
|
58
|
+
:by_month_day,
|
59
|
+
:count,
|
60
|
+
:until_date,
|
61
|
+
:by_set_position
|
62
|
+
] unless const_defined?(:ATTRS)
|
63
|
+
|
64
|
+
attr_accessor *ATTRS
|
65
|
+
|
66
|
+
def initialize(args = {})
|
67
|
+
self.attributes = args
|
68
|
+
end
|
69
|
+
|
70
|
+
# take attributes by the xml name or our more descriptive name
|
71
|
+
def attributes=(args = {})
|
72
|
+
ATTRS.each do |attr_name|
|
73
|
+
if args.has_key?(attr_name)
|
74
|
+
self.send(:"#{attr_name}=", args[attr_name])
|
75
|
+
elsif args.has_key?(attr_name.to_s)
|
76
|
+
self.send(:"#{attr_name}=", args[attr_name.to_s])
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def frequency=(val)
|
82
|
+
frequency = Zimbra::DateHelpers::Frequency.find(val).name
|
83
|
+
@frequency = frequency || val
|
84
|
+
end
|
85
|
+
|
86
|
+
def frequency_to_zimbra
|
87
|
+
Zimbra::DateHelpers::Frequency.find(frequency).zimbra_name rescue frequency
|
88
|
+
end
|
89
|
+
|
90
|
+
def until_date=(val)
|
91
|
+
@until_date = Time.parse(val) rescue val
|
92
|
+
end
|
93
|
+
|
94
|
+
def by_set_position=(val)
|
95
|
+
if val == [0]
|
96
|
+
@by_set_position = nil
|
97
|
+
else
|
98
|
+
@by_set_position = val
|
99
|
+
end
|
100
|
+
end
|
101
|
+
def by_day=(val)
|
102
|
+
@by_day = if val.is_a?(Array)
|
103
|
+
val.collect do |day_specification|
|
104
|
+
day_specification[:day] = Zimbra::DateHelpers::WeekDay.find(day_specification[:day]) rescue day_specification[:day]
|
105
|
+
day_specification
|
106
|
+
end
|
107
|
+
else
|
108
|
+
val
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def to_hash(options = {})
|
113
|
+
hash = {
|
114
|
+
:frequency => frequency ? frequency.to_sym : nil,
|
115
|
+
:interval => interval,
|
116
|
+
:by_month => by_month,
|
117
|
+
:by_month_day => by_month_day,
|
118
|
+
:count => count,
|
119
|
+
:until_date => until_date,
|
120
|
+
:by_set_position => by_set_position
|
121
|
+
}
|
122
|
+
hash[:by_day] = by_day.collect do |day_specification|
|
123
|
+
day_specification[:day] = day_specification[:day].to_sym if day_specification[:day]
|
124
|
+
day_specification
|
125
|
+
end if by_day
|
126
|
+
hash.reject! { |key, value| value.nil? }
|
127
|
+
hash.reject! { |key, value| options[:except].include?(key.to_sym) || options[:except].include?(key.to_s) } if options[:except]
|
128
|
+
hash.reject! { |key, value| !options[:only].include?(key.to_sym) && !options[:only].include?(key.to_s) } if options[:only]
|
129
|
+
hash
|
130
|
+
end
|
131
|
+
|
132
|
+
def create_xml(document)
|
133
|
+
document.add "rule" do |rule_element|
|
134
|
+
rule_element.set_attr "freq", frequency_to_zimbra
|
135
|
+
|
136
|
+
if until_date
|
137
|
+
rule_element.add "until" do |until_element|
|
138
|
+
until_element.set_attr "d", until_date.utc.strftime("%Y%m%dT%H%M%SZ")
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
rule_element.add "interval" do |interval_element|
|
143
|
+
interval_element.set_attr "ival", interval
|
144
|
+
end
|
145
|
+
|
146
|
+
if count && count > 0
|
147
|
+
rule_element.add "count" do |count_element|
|
148
|
+
count_element.set_attr "num", count
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
if by_day && by_day.count > 0
|
153
|
+
rule_element.add "byday" do |by_day_element|
|
154
|
+
by_day.each do |day_spec|
|
155
|
+
by_day_element.add "wkday" do |wkday_element|
|
156
|
+
wkday_element.set_attr "day", day_spec[:day].zimbra_name
|
157
|
+
wkday_element.set_attr "ordwk", day_spec[:week_number] if day_spec.has_key?(:week_number)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
if by_month && by_month.count > 0
|
164
|
+
rule_element.add "bymonth" do |by_month_element|
|
165
|
+
by_month_element.set_attr "molist", by_month.join(',')
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
if by_month_day && by_month_day.count > 0
|
170
|
+
rule_element.add "bymonthday" do |by_month_day_element|
|
171
|
+
by_month_day_element.set_attr "modaylist", by_month_day.join(',')
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
if by_set_position
|
176
|
+
rule_element.add "bysetpos" do |bysetpos_element|
|
177
|
+
bysetpos_element.set_attr "poslist", by_set_position.join(',')
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module Zimbra
|
2
|
+
class Appointment
|
3
|
+
class Reply
|
4
|
+
ATTRS = [
|
5
|
+
:sequence_number, :date, :email_address, :participation_status, :sent_by, :recurrence_range_type, :recurrence_id, :timezone, :recurrence_id_utc
|
6
|
+
] unless const_defined?(:ATTRS)
|
7
|
+
|
8
|
+
attr_accessor *ATTRS
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def new_from_zimbra_attributes(zimbra_attributes)
|
12
|
+
new(parse_zimbra_attributes(zimbra_attributes))
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse_zimbra_attributes(zimbra_attributes)
|
16
|
+
zimbra_attributes = Zimbra::Hash.symbolize_keys(zimbra_attributes.dup, true)
|
17
|
+
|
18
|
+
{
|
19
|
+
:sequence_number => zimbra_attributes[:seq],
|
20
|
+
:date => zimbra_attributes[:d],
|
21
|
+
:email_address => zimbra_attributes[:at],
|
22
|
+
:participation_status => zimbra_attributes[:ptst],
|
23
|
+
:sent_by => zimbra_attributes[:sentBy],
|
24
|
+
:recurrence_range_type => zimbra_attributes[:rangeType],
|
25
|
+
:recurrence_id => zimbra_attributes[:recurId],
|
26
|
+
:timezone => zimbra_attributes[:tz],
|
27
|
+
:recurrence_id_utc => zimbra_attributes[:ridZ]
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize(args = {})
|
33
|
+
self.attributes = args
|
34
|
+
end
|
35
|
+
|
36
|
+
# take attributes by the xml name or our more descriptive name
|
37
|
+
def attributes=(args = {})
|
38
|
+
ATTRS.each do |attr_name|
|
39
|
+
if args.has_key?(attr_name)
|
40
|
+
self.send(:"#{attr_name}=", args[attr_name])
|
41
|
+
elsif args.has_key?(attr_name.to_s)
|
42
|
+
self.send(:"#{attr_name}=", args[attr_name.to_s])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def participation_status=(val)
|
48
|
+
@participation_status = parse_participation_status(val)
|
49
|
+
end
|
50
|
+
|
51
|
+
def date=(val)
|
52
|
+
if val.is_a?(Integer)
|
53
|
+
@date = parse_date_in_seconds(val)
|
54
|
+
else
|
55
|
+
@date = val
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_hash(options = {})
|
60
|
+
hash = ATTRS.inject({}) do |hash, attr_name|
|
61
|
+
hash[attr_name] = self.send(attr_name)
|
62
|
+
hash
|
63
|
+
end
|
64
|
+
hash.reject! { |key, value| options[:except].include?(key.to_sym) || options[:except].include?(key.to_s) } if options[:except]
|
65
|
+
hash.reject! { |key, value| !options[:only].include?(key.to_sym) && !options[:only].include?(key.to_s) } if options[:only]
|
66
|
+
hash
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def parse_participation_status(status)
|
72
|
+
possible_values = {
|
73
|
+
'NE' => :needs_action,
|
74
|
+
'AC' => :accept,
|
75
|
+
'TE' => :tentative,
|
76
|
+
'DE' => :declined,
|
77
|
+
'DG' => :delegated,
|
78
|
+
'CO' => :completed,
|
79
|
+
'IN' => :in_process,
|
80
|
+
'WE' => :waiting,
|
81
|
+
'DF' => :deferred
|
82
|
+
}
|
83
|
+
possible_values[status] || status
|
84
|
+
end
|
85
|
+
|
86
|
+
def parse_date_in_seconds(seconds)
|
87
|
+
Time.at(seconds / 1000)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Zimbra
|
2
|
+
class Calendar < Folder
|
3
|
+
class << self
|
4
|
+
def all
|
5
|
+
CalendarService.find_all_by_view('appointment').reject { |c| c.view.nil? || c.view != 'appointment' }
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def appointments
|
10
|
+
Zimbra::Appointment.find_all_by_calendar_id(id)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class CalendarService < FolderService
|
15
|
+
def parse_xml_responses(xml)
|
16
|
+
Parser.get_all_response(xml)
|
17
|
+
end
|
18
|
+
|
19
|
+
class Parser < Zimbra::FolderService::Parser
|
20
|
+
class << self
|
21
|
+
def initialize_from_attributes(folder_attributes)
|
22
|
+
Zimbra::Calendar.new(folder_attributes)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# http://files.zimbra.com/docs/soap_api/8.0.4/soap-docs-804/api-reference/zimbraAdmin/DelegateAuth.html
|
2
|
+
|
3
|
+
module Zimbra
|
4
|
+
class DelegateAuthToken
|
5
|
+
class << self
|
6
|
+
def for_account_name(account_name)
|
7
|
+
DelegateAuthTokenService.get_by_account_name(account_name)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_accessor :account_name, :token, :lifetime
|
12
|
+
|
13
|
+
def initialize(args = {})
|
14
|
+
self.account_name = args[:account_name]
|
15
|
+
self.token = args[:token]
|
16
|
+
self.lifetime = args[:lifetime]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class DelegateAuthTokenService < HandsoapService
|
21
|
+
def get_by_account_name(account_name)
|
22
|
+
xml = invoke("n2:DelegateAuthRequest") do |message|
|
23
|
+
Builder.get_by_account_name(message, account_name)
|
24
|
+
end
|
25
|
+
return nil unless xml
|
26
|
+
Parser.delegate_auth_token_response(account_name, xml)
|
27
|
+
end
|
28
|
+
|
29
|
+
class Builder
|
30
|
+
class << self
|
31
|
+
def get_by_account_name(message, account_name)
|
32
|
+
message.add 'account', account_name do |c|
|
33
|
+
c.set_attr 'by', 'name'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
class Parser
|
39
|
+
class << self
|
40
|
+
def delegate_auth_token_response(account_name, response)
|
41
|
+
auth_token = (response/'//n2:authToken').to_s
|
42
|
+
lifetime = (response/'//n2:lifetime').to_i
|
43
|
+
|
44
|
+
Zimbra::DelegateAuthToken.new(account_name: account_name, token: auth_token, lifetime: lifetime)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Zimbra
|
2
|
+
class Hash < ::Hash
|
3
|
+
# Thanks to https://gist.github.com/dimus/335286 for this code
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def symbolize_keys(hash, recursive = false)
|
7
|
+
hash.keys.each do |key|
|
8
|
+
value = hash.delete(key)
|
9
|
+
key = key.respond_to?(:to_sym) ? key.to_sym : key
|
10
|
+
hash[key] = (recursive && value.is_a?(::Hash)) ? symbolize_keys(value.dup, recursive) : value
|
11
|
+
end
|
12
|
+
hash
|
13
|
+
end
|
14
|
+
|
15
|
+
def from_xml(xml_io)
|
16
|
+
begin
|
17
|
+
result = Nokogiri::XML(xml_io)
|
18
|
+
return { result.root.name.to_sym => xml_node_to_hash(result.root)}
|
19
|
+
rescue Exception => e
|
20
|
+
# raise your custom exception here
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def xml_node_to_hash(node)
|
25
|
+
# If we are at the root of the document, start the hash
|
26
|
+
if node.element?
|
27
|
+
result_hash = {}
|
28
|
+
if node.attributes != {}
|
29
|
+
result_hash[:attributes] = {}
|
30
|
+
node.attributes.keys.each do |key|
|
31
|
+
result_hash[:attributes][node.attributes[key].name.to_sym] = prepare(node.attributes[key].value)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
if node.children.size > 0
|
35
|
+
node.children.each do |child|
|
36
|
+
result = xml_node_to_hash(child)
|
37
|
+
|
38
|
+
if child.name == "text"
|
39
|
+
unless child.next_sibling || child.previous_sibling
|
40
|
+
if result_hash[:attributes]
|
41
|
+
result_hash['value'] = prepare(result)
|
42
|
+
return result_hash
|
43
|
+
else
|
44
|
+
return prepare(result)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
elsif result_hash[child.name.to_sym]
|
48
|
+
if result_hash[child.name.to_sym].is_a?(Object::Array)
|
49
|
+
result_hash[child.name.to_sym] << prepare(result)
|
50
|
+
else
|
51
|
+
result_hash[child.name.to_sym] = [result_hash[child.name.to_sym]] << prepare(result)
|
52
|
+
end
|
53
|
+
else
|
54
|
+
result_hash[child.name.to_sym] = prepare(result)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
return result_hash
|
59
|
+
else
|
60
|
+
return result_hash
|
61
|
+
end
|
62
|
+
else
|
63
|
+
return prepare(node.content.to_s)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def prepare(data)
|
68
|
+
(data.class == String && data.to_i.to_s == data) ? data.to_i : data
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|