ruby-jss 1.4.1 → 1.6.0

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.
Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +95 -0
  3. data/THANKS.md +3 -2
  4. data/lib/jamf.rb +18 -17
  5. data/lib/jamf/api/base_classes/collection_resource.rb +613 -0
  6. data/lib/jamf/api/{abstract_classes → base_classes}/json_object.rb +109 -101
  7. data/lib/jamf/api/{abstract_classes → base_classes}/prestage.rb +55 -30
  8. data/lib/jamf/api/{abstract_classes → base_classes}/resource.rb +10 -6
  9. data/lib/jamf/api/{abstract_classes → base_classes}/singleton_resource.rb +4 -3
  10. data/lib/jamf/api/connection.rb +13 -9
  11. data/lib/jamf/api/connection/api_error.rb +8 -8
  12. data/lib/jamf/api/connection/token.rb +16 -15
  13. data/lib/jamf/api/json_objects/device_enrollment_device.rb +14 -7
  14. data/lib/jamf/api/json_objects/{location.rb → device_enrollment_device_sync_state.rb} +27 -41
  15. data/lib/jamf/api/json_objects/device_enrollment_sync_status.rb +1 -1
  16. data/lib/jamf/api/json_objects/{attachment.rb → locale.rb} +14 -23
  17. data/lib/jamf/api/json_objects/md_prestage_name.rb +1 -1
  18. data/lib/jamf/api/json_objects/md_prestage_names.rb +2 -2
  19. data/lib/jamf/api/json_objects/md_prestage_skip_setup_items.rb +50 -1
  20. data/lib/jamf/api/json_objects/prestage_assignment.rb +2 -2
  21. data/lib/jamf/api/json_objects/prestage_location.rb +3 -3
  22. data/lib/jamf/api/json_objects/prestage_purchasing_data.rb +7 -7
  23. data/lib/jamf/api/json_objects/prestage_scope.rb +1 -1
  24. data/lib/jamf/api/{resources/collection_resources → json_objects}/time_zone.rb +9 -23
  25. data/lib/jamf/api/mixins/{abstract.rb → base_class.rb} +34 -16
  26. data/lib/jamf/api/mixins/bulk_deletable.rb +27 -6
  27. data/lib/jamf/api/mixins/change_log.rb +201 -51
  28. data/lib/jamf/api/{resources/collection_resources/computer.rb → mixins/filterable.rb} +19 -17
  29. data/lib/jamf/api/mixins/pageable.rb +208 -0
  30. data/lib/jamf/api/{json_objects/installed_application.rb → mixins/sortable.rb} +33 -33
  31. data/lib/jamf/api/resources/collection_resources/building.rb +16 -9
  32. data/lib/jamf/api/resources/collection_resources/category.rb +5 -4
  33. data/lib/jamf/api/resources/collection_resources/computer_prestage.rb +12 -5
  34. data/lib/jamf/api/resources/collection_resources/department.rb +0 -2
  35. data/lib/jamf/api/resources/collection_resources/device_enrollment.rb +10 -10
  36. data/lib/jamf/api/resources/collection_resources/inventory_preload_record.rb +11 -3
  37. data/lib/jamf/api/resources/collection_resources/mobile_device_prestage.rb +25 -23
  38. data/lib/jamf/api/resources/collection_resources/script.rb +61 -25
  39. data/lib/jamf/api/resources/singleton_resources/app_store_country_codes.rb +15 -5
  40. data/lib/jamf/api/resources/singleton_resources/locales.rb +155 -0
  41. data/lib/jamf/api/resources/singleton_resources/time_zones.rb +213 -0
  42. data/lib/jamf/client.rb +3 -3
  43. data/lib/jamf/client/management_action.rb +2 -3
  44. data/lib/jamf/composer.rb +2 -2
  45. data/lib/jamf/utility.rb +35 -7
  46. data/lib/jamf/validate.rb +63 -24
  47. data/lib/jamf/version.rb +1 -1
  48. data/lib/jss.rb +2 -2
  49. data/lib/jss/api_connection.rb +114 -406
  50. data/lib/jss/api_object.rb +3 -19
  51. data/lib/jss/api_object/categorizable.rb +1 -1
  52. data/lib/jss/api_object/computer.rb +13 -0
  53. data/lib/jss/api_object/configuration_profile.rb +61 -5
  54. data/lib/jss/api_object/directory_binding_type.rb +66 -60
  55. data/lib/jss/api_object/directory_binding_type/active_directory.rb +71 -34
  56. data/lib/jss/api_object/directory_binding_type/admitmac.rb +536 -467
  57. data/lib/jss/api_object/directory_binding_type/centrify.rb +21 -7
  58. data/lib/jss/api_object/directory_binding_type/open_directory.rb +4 -4
  59. data/lib/jss/api_object/distribution_point.rb +2 -2
  60. data/lib/jss/api_object/dock_item.rb +102 -96
  61. data/lib/jss/api_object/extendable.rb +1 -1
  62. data/lib/jss/api_object/group.rb +33 -2
  63. data/lib/jss/api_object/network_segment.rb +45 -13
  64. data/lib/jss/api_object/patch_source.rb +10 -9
  65. data/lib/jss/api_object/policy.rb +155 -25
  66. data/lib/jss/api_object/printer.rb +10 -4
  67. data/lib/jss/api_object/scopable.rb +10 -15
  68. data/lib/jss/api_object/scopable/scope.rb +31 -30
  69. data/lib/jss/api_object/script.rb +242 -352
  70. data/lib/jss/api_object/user.rb +1 -1
  71. data/lib/jss/client/management_action.rb +1 -2
  72. data/lib/jss/composer.rb +2 -2
  73. data/lib/jss/exceptions.rb +3 -0
  74. data/lib/jss/server.rb +15 -0
  75. data/lib/jss/utility.rb +213 -45
  76. data/lib/jss/version.rb +1 -1
  77. metadata +46 -64
  78. data/lib/jamf/api/abstract_classes/advanced_search.rb +0 -86
  79. data/lib/jamf/api/abstract_classes/collection_resource.rb +0 -433
  80. data/lib/jamf/api/abstract_classes/generic_reference.rb +0 -145
  81. data/lib/jamf/api/abstract_classes/prestage_skip_setup_items.rb +0 -126
  82. data/lib/jamf/api/json_objects/account_prefs.rb +0 -79
  83. data/lib/jamf/api/json_objects/android_details.rb +0 -139
  84. data/lib/jamf/api/json_objects/appletv_details.rb +0 -110
  85. data/lib/jamf/api/json_objects/cellular_network.rb +0 -151
  86. data/lib/jamf/api/json_objects/computer_prestage_skip_setup_items.rb +0 -67
  87. data/lib/jamf/api/json_objects/criterion.rb +0 -152
  88. data/lib/jamf/api/json_objects/extension_attribute_value.rb +0 -128
  89. data/lib/jamf/api/json_objects/installed_certificate.rb +0 -53
  90. data/lib/jamf/api/json_objects/installed_configuration_profile.rb +0 -67
  91. data/lib/jamf/api/json_objects/installed_ebook.rb +0 -58
  92. data/lib/jamf/api/json_objects/installed_provisioning_profile.rb +0 -59
  93. data/lib/jamf/api/json_objects/ios_details.rb +0 -244
  94. data/lib/jamf/api/json_objects/mobile_device_details.rb +0 -219
  95. data/lib/jamf/api/json_objects/mobile_device_security.rb +0 -101
  96. data/lib/jamf/api/json_objects/purchasing_data.rb +0 -125
  97. data/lib/jamf/api/mixins/locatable.rb +0 -124
  98. data/lib/jamf/api/mixins/referable.rb +0 -92
  99. data/lib/jamf/api/resources/collection_resources/account.rb +0 -163
  100. data/lib/jamf/api/resources/collection_resources/advanced_mobile_device_search.rb +0 -52
  101. data/lib/jamf/api/resources/collection_resources/advanced_user_search.rb +0 -52
  102. data/lib/jamf/api/resources/collection_resources/extension_attribute.rb +0 -45
  103. data/lib/jamf/api/resources/collection_resources/mobile_device.rb +0 -315
  104. data/lib/jamf/api/resources/collection_resources/site.rb +0 -77
  105. data/lib/jamf/api/resources/singleton_resources/authorization.rb +0 -88
  106. data/lib/jamf/api/resources/singleton_resources/client_checkin_settings.rb +0 -139
  107. data/lib/jamf/api/resources/singleton_resources/reenrollment_settings.rb +0 -95
