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
@@ -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
|