@@ -168,7 +168,7 @@ module JSS
168
168
  @phone_number = @init_data[:phone_number]
169
169
  @position = @init_data[:position]
170
170
  @ldap_server = JSS::APIObject.get_name @init_data[:ldap_server]
171
- @ldap_server_id = @init_data[:ldap_server][:id]
171
+ @ldap_server_id = @init_data[:ldap_server][:id] unless @init_data[:ldap_server].nil?
172
172
  @sites = @init_data[:sites] ? @init_data[:sites] : []
173
173
 
174
174
  if @init_data[:links]
@@ -103,8 +103,7 @@ module JSS
103
103
  orig_flags = flags
104
104
  prefs['apps'] << { 'bundle-id' => MGMT_ACTION_BUNDLE_ID, 'flags' => flags }
105
105
  end
106
- # system "/usr/bin/defaults write #{NCPREFS_DOMAIN} '#{prefs.to_plist}'"
107
- plist.open('w') { |f| f.write prefs.to_plist }
106
+ plist.open('w') { |f| f.write JSS.xml_plist_from(prefs) }
108
107
  system HUP_NOTIF_CTR_CMD if hup
109
108
  orig_flags
110
109
  end
data/lib/jss/composer.rb CHANGED
@@ -126,7 +126,7 @@ module JSS
126
126
  ###
127
127
  comp_plist_out = Pathname.new "/tmp/#{PKG_BUNDLE_ID_PFX}-#{pkg_filename}.plist"
128
128
  system "#{PKGBUILD} --analyze --root '#{root}' '#{comp_plist_out}'"
129
- comp_plist = Plist.parse_xml comp_plist_out.read
129
+ comp_plist = JSS.parse_plist comp_plist_out
130
130
 
131
131
  ### if the plist is empty, there are no bundles in the pkg
132
132
  if comp_plist[0].nil?
@@ -141,7 +141,7 @@ module JSS
141
141
  bndl['BundleHasStrictIdentifier'] = false
142
142
  end
143
143
  ### write out the edits
144
- comp_plist_out.open('w') { |f| f.write comp_plist.to_plist }
144
+ comp_plist_out.open('w') { |f| f.write JSS.xml_plist_from(comp_plist) }
145
145
  comp_plist_arg = "--component-plist '#{comp_plist_out}'"
146
146
  end
147
147
 
@@ -87,6 +87,9 @@ module JSS
87
87
  ###
88
88
  class AuthenticationError < RuntimeError; end
89
89
 
90
+ ###
91
+ class AuthorizationError < RuntimeError ; end
92
+
90
93
  ### ConflictError - raise this when
91
94
  ### attempts to PUT or PUSH to the API
92
95
  ### result in a 409 Conflict http error.
data/lib/jss/server.rb CHANGED
@@ -110,6 +110,21 @@ module JSS
110
110
  @act_code_data[:code]
111
111
  end
112
112
 
113
+ # Update the activation code and organization name for this server
114
+ #
115
+ # @param org: [String] the organization to which the server is licensed
116
+ # @param code: [String ] the activation code for the server licence
117
+ #
118
+ # @return [void]
119
+ def update_activation_code(org:, code:)
120
+ xml = REXML::Document.new JSS::APIConnection::XML_HEADER
121
+ acode = xml.add_element ACTIVATION_CODE_KEY.to_s
122
+ acode.add_element('organization_name').text = org
123
+ acode.add_element('code').text = code
124
+
125
+ @api.put_rsrc ACTIVATION_CODE_RSRC, xml.to_s
126
+ end
127
+
113
128
  # Remove the api object from
114
129
  # the instance_variables used to create
115
130
  # pretty-print (pp) output.
data/lib/jss/utility.rb CHANGED
@@ -23,61 +23,195 @@
23
23
  #
24
24
  #
25
25
 
26
- #
27
26
  module JSS
28
27
 
29
28
  # A collection of useful utility methods. Mostly for
30
29
  # converting values between formats, parsing data, and
31
30
  # user interaction.
32
31
 
33
- # Converts an OS Version into an Array of higher OS versions.
32
+ # Hash of 'minor' => 'maint'
33
+ # The maximum maint release for macOS 10.minor.maint
34
+ # e.g the highest release of 10.6 was 10.6.8, the highest release of
35
+ # 10.15 was 10.15.7
36
+ #
37
+ # 12 is the default for the current OS and higher
38
+ # (and hoping apple doesn't release 10.16.13)
39
+ OS_TEN_MAXS = {
40
+ 2 => 8,
41
+ 3 => 9,
42
+ 4 => 11,
43
+ 5 => 8,
44
+ 6 => 8,
45
+ 7 => 5,
46
+ 8 => 5,
47
+ 9 => 5,
48
+ 10 => 5,
49
+ 11 => 6,
50
+ 12 => 6,
51
+ 13 => 6,
52
+ 14 => 6,
53
+ 15 => 7
54
+ }
55
+
56
+ # Hash of 'major' => 'minor'
57
+ # The maximum minor release for macOS major.minor
58
+ # e.g. the highest release of 11 is 11.12
59
+ #
60
+ # 12 is the default for the current OS and higher
61
+ # (and hoping apple doesn't release, e.g., 11.13)
62
+ MAC_OS_MAXS = {
63
+ 11 => 12,
64
+ 12 => 12,
65
+ 13 => 12,
66
+ 14 => 12,
67
+ 15 => 12,
68
+ 16 => 12,
69
+ 17 => 12,
70
+ 18 => 12,
71
+ 19 => 12,
72
+ 20 => 12
73
+ }
74
+
75
+ # Converts an OS Version into an Array of equal or higher OS versions, up to
76
+ # some non-existant max, hopefully far in the future, currently 20.12.10
77
+ #
78
+ # This array can then be joined with commas and used as the value of the
79
+ # os_requirements for Packages and Scripts.
34
80
  #
35
- # It's unlikely that this library will still be in use as-is by the release of OS X 10.30.
36
- # Hopefully well before then JAMF will implement a "minimum OS" in the JSS itself.
81
+ # It's unlikely that this method, as written, will still be in use by
82
+ # the release of macOS 20.12.10, but currently thats the upper limit.
37
83
  #
38
- # @param min_os [String] the mimimum OS version to expand, e.g. ">=10.6.7" or "10.6.7"
84
+ # Hopefully well before then JAMF will implement a "minimum OS" in Jamf Pro
85
+ # itself, then we could avoid the inherant limitations in using a method like
86
+ # this.
39
87
  #
40
- # @return [Array] Nearly all potential OS versions from the minimum to 10.19.x.
88
+ # When the highest maint. release of an OS version is not known, because its
89
+ # the currently released OS version or higher, then this method assumes '12'
90
+ # e.g. '10.16.12', '11.12', '12.12', etc.
91
+ #
92
+ # Apple has never released more than 11 updates to a version of macOS
93
+ # (that being 10.4), so hopefully 12 is enough
94
+ #
95
+ # Since Big Sur might report itself as either '10.16' or '11.x.x', this method
96
+ # will allow for both possibilities, and the array will contain whatever
97
+ # iterations needed for both version numbers
98
+ #
99
+ # @param min_os [String] the mimimum OS version to expand, e.g. ">=10.9.4" or "11.1"
100
+ #
101
+ # @return [Array] Nearly all potential OS versions from the minimum to 20.12.10
41
102
  #
42
103
  # @example
43
- # JSS.expand_min_os ">=10.6.7" # => returns this array
44
- # # ["10.6.7",
45
- # # "10.6.8",
104
+ # JSS.expand_min_os ">=10.9.4" # => returns this array
105
+ # # ["10.9.4",
106
+ # # "10.9.5",
107
+ # # "10.10.x"
46
108
  # # ...
47
- # # "10.6.25",
48
- # # "10.7.x",
49
- # # "10.8.x",
109
+ # # "10.16.x",
110
+ # # "11.x",
111
+ # # "12.x",
50
112
  # # ...
51
- # # "10.30.x"]
113
+ # # "20.x"]
52
114
  #
53
115
  #
54
116
  def self.expand_min_os(min_os)
55
117
  min_os = min_os.delete '>='
56
118
 
57
119
  # split the version into major, minor and maintenance release numbers
58
- (maj, min, maint) = min_os.split('.')
120
+ major, minor, maint = min_os.split('.')
121
+ minor = 'x' if minor.nil? || minor == '0'
59
122
  maint = 'x' if maint.nil? || maint == '0'
60
123
 
61
- # if the maint release number is an "x" just start the list of OK OS's with it
62
- if maint == 'x'
63
- ok_oses = [maj + '.' + min.to_s + '.x']
124
+ ok_oses = []
64
125
 
65
- # otherwise, start with it and explicitly add all maint releases up to 15
66
- # (and hope apple doesn't do more than 15 maint releases for an OS)
67
- else
68
- ok_oses = []
69
- (maint.to_i..25).each do |m|
70
- ok_oses << maj + '.' + min + '.' + m.to_s
126
+ # Deal with 10.x.x up to 10.16
127
+ if major == '10'
128
+
129
+ # In big sur with SYSTEM_VERSION_COMPAT
130
+ # set, it will only ever report as `10.16`
131
+ # So if major is 10 and minor is 16, ignore maint
132
+ # and start explicitly at '10.16'
133
+ if minor == '16'
134
+ ok_oses << '10.16'
135
+
136
+ # But for Catalina and below, we need to
137
+ # expand things out
138
+ else
139
+ # e.g. 10.14.x
140
+ # doesn't expand to anything
141
+ if maint == 'x'
142
+ ok_oses << "10.#{minor}.x"
143
+
144
+ # e.g. 10.15.5
145
+ # expand to 10.15.5, 10.15.6, 10.15.7
146
+ else
147
+ max_maint_for_minor = OS_TEN_MAXS[minor.to_i]
148
+
149
+ (maint.to_i..max_maint_for_minor).each do |m|
150
+ ok_oses << "#{major}.#{minor}.#{m}"
151
+ end # each m
152
+ end # if maint == x
153
+
154
+ # now if we started below catalina, account for everything
155
+ # up to 10.15.x
156
+ if minor.to_i < 15
157
+ ((minor.to_i + 1)..15).each { |v| ok_oses << "10.#{v}.x" }
158
+ end
159
+
160
+ # and add big sur with SYSTEM_VERSION_COMPAT
161
+ ok_oses << '10.16'
162
+ end # if minor == 16
163
+
164
+ # now reset these so we can go higher
165
+ major = '11'
166
+ minor = 'x'
167
+ maint = 'x'
168
+ end # if major == 10
169
+
170
+ # if the min os is 11.0.0 or equiv, and we aven't added 10.16
171
+ # for SYSTEM_VERSION_COMPAT, add it now
172
+ if ['11', '11.x', '11.x.x', '11.0', '11.0.0'].include?(min_os) && !ok_oses.include?('10.16')
173
+ ok_oses << '10.16'
174
+ end
175
+
176
+ # e.g. 11.x, or 11.x.x
177
+ # expand to 11.x, 12.x, 13.x, ... 20.x
178
+ if minor == 'x'
179
+ ((major.to_i)..20).each { |v| ok_oses << "#{v}.x" }
180
+
181
+ # e.g. 11.2.x
182
+ # expand to 11.2.x, 11.3.x, ... 11.12.x,
183
+ # 12.x, 13.x, ... 20.x
184
+ elsif maint == 'x'
185
+ # first expand the minors out to their max
186
+ # e.g. 11.2.x, 11.3.x, ... 11.12.x
187
+ max_minor_for_major = MAC_OS_MAXS[major.to_i]
188
+ ((minor.to_i)..max_minor_for_major).each do |m|
189
+ ok_oses << "#{major}.#{m}.x"
71
190
  end # each m
191
+
192
+ # then add the majors out to 20
193
+ ((major.to_i + 1)..20).each { |v| ok_oses << "#{v}.x" }
194
+
195
+ # e.g. 11.2.3
196
+ # expand to 11.2.3, 11.2.4, ... 11.2.10,
197
+ # 11.3.x, 11.4.x, ... 11.12.x,
198
+ # 12.x, 13.x, ... 20.x
199
+ else
200
+ # first expand the maints out to 10
201
+ # e.g. 11.2.3, 11.2.4, ... 11.2.10
202
+ ((maint.to_i)..10).each { |mnt| ok_oses << "#{major}.#{minor}.#{mnt}" }
203
+
204
+ # then expand the minors out to their max
205
+ # e.g. 11.3.x, ... 11.12.x
206
+ max_minor_for_major = MAC_OS_MAXS[major.to_i]
207
+ ((minor.to_i + 1)..max_minor_for_major).each { |min| ok_oses << "#{major}.#{min}.x" }
208
+
209
+ # then add the majors out to 20
210
+ ((major.to_i + 1)..20).each { |v| ok_oses << "#{v}.x" }
72
211
  end
73
212
 
74
- # now account for all OS X versions starting with 10.
75
- # up to 10.30.x
76
- ((min.to_i + 1)..30).each do |v|
77
- ok_oses << maj + '.' + v.to_s + '.x'
78
- end # each v
79
213
  ok_oses
80
- end
214
+ end # def self.expand_min_os(min_os)
81
215
 
82
216
  # Scripts and packages can have processor limitations.
83
217
  # This method tests a given processor, against a requirement
@@ -95,6 +229,7 @@ module JSS
95
229
  #
96
230
  def self.processor_ok?(requirement, processor = nil)
97
231
  return true if requirement.to_s.empty? || requirement =~ /none/i
232
+
98
233
  processor ||= `/usr/bin/uname -p`
99
234
  requirement == (processor.to_s.include?('86') ? 'x86' : 'ppc')
100
235
  end
@@ -115,6 +250,7 @@ module JSS
115
250
  def self.os_ok?(requirement, os_to_check = nil)
116
251
  return true if requirement.to_s =~ /none/i
117
252
  return true if requirement.to_s == 'n'
253
+
118
254
  requirement = JSS.to_s_and_a(requirement)[:arrayform]
119
255
  return true if requirement.empty?
120
256
 
@@ -174,19 +310,26 @@ module JSS
174
310
  { stringform: valstr, arrayform: valarr }
175
311
  end # to_s_and_a
176
312
 
177
- # Parse a plist into a Ruby data structure.
178
- # This enhances Plist::parse_xml taking file paths, as well as XML Strings
179
- # and reading the files regardless of binary/XML format.
313
+ # Parse a plist into a Ruby data structure. The plist parameter may be
314
+ # a String containing an XML plist, or a path to a plist file, or it may be
315
+ # a Pathname object pointing to a plist file. The plist files may be XML or
316
+ # binary.
180
317
  #
181
318
  # @param plist[Pathname, String] the plist XML, or the path to a plist file
182
319
  #
320
+ # @param symbol_keys[Boolean] should any Hash keys in the result be converted
321
+ # into Symbols rather than remain as Strings?
322
+ #
183
323
  # @return [Object] the parsed plist as a ruby hash,array, etc.
184
324
  #
185
- def self.parse_plist(plist)
325
+ def self.parse_plist(plist, symbol_keys: false)
326
+ require 'cfpropertylist'
327
+
186
328
  # did we get a string of xml, or a string pathname?
187
329
  case plist
188
330
  when String
189
- return Plist.parse_xml plist if plist.include? '</plist>'
331
+ return CFPropertyList.native_types(CFPropertyList::List.new(data: plist).value, symbol_keys) if plist.include? '</plist>'
332
+
190
333
  plist = Pathname.new plist
191
334
  when Pathname
192
335
  true
@@ -197,9 +340,30 @@ module JSS
197
340
  # if we're here, its a Pathname
198
341
  raise JSS::MissingDataError, "No such file: #{plist}" unless plist.file?
199
342
 
200
- Plist.parse_xml `/usr/libexec/PlistBuddy -x -c print #{Shellwords.escape(plist.to_s)}`.force_encoding('UTF-8')
343
+ CFPropertyList.native_types(CFPropertyList::List.new(file: plist).value, symbol_keys)
201
344
  end # parse_plist
202
345
 
346
+ # Convert any ruby data to an XML plist.
347
+ #
348
+ # NOTE: Binary data is tricky. Easiest way is to pass in a
349
+ # Pathname or IO object (anything that responds to `read` and
350
+ # returns a bytestring)
351
+ # and then the CFPropertyList.guess method will read it and
352
+ # convert it to a Plist <data> element with base64 encoded
353
+ # data.
354
+ # For more info, see CFPropertyList.guess
355
+ #
356
+ # @param data [Object] the data to be converted, usually a Hash
357
+ #
358
+ # @return [String] the object converted into an XML plist
359
+ #
360
+ def self.xml_plist_from(data)
361
+ require 'cfpropertylist'
362
+ plist = CFPropertyList::List.new
363
+ plist.value = CFPropertyList.guess(data, convert_unknown_to_string: true)
364
+ plist.to_str(CFPropertyList::List::FORMAT_XML)
365
+ end
366
+
203
367
  # Converts anything that responds to #to_s to a Time, or nil
204
368
  #
205
369
  # Return nil if the item is nil, 0 or an empty String.
@@ -223,9 +387,7 @@ module JSS
223
387
 
224
388
  # if the UTC offset of the datetime is zero, make a new one with the correct local offset
225
389
  # (which might also be zero if we happen to be in GMT)
226
- if the_dt.offset.zero?
227
- the_dt = DateTime.new(the_dt.year, the_dt.month, the_dt.day, the_dt.hour, the_dt.min, the_dt.sec, JSS::TIME_ZONE_OFFSET)
228
- end
390
+ the_dt = DateTime.new(the_dt.year, the_dt.month, the_dt.day, the_dt.hour, the_dt.min, the_dt.sec, JSS::TIME_ZONE_OFFSET) if the_dt.offset.zero?
229
391
  # now convert it to a Time and return it
230
392
  Time.at the_dt.strftime('%s').to_i, usec
231
393
  end # parse_time
@@ -247,6 +409,7 @@ module JSS
247
409
  #
248
410
  def self.epoch_to_time(epoch)
249
411
  return nil if NIL_DATES.include? epoch
412
+
250
413
  Time.at(epoch.to_i / 1000.0)
251
414
  end # parse_date
252
415
 
@@ -266,6 +429,7 @@ module JSS
266
429
  def self.api_object_class(name)
267
430
  klass = api_object_names[name.downcase.to_sym]
268
431
  raise JSS::InvalidDataError, "Unknown API Object Class: #{name}" unless klass
432
+
269
433
  klass
270
434
  end
271
435
 
@@ -283,11 +447,13 @@ module JSS
283
447
  #
284
448
  def self.api_object_names
285
449
  return @api_object_names if @api_object_names
450
+
286
451
  @api_object_names ||= {}
287
452
  JSS.constants.each do |const|
288
453
  klass = JSS.const_get const
289
454
  next unless klass.is_a? Class
290
455
  next unless klass.ancestors.include? JSS::APIObject
456
+
291
457
  @api_object_names[klass.const_get(:RSRC_LIST_KEY).to_sym] = klass if klass.constants.include? :RSRC_LIST_KEY
292
458
  @api_object_names[klass.const_get(:RSRC_OBJECT_KEY).to_sym] = klass if klass.constants.include? :RSRC_OBJECT_KEY
293
459
  end
@@ -324,6 +490,7 @@ module JSS
324
490
  #
325
491
  def self.array_to_rexml_array(element, list)
326
492
  raise JSS::InvalidDataError, 'Arg. must be an Array.' unless list.is_a? Array
493
+
327
494
  element = element.to_s
328
495
  list.map do |v|
329
496
  e = REXML::Element.new(element)
@@ -350,6 +517,7 @@ module JSS
350
517
  #
351
518
  def self.hash_to_rexml_array(hash)
352
519
  raise InvalidDataError, 'Arg. must be a Hash.' unless hash.is_a? Hash
520
+
353
521
  ary = []
354
522
  hash.each_pair do |k, v|
355
523
  el = REXML::Element.new k.to_s
@@ -428,11 +596,11 @@ module JSS
428
596
 
429
597
  {
430
598
  major: major.to_i,
431
- minor: minor.to_i,
432
- revision: revision.to_i,
433
- maint: revision.to_i,
434
- patch: revision.to_i,
435
- build: build,
599
+ minor: minor.to_i,
600
+ revision: revision.to_i,
601
+ maint: revision.to_i,
602
+ patch: revision.to_i,
603
+ build: build,
436
604
  version: Gem::Version.new("#{major}.#{minor}.#{revision}")
437
605
  }
438
606
  end
@@ -457,6 +625,7 @@ module JSS
457
625
  @stdin_lines ||= ($stdin.tty? ? [] : $stdin.read.lines.map { |l| l.chomp("\n") })
458
626
 
459
627
  return @stdin_lines.join("\n") if line <= 0
628
+
460
629
  idx = line - 1
461
630
  @stdin_lines[idx]
462
631
  end
@@ -489,10 +658,9 @@ module JSS
489
658
  # @return [Boolean] The new state of devmode
490
659
  #
491
660
  def self.devmode(setting)
492
- @devmode = setting == :on ? true : false
661
+ @devmode = setting == :on
493
662
  end
494
663
 
495
-
496
664
  # is devmode currently on?
497
665
  #
498
666
  # @return [Boolean]