ruby-jss 0.8.2 → 0.9.0.b1

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.

Potentially problematic release.


This version of ruby-jss might be problematic. Click here for more details.

@@ -108,24 +108,18 @@ module JSS
108
108
  # *NOTE* Some API objects have data broken into subsections, in which case the
109
109
  # VALID_DATA_KEYS are expected in the section :general.
110
110
  #
111
+ #
112
+ # === Optional Constants
113
+ #
114
+ # ==== OTHER_LOOKUP_KEYS = [Hash{Symbol=>Hash}] Every object can be looked up by
115
+ # :id and :name, but some have other uniq identifiers that can also be used,
116
+ # e.g. :serial_number, :mac_address, and so on. This Hash, if defined,
117
+ # speficies those other keys for the subclass
118
+ # For more details about this hash, see {APIObject::DEFAULT_LOOKUP_KEYS},
119
+ # {APIObject.fetch}, and {APIObject#lookup_object_data}
120
+ #
111
121
  class APIObject
112
122
 
113
- # Mix-Ins
114
- #####################################
115
-
116
- # Class Variables
117
- #####################################
118
-
119
- # This Hash holds the most recent API query for a list of all items in any subclass,
120
- # keyed by the subclass's RSRC_LIST_KEY. See the self.all class method.
121
- #
122
- # When the .all method is called without an argument, and this hash has
123
- # a matching value, the value is returned, rather than requerying the
124
- # API. The first time a class calls .all, or whnever refresh is
125
- # not false, the API is queried and the value in this hash is updated.
126
- #
127
- @@all_items = {}
128
-
129
123
  # Class Methods
130
124
  #####################################
131
125
 
@@ -148,7 +142,7 @@ module JSS
148
142
  # class methods for accessing those other values as mapped Arrays,
149
143
  # e.g. JSS::Computer.all_udids
150
144
  #
151
- # The results of the first query for each subclass is stored in @@all_items
145
+ # The results of the first query for each subclass is stored in JSS.api.object_list_cache
152
146
  # and returned at every future call, so as to not requery the server every time.
153
147
  #
154
148
  # To force requerying to get updated data, provided a non-false argument.
@@ -161,9 +155,9 @@ module JSS
161
155
  #
162
156
  def self.all(refresh = false)
163
157
  raise JSS::UnsupportedError, '.all can only be called on subclasses of JSS::APIObject' if self == JSS::APIObject
164
- @@all_items[self::RSRC_LIST_KEY] = nil if refresh
165
- return @@all_items[self::RSRC_LIST_KEY] if @@all_items[self::RSRC_LIST_KEY]
166
- @@all_items[self::RSRC_LIST_KEY] = JSS::API.get_rsrc(self::RSRC_BASE)[self::RSRC_LIST_KEY]
158
+ JSS.api.object_list_cache[self::RSRC_LIST_KEY] = nil if refresh
159
+ return JSS.api.object_list_cache[self::RSRC_LIST_KEY] if JSS.api.object_list_cache[self::RSRC_LIST_KEY]
160
+ JSS.api.object_list_cache[self::RSRC_LIST_KEY] = JSS.api.get_rsrc(self::RSRC_BASE)[self::RSRC_LIST_KEY]
167
161
  end
168
162
 
169
163
  # Returns an Array of the JSS id numbers of all the members
@@ -174,7 +168,7 @@ module JSS
174
168
  #
175
169
  # @param refresh[Boolean] should the data be re-queried from the API?
176
170
  #
177
- # @return [Array<Integer>] the ids of all items of this subclass in the JSS
171
+ # @return [Array<Integer>] the ids of all it1ems of this subclass in the JSS
178
172
  #
179
173
  def self.all_ids(refresh = false)
180
174
  all(refresh).map { |i| i[:id] }
@@ -238,9 +232,9 @@ module JSS
238
232
  # @return [Hash{Integer => Object}] the objects requested
239
233
  def self.all_objects(refresh = false)
240
234
  objects_key = "#{self::RSRC_LIST_KEY}_objects".to_sym
241
- @@all_items[objects_key] = nil if refresh
242
- return @@all_items[objects_key] if @@all_items[objects_key]
243
- @@all_items[objects_key] = all(refresh).map { |o| new id: o[:id] }
235
+ JSS.api.object_list_cache[objects_key] = nil if refresh
236
+ return JSS.api.object_list_cache[objects_key] if JSS.api.object_list_cache[objects_key]
237
+ JSS.api.object_list_cache[objects_key] = all(refresh).map { |o| new id: o[:id] }
244
238
  end
245
239
 
246
240
  # Return true or false if an object of this subclass
@@ -368,6 +362,44 @@ module JSS
368
362
  end
369
363
  end
370
364
 
365
+ # What are all the lookup keys available for this class?
366
+ #
367
+ # @return [Array<Symbol>] the DEFAULT_LOOKUP_KEYS plus any OTHER_LOOKUP_KEYS
368
+ # defined for this class
369
+ #
370
+ def self.lookup_keys
371
+ return DEFAULT_LOOKUP_KEYS.keys unless defined? self::OTHER_LOOKUP_KEYS
372
+ DEFAULT_LOOKUP_KEYS.keys + self::OTHER_LOOKUP_KEYS.keys
373
+ end
374
+
375
+ # @return [Hash] the available lookup keys mapped to the appropriate
376
+ # resource key for building a REST url to retrieve an object.
377
+ #
378
+ def self.rsrc_keys
379
+ hash = {}
380
+ all_keys = if defined?(self::OTHER_LOOKUP_KEYS)
381
+ DEFAULT_LOOKUP_KEYS.merge self::OTHER_LOOKUP_KEYS
382
+ else
383
+ DEFAULT_LOOKUP_KEYS
384
+ end
385
+ all_keys.each { |key, deets| hash[key] = deets[:rsrc_key]}
386
+ hash
387
+ end
388
+
389
+ # @return [Hash] the available lookup keys mapped to the appropriate
390
+ # list class method (e.g. id: :all_ids )
391
+ #
392
+ def self.lookup_key_list_methods
393
+ hash = {}
394
+ all_keys = if defined?(self::OTHER_LOOKUP_KEYS)
395
+ DEFAULT_LOOKUP_KEYS.merge self::OTHER_LOOKUP_KEYS
396
+ else
397
+ DEFAULT_LOOKUP_KEYS
398
+ end
399
+ all_keys.each { |key, deets| hash[key] = deets[:list]}
400
+ hash
401
+ end
402
+
371
403
  # Retrieve an object from the API.
372
404
  #
373
405
  # This is the preferred way to retrieve existing objects from the JSS.
@@ -382,11 +414,26 @@ module JSS
382
414
  #
383
415
  # @return [APIObject] The ruby-instance of a JSS object
384
416
  #
385
- def self.fetch(**args)
417
+ def self.fetch(arg)
386
418
  raise JSS::UnsupportedError, 'JSS::APIObject cannot be instantiated' if self.class == JSS::APIObject
387
- raise ArgumentError, 'Use .create to create new JSS objects' if args[:id] == :new
388
- new args
389
- end
419
+
420
+ # if given a hash (or a colletion of named params)
421
+ # pass to .new
422
+ if arg.is_a? Hash
423
+ raise ArgumentError, 'Use .create to create new JSS objects' if arg[:id] == :new
424
+ return new arg
425
+ end
426
+
427
+ # loop thru the lookup_key list methods for this class
428
+ # and if it's result includes the desired value,
429
+ # the pass they key and arg to .new
430
+ lookup_key_list_methods.each do |key, method_name|
431
+ return new({key => arg}) if self.send(method_name).include? arg
432
+ end # each key
433
+
434
+ # if we're here, we couldn't find a matching object
435
+ raise NoSuchItemError, "No #{self::RSRC_OBJECT_KEY} found matching '#{arg}'"
436
+ end # fetch
390
437
 
391
438
  # Make a ruby instance of a not-yet-existing APIObject.
392
439
  #
@@ -404,14 +451,13 @@ module JSS
404
451
  #
405
452
  # @return [APIObject] The un-created ruby-instance of a JSS object
406
453
  #
407
- def self.make(**args)
454
+ def self.make(args = {})
408
455
  raise JSS::UnsupportedError, 'JSS::APIObject cannot be instantiated' if self.class == JSS::APIObject
409
456
  raise ArgumentError, "Use '#{self.class}.fetch id: xx' to retrieve existing JSS objects" if args[:id]
410
457
  args[:id] = :new
411
458
  new args
412
459
  end
413
460
 
414
-
415
461
  ### Class Constants
416
462
  #####################################
417
463
 
@@ -420,13 +466,37 @@ module JSS
420
466
  #
421
467
  REQUIRED_DATA_KEYS = [:id, :name].freeze
422
468
 
423
- # By default, these keys are available for object lookups
424
- # Others can be added by subclasses using an array of them
425
- # as the second argument to super(initialize)
426
- # The keys must be Symbols that match the keyname in the resource url.
427
- # e.g. :serialnumber for JSSResource/computers/serialnumber/xxxxx
469
+ # All API objects have an id and a name. As such By these keys are available
470
+ # for object lookups.
428
471
  #
429
- DEFAULT_LOOKUP_KEYS = [:id, :name].freeze
472
+ # Others can be defined by subclasses in their OTHER_LOOKUP_KEYS constant
473
+ # which has the same format, described here:
474
+ #
475
+ # The merged Hashes DEFAULT_LOOKUP_KEYS and OTHER_LOOKUP_KEYS
476
+ # define what unique identifiers can be passed as parameters to the
477
+ # fetch method for retrieving an object from the API.
478
+ # They also define the class methods that return a list (Array) of all such
479
+ # identifiers for the class (e.g. the :all_ids class method returns an array
480
+ # of all id's for an APIObject subclass)
481
+ #
482
+ # Since there's often a discrepency between the name of the identifier as
483
+ # an attribute (e.g. serial_number) and the REST resource key for
484
+ # retrieving that object (e.g. ../computers/serialnumber/xxxxx) this hash
485
+ # also explicitly provides the REST resource key for a given lookup key, so
486
+ # e.g. both serialnumber and serial_number can be used, and both will have
487
+ # the resource key 'serialnumber' and the list method ':all_serial_numbers'
488
+ #
489
+ # Here's how the Hash is structured, using serialnumber as an example:
490
+ #
491
+ # LOOKUP_KEYS = {
492
+ # serialnumber: {rsrc_key: :serialnumber, list: :all_serial_numbers},
493
+ # serial_number: {rsrc_key: :serialnumber, list: :all_serial_numbers}
494
+ # }
495
+ #
496
+ DEFAULT_LOOKUP_KEYS = {
497
+ id: {rsrc_key: :id, list: :all_ids},
498
+ name: {rsrc_key: :name, list: :all_names}
499
+ }.freeze
430
500
 
431
501
  # Attributes
432
502
  #####################################
@@ -461,20 +531,13 @@ module JSS
461
531
  # @option args :name[String] the name to look up
462
532
  #
463
533
  # @option args :data[Hash] the JSON output of a separate {JSS::APIConnection} query
534
+ # NOTE: This arg is deprecated and will be removed in a future release.
464
535
  #
465
- # @param other_lookup_keys[Array<Symbol>] Hash keys other than :id and :name, by which an API
466
- # lookup may be performed.
467
536
  #
468
- def initialize(args = {}, other_lookup_keys = [])
469
- args[:other_lookup_keys] ||= other_lookup_keys
537
+ def initialize(args = {})
470
538
 
471
539
  raise JSS::UnsupportedError, 'JSS::APIObject cannot be instantiated' if self.class == JSS::APIObject
472
540
 
473
- ### what lookup key are we using, if any?
474
- lookup_keys = DEFAULT_LOOKUP_KEYS
475
- lookup_keys += self.class::OTHER_LOOKUP_KEYS if defined? self.class::OTHER_LOOKUP_KEYS
476
- lookup_key = (lookup_keys & args.keys)[0]
477
-
478
541
  ####### Previously looked-up JSON data
479
542
  # DEPRECATED: pre-lookedup data is never used
480
543
  # and support for it will be going away.
@@ -486,9 +549,9 @@ module JSS
486
549
 
487
550
  ###### Make a new one in the JSS, but only if we've included the Creatable module
488
551
  elsif args[:id] == :new
489
-
490
552
  validate_init_for_creation(args)
491
553
  setup_object_for_creation(args)
554
+
492
555
  return
493
556
 
494
557
  ###### Look up the data via the API
@@ -496,6 +559,7 @@ module JSS
496
559
  @init_data = look_up_object_data(args)
497
560
  end ## end arg parsing
498
561
 
562
+
499
563
  parse_init_data
500
564
  @need_to_update = false
501
565
  end # init
@@ -596,7 +660,7 @@ module JSS
596
660
  #
597
661
  def delete
598
662
  return nil unless @in_jss
599
- JSS::API.delete_rsrc @rest_rsrc
663
+ JSS.api.delete_rsrc @rest_rsrc
600
664
  @rest_rsrc = "#{self.class::RSRC_BASE}/name/#{CGI.escape @name}"
601
665
  @id = nil
602
666
  @in_jss = false
@@ -668,16 +732,20 @@ module JSS
668
732
  #
669
733
  def look_up_object_data(args)
670
734
  # what lookup key are we using?
671
- combined_lookup_keys = self.class::DEFAULT_LOOKUP_KEYS + args[:other_lookup_keys]
672
- lookup_key = (combined_lookup_keys & args.keys)[0]
735
+ lookup_keys = self.class.lookup_keys
736
+ lookup_key = (self.class.lookup_keys & args.keys)[0]
737
+ raise JSS::MissingDataError, "Args must include a lookup key, one of: :#{lookup_keys.join(', :')}" unless lookup_key
738
+ rsrc_key = self.class.rsrc_keys[lookup_key]
673
739
 
674
- raise JSS::MissingDataError, "Args must include a lookup key, one of: :#{combined_lookup_keys.join(', :')}" unless lookup_key
740
+ rsrc = "#{self.class::RSRC_BASE}/#{rsrc_key}/#{args[lookup_key]}"
675
741
 
676
- rsrc = "#{self.class::RSRC_BASE}/#{lookup_key}/#{args[lookup_key]}"
742
+ # if needed, a non-standard object key can be passed by a subclass.
743
+ # e.g. User when loookup is by email.
744
+ rsrc_object_key = args[:rsrc_object_key] ? args[:rsrc_object_key] : self.class::RSRC_OBJECT_KEY
677
745
 
678
- return JSS::API.get_rsrc(rsrc)[self.class::RSRC_OBJECT_KEY]
746
+ return JSS.api.get_rsrc(rsrc)[rsrc_object_key]
679
747
  rescue RestClient::ResourceNotFound
680
- raise NoSuchItemError, "No #{self.class::RSRC_OBJECT_KEY} found matching: #{lookup_key}/#{args[lookup_key]}"
748
+ raise NoSuchItemError, "No #{self.class::RSRC_OBJECT_KEY} found matching: #{rsrc_key}/#{args[lookup_key]}"
681
749
  end
682
750
 
683
751
  # Start examining the @init_data recieved from the API
@@ -685,6 +753,7 @@ module JSS
685
753
  # @return [void]
686
754
  #
687
755
  def parse_init_data
756
+ @init_data ||= {}
688
757
  # set empty strings to nil
689
758
  @init_data.jss_nillify! '', :recurse
690
759
 
@@ -802,7 +871,7 @@ module JSS
802
871
  def setup_object_for_creation(args)
803
872
  # NOTE: subclasses may want to pre-populate more keys in @init_data when :id == :new
804
873
  # then parse them into attributes later.
805
- @init_data = { name: args[:name] }
874
+ @init_data = args
806
875
  @name = args[:name]
807
876
  @in_jss = false
808
877
  @rest_rsrc = "#{self.class::RSRC_BASE}/name/#{CGI.escape @name}"
@@ -61,7 +61,12 @@ module JSS
61
61
  RSRC_OBJECT_KEY = :account
62
62
 
63
63
  # these keys, as well as :id and :name, can be used to look up objects of this class in the JSS
64
- OTHER_LOOKUP_KEYS = [:userid, :username, :groupid, :groupname].freeze
64
+ OTHER_LOOKUP_KEYS = {
65
+ userid: {rsrc_key: :userid, list: :all_user_ids},
66
+ username: {rsrc_key: :username, list: :all_user_names},
67
+ groupid: {rsrc_key: :groupid, list: :all_group_ids},
68
+ groupname: {rsrc_key: :groupname, list: :all_group_names}
69
+ }.freeze
65
70
 
66
71
  # Class Methods
67
72
  #####################################
@@ -167,11 +167,11 @@ module JSS
167
167
  raise JSS::InvalidDataError, 'JSS::Criteriable::Criteria instance required' unless @criteria.is_a? JSS::Criteriable::Criteria
168
168
  raise JSS::InvalidDataError, 'display_fields must be an Array.' unless @display_fields.is_a? Array
169
169
 
170
- orig_timeout = JSS::API.cnx.options[:timeout]
171
- JSS::API.timeout = 1800
170
+ orig_timeout = JSS.api_connection.cnx.options[:timeout]
171
+ JSS.api_connection.timeout = 1800
172
172
  super()
173
173
  requery_search_results if get_results
174
- JSS::API.timeout = orig_timeout
174
+ JSS.api_connection.timeout = orig_timeout
175
175
 
176
176
  @id # remember to return the id
177
177
  end
@@ -185,11 +185,11 @@ module JSS
185
185
  # @return [Integer] the id of the updated search
186
186
  #
187
187
  def update(get_results = false)
188
- orig_timeout = JSS::API.cnx.options[:timeout]
189
- JSS::API.timeout = 1800
188
+ orig_timeout = JSS.api_connection.cnx.options[:timeout]
189
+ JSS.api_connection.timeout = 1800
190
190
  super()
191
191
  requery_search_results if get_results
192
- JSS::API.timeout = orig_timeout
192
+ JSS.api_connection.timeout = orig_timeout
193
193
 
194
194
  @id # remember to return the id
195
195
  end
@@ -201,17 +201,17 @@ module JSS
201
201
  # @return [Array<Hash>] the new search results
202
202
  #
203
203
  def requery_search_results
204
- orig_open_timeout = JSS::API.cnx.options[:open_timeout]
205
- orig_timeout = JSS::API.cnx.options[:timeout]
206
- JSS::API.timeout = 1800
207
- JSS::API.open_timeout = 1800
204
+ orig_open_timeout = JSS.api_connection.cnx.options[:open_timeout]
205
+ orig_timeout = JSS.api_connection.cnx.options[:timeout]
206
+ JSS.api_connection.timeout = 1800
207
+ JSS.api_connection.open_timeout = 1800
208
208
  begin
209
209
  requery = self.class.new(id: @id)
210
210
  @search_results = requery.search_results
211
211
  @result_display_keys = requery.result_display_keys
212
212
  ensure
213
- JSS::API.timeout = orig_timeout
214
- JSS::API.open_timeout = orig_open_timeout
213
+ JSS.api_connection.timeout = orig_timeout
214
+ JSS.api_connection.open_timeout = orig_open_timeout
215
215
  end
216
216
  end
217
217
 
@@ -26,24 +26,14 @@
26
26
  ###
27
27
  module JSS
28
28
 
29
- # Module Constants
30
- #####################################
31
-
32
- # Module Variables
33
- #####################################
34
-
35
- # Module Methods
36
- #####################################
37
-
38
29
  # This class represents a Computer in the JSS.
39
30
  #
40
- # ===Adding Computers to the JSS
31
+ # === Adding Computers to the JSS
41
32
  #
42
- # This class cannot be used to add new Computers to the JSS. Please use other
43
- # Casper methods (like the Recon App or QuickAdd package)
33
+ # At the moment, this class cannot be used to add new Computers to the JSS.
34
+ # Please use other methods (like the Recon App or QuickAdd package)
44
35
  #
45
- # ---
46
- # ===Editing values
36
+ # === Editing values
47
37
  #
48
38
  # Any data that arrives in the JSS via an "inventory update"
49
39
  # (a.k.a. 'recon') cannot be modified through this class, or the API.
@@ -66,27 +56,91 @@ module JSS
66
56
  # After making any changes, you must call #update to send those
67
57
  # changes to the server.
68
58
  #
69
- # ---
70
59
  # === MDM Commands
71
60
  #
72
- # ==== MDM Commands are Not Yet Supported!
73
- # *Hopefully they will be soon*
61
+ # The following methods can be used to send an APNS command to the
62
+ # computer represented by an instance of JSS::Computer, equivalent to
63
+ # clicking one of the buttons on the Management Commands section of the
64
+ # Management tab of the Computer details page in the JSS UI.
65
+ #
66
+ # - {#blank_push} (aliases blank, noop, send_blank_push)
67
+ # - {#device_lock} (aliases lock, lock_device)
68
+ # - {#erase_device} (aliases wipe)
69
+ # - {#remove_mdm_profile}
70
+ #
71
+ # To send an MDM command without making a Computer instance, use the class
72
+ # {JSS::Computer.send_mdm_command} which can take multiple computer
73
+ # identifiers at once.
74
+ #
75
+ # NOTE: the poorly named 'UnmanageDevice' command via the API is implemented
76
+ # as the {#remove_mdm_profile} method (which is its name in the webUI).
77
+ # Calling that method will NOT unmanage the machine from the JSS's point
78
+ # of view, it will just remove the mdm management profile from the machine
79
+ # and all configuration profiles that were installed via the JSS. Those
80
+ # profiles may be re-installed automatically later if the computer is still in
81
+ # scope for them
82
+ #
83
+ # The {#make_unmanaged} method also removes the mdm profile, but actually
84
+ # does make the machine unmanged by the JSS, setting the management acct to
85
+ # nil, and requring re-enrollment.
86
+ #
87
+ # === Computer History
88
+ #
89
+ # Computer instances can now retrieve their management history from the JSS.
90
+ #
91
+ # The full history data is available from the {#history} method, but beware that
92
+ # it is very large.
93
+ #
94
+ # Subsets of that history have their own methods, which are faster and only retrieve
95
+ # the subset requested. See {#usage_logs}, {#audits}, {#policy_los},
96
+ # {#completed_policies}, {#failed_polices}, {#casper_remote_logs},
97
+ # {#screen_sharing_logs}, {#casper_imaging_logs}, {#commands},
98
+ # {#user_location_history},and {#app_store_app_history}
99
+ #
100
+ # When any of the history methods is used the first time, the data is read
101
+ # from the API and cached internally, and that data is
102
+ # used for all future calls.. To re-read the data from the API and re-cache it,
103
+ # provide any non-false parameter to the subset methods , or `refresh: true`
104
+ # to the main {#history} method.
105
+ #
106
+ # === Appication Usage History
107
+ #
108
+ # Computer Instances now have access to their Application Usage history
109
+ # via the {#application_usage} method.
110
+ # Call the method with a start-date value (either a String or a Time object)
111
+ # and an optional end-date value. If you omite the end-date, the start-date
112
+ # is used and you'll see usage for just that day.
113
+ #
114
+ # See {#application_usage} for details about the data returned.
74
115
  #
75
- # The following methods will be used to send an APNS command to the computer represented by an
76
- # instance of JSS::Computer, equivalent to clicking one of the buttons on
77
- # the Management Commands section of the Management tab of the Computer details page in the JSS UI.
116
+ # NOTE: your JSS must be gathering Appication Usage data in order
117
+ # for any data to be returned, and the usage history will only go back as
118
+ # far as your setting for flushing of Application Usage Logs.
78
119
  #
79
- # The methods supported will be:
80
- # - #blank_push (aliases blank, noop, send_blank_push)
81
- # - #device_lock (aliases lock, lock_device)
82
- # - #erase_device (aliases wipe)
120
+ # === Management Data
83
121
  #
84
- # To send an MDM command without making an instance, use the class method {.send_mdm_command}
122
+ # The computers 'manamgement data', as presented on the 'Management' tab of
123
+ # the computer's detail page in the JSS web UI, is available from the
124
+ # {#management_data} method. That method may return a large dataset,
125
+ # unless a subset is requested.
85
126
  #
86
- # Each returns true if the command as sent.
127
+ # Subsets of management data have their own methods, which are faster and
128
+ # only retrieve the subset requested. See {#smart_groups}, {#static_groups},
129
+ # {#policies}, {#configuration_profiles}, {#ebooks}, {#app_store_apps},
130
+ # {#restricted_software}, and {#patch_titles}
87
131
  #
88
- # ---
89
- # ===Other Methods
132
+ # The subset methods can take an 'only:' parameter, which is a symbol specifying
133
+ # the value you care to see. For example {#smart_groups} returns an array
134
+ # of hashes, one for each smart_group the computer is in. Those hashes
135
+ # have two keys, :name, and :id. However if you only want an array of
136
+ # names, you can call `smart_groups only: :name`
137
+ #
138
+ # When any of the manamgement data methods are used the first time, the data
139
+ # is read from the API and cached internally, the cache is then
140
+ # used for all future calls. To re-read the data from the API and re-cache it,
141
+ # provide `refresh: true` to any of the manamgement data methods.
142
+ #
143
+ # === Other Methods
90
144
  #
91
145
  # - {#set_management_to} change the management acct and passwd for this computer, aliased to #make_managed
92
146
  # - requires calling #update to push changes to the server
@@ -118,14 +172,185 @@ module JSS
118
172
 
119
173
  extend JSS::Matchable
120
174
 
121
- # Class Variables
175
+ # Class Constants
122
176
  #####################################
123
177
 
124
- @@all_computers = nil
178
+ # The base for REST resources of this class
179
+ RSRC_BASE = 'computers'.freeze
180
+
181
+ # The (temporary?) list-resource
182
+ LIST_RSRC = "#{RSRC_BASE}/subset/basic".freeze
183
+
184
+ # the hash key used for the JSON list output of all objects in the JSS
185
+ RSRC_LIST_KEY = :computers
186
+
187
+ # The hash key used for the JSON object output.
188
+ # It's also used in various error messages
189
+ RSRC_OBJECT_KEY = :computer
190
+
191
+ # these keys, as well as :id and :name, are present in valid API JSON data for this class
192
+ # DEPRECATED, with be removed in a future release.
193
+ VALID_DATA_KEYS = %i[sus distribution_point alt_mac_address].freeze
194
+
195
+ # these keys, as well as :id and :name, can be used to look up objects of this class in the JSS
196
+ OTHER_LOOKUP_KEYS = {
197
+ udid: { rsrc_key: :udid, list: :all_udids },
198
+ serialnumber: { rsrc_key: :serialnumber, list: :all_serial_numbers },
199
+ serial_number: { rsrc_key: :serialnumber, list: :all_serial_numbers },
200
+ macaddress: { rsrc_key: :macaddress, list: :all_mac_addresses },
201
+ mac_address: { rsrc_key: :macaddress, list: :all_mac_addresses }
202
+ }.freeze
203
+
204
+ # This class lets us seach for computers
205
+ SEARCH_CLASS = JSS::AdvancedComputerSearch
206
+
207
+ # This is the class for relevant Extension Attributes
208
+ EXT_ATTRIB_CLASS = JSS::ComputerExtensionAttribute
209
+
210
+ # Boot partitions are noted with the string "(Boot Partition)" at the end
211
+ BOOT_FLAG = ' (Boot Partition)'.freeze
212
+
213
+ # file uploads can send attachments to the JSS using :computers as the sub-resource.
214
+ UPLOAD_TYPES = { attachment: :computers }.freeze
215
+
216
+ # The base REST resource for sending computer MDM commands
217
+ COMPUTER_MDM_RSRC = 'computercommands/command'.freeze
218
+
219
+ # A mapping of Symbols available to the send_mdm_command class method, to
220
+ # the String commands actuallly sent via the API.
221
+ COMPUTER_MDM_COMMANDS = {
222
+ blank_push: 'BlankPush',
223
+ blankpush: 'BlankPush',
224
+ send_blank_push: 'BlankPush',
225
+ blank: 'BlankPush',
226
+ noop: 'BlankPush',
227
+ device_lock: 'DeviceLock',
228
+ devicelock: 'DeviceLock',
229
+ lock: 'DeviceLock',
230
+ lock_device: 'DeviceLock',
231
+ erase_device: 'EraseDevice',
232
+ erasedevice: 'EraseDevice',
233
+ erase: 'EraseDevice',
234
+ wipe: 'EraseDevice',
235
+ unmanage_device: 'UnmanageDevice',
236
+ unmanagedevice: 'UnmanageDevice',
237
+ unmanage: 'UnmanageDevice'
238
+ }.freeze
239
+
240
+ # these MDM commands require a passcode
241
+ COMPUTER_MDM_COMMANDS_NEEDING_PASSCODE = %w[DeviceLock EraseDevice].freeze
242
+
243
+ # The API resource for app usage
244
+ APPLICATION_USAGE_RSRC = 'computerapplicationusage'.freeze
245
+
246
+ # The date format for retrieving usage data
247
+ APPLICATION_USAGE_DATE_FMT = '%Y-%m-%d'.freeze
248
+
249
+ # The classes that can be used with the date format
250
+ APPLICATION_USAGE_DATE_CLASSES = [Time, DateTime, Date].freeze
251
+
252
+ # The top-level hash key of the raw app usage data
253
+ APPLICATION_USAGE_KEY = :computer_application_usage
254
+
255
+ # The API resource for computer_management data
256
+ MGMT_DATA_RSRC = 'computermanagement'.freeze
257
+
258
+ # The top-level hash key of the computer_management data
259
+ MGMT_DATA_KEY = :computer_management
260
+
261
+ # Thes are both the subset names in the resrouce URLS (when
262
+ # converted to strings) and the second-level hash key of the
263
+ # returned subset data.
264
+ MGMT_DATA_SUBSETS = %i[
265
+ smart_groups
266
+ static_groups
267
+ mac_app_store_apps
268
+ policies
269
+ ebooks
270
+ os_x_configuration_profiles
271
+ restricted_software
272
+ patch_reporting_software_titles
273
+ ].freeze
274
+
275
+ # The API Resource for the computer checkin settings
276
+ CHECKIN_RSRC = 'computercheckin'.freeze
277
+
278
+ # The top-level hash key for the checkin settings
279
+ CHECKIN_KEY = :computer_check_in
280
+
281
+ # The API Resource for the computer inventory collection settings
282
+ INV_COLLECTION_RSRC = 'computerinventorycollection'.freeze
283
+
284
+ # The top-level hash key for the inventory collection settings
285
+ INV_COLLECTION_KEY = :computer_inventory_collection
286
+
287
+ # The API Resource for the computer history data
288
+ HISTORY_RSRC = 'computerhistory'.freeze
289
+
290
+ # The top-level hash key for the history data
291
+ HISTORY_KEY = :computer_history
292
+
293
+ # The keys are both the subset names in the resrouce URLS (when
294
+ # converted to strings) and the second-level hash key of the
295
+ # returned subset data.
296
+ #
297
+ # The values are the key within each history item that contains the
298
+ # 'epoch' timestamp, for conver
299
+ HISTORY_SUBSETS = %i[
300
+ computer_usage_logs
301
+ audits
302
+ policy_logs
303
+ casper_remote_logs
304
+ screen_sharing_logs
305
+ casper_imaging_logs
306
+ commands
307
+ user_location
308
+ mac_app_store_applications
309
+ ].freeze
310
+
311
+ # HISTORY_SUBSETS = %i(
312
+ # computer_usage_logs date_time_epoch
313
+ # audits
314
+ # policy_logs date_completed_epoch
315
+ # casper_remote_logs date_time_epoch
316
+ # screen_sharing_logs date_time_epoch
317
+ # casper_imaging_logs
318
+ # commands completed_epoch
319
+ # user_location
320
+ # mac_app_store_applications
321
+ # ).freeze
322
+
323
+ POLICY_STATUS_COMPLETED = 'Completed'.freeze
324
+
325
+ POLICY_STATUS_FAILED = 'Failed'.freeze
326
+
327
+ POLICY_STATUS_PENDING = 'Pending'.freeze
125
328
 
126
329
  # Class Methods
127
330
  #####################################
128
331
 
332
+ # Display the current Computer CheckIn settings in the JSS.
333
+ # Currently this is read-only in ruby-jss, even tho the API
334
+ # allows updating.
335
+ #
336
+ # @return [Hash] the Computer Checkin Settings from the
337
+ # currently connected JSS.
338
+ #
339
+ def self.checkin_settings
340
+ JSS.api_connection.get_rsrc(CHECKIN_RSRC)[CHECKIN_KEY]
341
+ end
342
+
343
+ # Display the current Computer Inventory Collection settings in the JSS.
344
+ # Currently this is read-only in ruby-jss, even tho the API
345
+ # allows updating.
346
+ #
347
+ # @return [Hash] the Computer Inventpry Collection Settings from the
348
+ # currently connected JSS.
349
+ #
350
+ def self.inventory_collection_settings
351
+ JSS.api_connection.get_rsrc(INV_COLLECTION_RSRC)[INV_COLLECTION_KEY]
352
+ end
353
+
129
354
  # A larger set of info about the computers in the JSS.
130
355
  #
131
356
  # Casper 9.4 introduced the API Resource /computers/subset/basic
@@ -143,9 +368,9 @@ module JSS
143
368
  # @return [Array<Hash{:name=>String, :id=> Integer}>]
144
369
  #
145
370
  def self.all(refresh = false)
146
- @@all_computers = nil if refresh
147
- return @@all_computers if @@all_computers
148
- @@all_computers = JSS::API.get_rsrc(self::LIST_RSRC)[self::RSRC_LIST_KEY]
371
+ JSS.api.object_list_cache[RSRC_LIST_KEY] = nil if refresh
372
+ return JSS.api.object_list_cache[RSRC_LIST_KEY] if JSS.api.object_list_cache[RSRC_LIST_KEY]
373
+ JSS.api.object_list_cache[RSRC_LIST_KEY] = JSS.api_connection.get_rsrc(self::LIST_RSRC)[self::RSRC_LIST_KEY]
149
374
  end
150
375
 
151
376
  # @return [Array<String>] all computer serial numbers in the jss
@@ -170,7 +395,7 @@ module JSS
170
395
 
171
396
  # @return [Array<Hash>] all unmanaged computers in the jss
172
397
  def self.all_unmanaged(refresh = false)
173
- all(refresh).select { |d| !(d[:managed]) }
398
+ all(refresh).reject { |d| d[:managed] }
174
399
  end
175
400
 
176
401
  # @return [Array<Hash>] all laptop computers in the jss
@@ -200,7 +425,7 @@ module JSS
200
425
 
201
426
  # @return [Array<Hash>] all desktop macs in the jss
202
427
  def self.all_desktops(refresh = false)
203
- all(refresh).select { |d| d[:model] !~ /serve|book/i }
428
+ all(refresh).reject { |d| d[:model] =~ /serve|book/i }
204
429
  end
205
430
 
206
431
  # @return [Array<Hash>] all imacs in the jss
@@ -218,88 +443,53 @@ module JSS
218
443
  all(refresh).select { |d| d[:model] =~ /^macpro/i }
219
444
  end
220
445
 
221
- # Send an MDM command to a managed computer by id or name
222
- #
223
- # @param computer[String,Integer] the name or id of the computer to recieve the command
224
- # @param command[Symbol] the command to send, one of the keys of COMPUTER_MDM_COMMANDS
225
- #
226
- # @return [true] if the command was sent
227
- #
228
-
229
- # Not functional until I get more docs from JAMF
446
+ # Send an MDM command to one or more managed computers by id or name
230
447
  #
231
- # def self.send_mdm_command(computer,command)
232
448
  #
233
- # raise JSS::NoSuchItemError, "Unknown command '#{command}'" unless COMPUTER_MDM_COMMANDS.keys.include? command
449
+ # @param targets[String,Integer,Array<String,Integer>]
450
+ # the name or id of the computer to receive the command, or
451
+ # an array of such names or ids, or a comma-separated string
452
+ # of them.
453
+ # @param command[Symbol] the command to send, one of the keys
454
+ # of COMPUTER_MDM_COMMANDS
234
455
  #
235
- # command_xml ="#{JSS::APIConnection::XML_HEADER}<computer><command>#{COMPUTER_MDM_COMMANDS[command]}</command></computer>"
236
- # the_id = nil
456
+ # @param passcode[String] some commands require a 6-character passcode
237
457
  #
238
- # if computer.to_s =~ /^\d+$/
239
- # the_id = computer
240
- # else
241
- # the_id = self.map_all_ids_to(:name).invert[computer]
242
- # end
458
+ # @return [String] The uuid of the MDM command sent, if applicable
459
+ # (blank pushes do not generate uuids)
243
460
  #
244
- # if the_id
245
- # response = JSS::API.put_rsrc("#{RSRC_BASE}/id/#{the_id}", command_xml)
246
- # response =~ %r{<notification_sent>(.+)</notification_sent>}
247
- # return ($1 and $1 == "true")
248
- # end
249
- # raise JSS::UnmanagedError, "Cannot send command to unknown/unmanaged computer '#{computer}'"
250
- # end
461
+ def self.send_mdm_command(targets, command, passcode = nil)
462
+ raise JSS::NoSuchItemError, "Unknown command '#{command}'" unless COMPUTER_MDM_COMMANDS.keys.include? command
251
463
 
252
- # Class Constants
253
- #####################################
464
+ command = COMPUTER_MDM_COMMANDS[command]
465
+ cmd_rsrc = "#{COMPUTER_MDM_RSRC}/#{command}"
254
466
 
255
- # The base for REST resources of this class
256
- RSRC_BASE = 'computers'.freeze
257
-
258
- # The (temporary?) list-resource
259
- LIST_RSRC = "#{RSRC_BASE}/subset/basic".freeze
260
-
261
- # the hash key used for the JSON list output of all objects in the JSS
262
- RSRC_LIST_KEY = :computers
263
-
264
- # The hash key used for the JSON object output.
265
- # It's also used in various error messages
266
- RSRC_OBJECT_KEY = :computer
467
+ if COMPUTER_MDM_COMMANDS_NEEDING_PASSCODE.include? command
468
+ unless passcode && passcode.is_a?(String) && passcode.length == 6
469
+ raise JSS::MissingDataError, "Command '#{command}' requires a 6-character passcode"
470
+ end
471
+ cmd_rsrc << "/passcode/#{passcode}"
472
+ end
267
473
 
268
- # these keys, as well as :id and :name, are present in valid API JSON data for this class
269
- # DEPRECATED, with be removed in a future release.
270
- VALID_DATA_KEYS = [:sus, :distribution_point, :alt_mac_address].freeze
474
+ targets = JSS.to_s_and_a(targets.to_s)[:arrayform] unless targets.is_a? Array
271
475
 
272
- # these keys, as well as :id and :name, can be used to look up objects of this class in the JSS
273
- OTHER_LOOKUP_KEYS = [:udid, :serialnumber, :mac_address].freeze
476
+ # make sure its an array of ids
477
+ targets.map! do |comp|
478
+ if all_ids.include? comp.to_i
479
+ comp.to_i
480
+ elsif all_names.include? comp
481
+ map_all_ids_to(:name).invert[comp]
482
+ else
483
+ raise JSS::NoSuchItemError, "No computer found matching '#{comp}'"
484
+ end # if
485
+ end # map!
274
486
 
275
- # This class lets us seach for computers
276
- SEARCH_CLASS = JSS::AdvancedComputerSearch
487
+ cmd_rsrc << "/id/#{targets.join ','}"
277
488
 
278
- # This is the class for relevant Extension Attributes
279
- EXT_ATTRIB_CLASS = JSS::ComputerExtensionAttribute
280
-
281
- # Boot partitions are noted with the string "(Boot Partition)" at the end
282
- BOOT_FLAG = ' (Boot Partition)'.freeze
283
-
284
- # file uploads can send attachments to the JSS using :computers as the sub-resource.
285
- UPLOAD_TYPES = { attachment: :computers }.freeze
286
-
287
- # A mapping of Symbols available to the send_mdm_command class method, to
288
- # the String commands actuallly sent via the API.
289
- COMPUTER_MDM_COMMANDS = {
290
- blank_push: 'BlankPush',
291
- send_blank_push: 'BlankPush',
292
- blank: 'BlankPush',
293
- noop: 'BlankPush',
294
- device_lock: 'DeviceLock',
295
- lock: 'DeviceLock',
296
- lock_device: 'DeviceLock',
297
- erase_device: 'EraseDevice',
298
- erase: 'EraseDevice',
299
- wipe: 'EraseDevice',
300
- unmanage_device: 'UnmanageDevice',
301
- unmanage: 'UnmanageDevice'
302
- }.freeze
489
+ result = JSS::API.post_rsrc cmd_rsrc, nil
490
+ result =~ %r{<command_uuid>(.*)</command_uuid>}
491
+ Regexp.last_match(1)
492
+ end # send mdm command
303
493
 
304
494
  # Attributes
305
495
  #####################################
@@ -526,7 +716,7 @@ module JSS
526
716
  # As well as :id and :name, computers can be queried using :udid, :serialnumber, and :mac_address
527
717
  #
528
718
  def initialize(args = {})
529
- super args, [:udid, :serialnumber, :mac_address]
719
+ super args
530
720
 
531
721
  # now we have raw @init_data with something in it, so fill out the instance vars
532
722
  @alt_mac_address = @init_data[:general][:alt_mac_address]
@@ -621,6 +811,234 @@ module JSS
621
811
  @software[:licensed_software]
622
812
  end
623
813
 
814
+ # Get application usage data for this computer
815
+ # for a given date range.
816
+ #
817
+ # @param start_date [String,Date,DateTime,Time]
818
+ #
819
+ # @param end_date [String,Date,DateTime,Time] Defaults to start_date
820
+ #
821
+ # @return [Hash{Date=>Array<Hash>}] For each day in the range, an Array
822
+ # with one Hash per application used. The hash keys are:
823
+ # :name => String, the name of the app
824
+ # :version => String ,the version of the app
825
+ # :foreground => Integer, the minutes it was in the foreground
826
+ # :open => Integer, the minutes it was running.
827
+ #
828
+ def application_usage(start_date, end_date = nil)
829
+ end_date ||= start_date
830
+ start_date = Time.parse start_date if start_date.is_a? String
831
+ end_date = Time.parse end_date if end_date.is_a? String
832
+ unless ([start_date.class, end_date.class] - APPLICATION_USAGE_DATE_CLASSES).empty?
833
+ raise JSS::InvalidDataError, 'Invalid Start or End Date'
834
+ end
835
+ start_date = start_date.strftime APPLICATION_USAGE_DATE_FMT
836
+ end_date = end_date.strftime APPLICATION_USAGE_DATE_FMT
837
+ data = JSS.api_connection.get_rsrc(APPLICATION_USAGE_RSRC + "/id/#{@id}/#{start_date}_#{end_date}")
838
+ parsed_data = {}
839
+ data[APPLICATION_USAGE_KEY].each do |day_hash|
840
+ date = Date.parse day_hash[:date]
841
+ parsed_data[date] = day_hash[:apps]
842
+ end
843
+ parsed_data
844
+ end # app usage
845
+
846
+ # The 'computer management' data for this computer, looked up on the fly.
847
+ #
848
+ # Without specifying a subset:, the entire dataset is returned as a hash of
849
+ # arrays, one per subset
850
+ # If a subset is given then only that array is returned, and it contains
851
+ # hashes with data about each item (usually :name and :id)
852
+ #
853
+ # If the only: param is provided with a subset, it is used as a hash-key to
854
+ # map the array to just those values, so subset: :smart_groups, only: :name
855
+ # will return an array of names of smartgroups that contain this computer.
856
+ #
857
+ # @param subset[Symbol] Fetch only a subset of data, as an array.
858
+ # must be one of the symbols in MGMT_DATA_SUBSETS
859
+ #
860
+ # @param only[Symbol] When fetching a subset, only return one value
861
+ # per item in the array. meaningless without a subset.
862
+ #
863
+ # @param refresh[Boolean] should the data be re-cached from the API?
864
+ #
865
+ # @return [Hash] Without a subset:, a hash of all subsets, each of which is
866
+ # an Array
867
+ #
868
+ # @return [Array] With a subset:, an array of items in that subset.
869
+ #
870
+ def management_data(subset: nil, only: nil, refresh: false)
871
+ @management_data ||= {}
872
+ if subset
873
+ management_data_subset(subset: subset, only: only, refresh: refresh)
874
+ else
875
+ full_management_data refresh
876
+ end
877
+ end
878
+
879
+ def full_management_data(refresh = false)
880
+ @management_data[:full] = nil if refresh
881
+ return @management_data[:full] if @management_data[:full]
882
+ mgmt_rsrc = MGMT_DATA_RSRC + "/id/#{@id}"
883
+ @management_data[:full] = JSS.api.get_rsrc(mgmt_rsrc)[MGMT_DATA_KEY]
884
+ @management_data[:full]
885
+ end
886
+ private :full_management_data
887
+
888
+ def management_data_subset(subset: nil, only: nil, refresh: false)
889
+ raise "Subset must be one of :#{MGMT_DATA_SUBSETS.join ', :'}" unless MGMT_DATA_SUBSETS.include? subset
890
+ @management_data[subset] = nil if refresh
891
+ return @management_data[subset] if @management_data[subset]
892
+ subset_rsrc = MGMT_DATA_RSRC + "/id/#{@id}/subset/#{subset}"
893
+ @management_data[subset] = JSS.api.get_rsrc(subset_rsrc)[MGMT_DATA_KEY]
894
+ return @management_data[subset] unless only
895
+ @management_data[subset].map { |d| d[only] }
896
+ end
897
+ private :management_data_subset
898
+
899
+ # A shortcut for 'management_data subset: :smart_groups'
900
+ #
901
+ def smart_groups(only: nil, refresh: false)
902
+ management_data subset: :smart_groups, only: only, refresh: refresh
903
+ end
904
+
905
+ # A shortcut for 'management_data subset: :static_groups'
906
+ #
907
+ def static_groups(only: nil, refresh: false)
908
+ management_data subset: :static_groups, only: only, refresh: refresh
909
+ end
910
+
911
+ # A shortcut for 'management_data subset: :policies'
912
+ #
913
+ def policies(only: nil, refresh: false)
914
+ management_data subset: :policies, only: only, refresh: refresh
915
+ end
916
+
917
+ # A shortcut for 'management_data subset: :os_x_configuration_profiles'
918
+ #
919
+ def configuration_profiles(only: nil, refresh: false)
920
+ management_data subset: :os_x_configuration_profiles, only: only, refresh: refresh
921
+ end
922
+
923
+ # A shortcut for 'management_data subset: :ebooks'
924
+ #
925
+ def ebooks(only: nil, refresh: false)
926
+ management_data subset: :ebooks, only: only, refresh: refresh
927
+ end
928
+
929
+ # A shortcut for 'management_data subset: :mac_app_store_apps'
930
+ #
931
+ def app_store_apps(only: nil, refresh: false)
932
+ management_data subset: :mac_app_store_apps, only: only, refresh: refresh
933
+ end
934
+
935
+ # A shortcut for 'management_data subset: :restricted_software'
936
+ #
937
+ def restricted_software(only: nil, refresh: false)
938
+ management_data subset: :restricted_software, only: only, refresh: refresh
939
+ end
940
+
941
+ # A shortcut for 'management_data subset: :patch_reporting_software_titles'
942
+ #
943
+ def patch_titles(only: nil, refresh: false)
944
+ management_data subset: :patch_reporting_software_titles, only: only, refresh: refresh
945
+ end
946
+
947
+ # Return this computer's history.
948
+ # WARNING! Its huge, better to use a subset a
949
+ # nd one of the shortcut methods.
950
+ #
951
+ # @param subset[Symbol] the subset to return, rather than full history.
952
+ #
953
+ # @param refresh[Boolean] should we re-cache the data from the API?
954
+ #
955
+ # @return [Hash] The full history
956
+ #
957
+ # @return [Array] The history subset requested
958
+ #
959
+ def history(subset: nil, refresh: false)
960
+ @history ||= {}
961
+ if subset
962
+ history_subset(subset: subset, refresh: refresh)
963
+ else
964
+ full_history refresh
965
+ end
966
+ end
967
+
968
+ def full_history(refresh = false)
969
+ @history[:full] = nil if refresh
970
+ return @history[:full] if @history[:full]
971
+ history_rsrc = HISTORY_RSRC + "/id/#{@id}"
972
+ @history[:full] = JSS.api.get_rsrc(history_rsrc)[HISTORY_KEY]
973
+ @history[:full]
974
+ end
975
+ private :full_history
976
+
977
+ def history_subset(subset: nil, refresh: false)
978
+ raise "Subset must be one of :#{HISTORY_SUBSETS.join ', :'}" unless HISTORY_SUBSETS.include? subset
979
+ @history[subset] = nil if refresh
980
+ return @history[subset] if @history[subset]
981
+ subset_rsrc = HISTORY_RSRC + "/id/#{@id}/subset/#{subset}"
982
+ @history[subset] = JSS.api.get_rsrc(subset_rsrc)[HISTORY_KEY]
983
+ @history[subset]
984
+ end
985
+ private :history_subset
986
+
987
+ # Shortcut for history(:computer_usage_logs)
988
+ def usage_logs(refresh = false)
989
+ history(subset: :computer_usage_logs, refresh: refresh)
990
+ end
991
+
992
+ # Shortcut for history(:audits)
993
+ def audits(refresh = false)
994
+ history(subset: :audits, refresh: refresh)
995
+ end
996
+
997
+ # Shortcut for history(:policy_logs)
998
+ def policy_logs(refresh = false)
999
+ history(subset: :policy_logs, refresh: refresh)
1000
+ end
1001
+
1002
+ # Shortcut for history(:policy_logs), but just the completed policies
1003
+ def completed_policies(refresh = false)
1004
+ policy_logs(refresh).select { |pl| pl[:status] == POLICY_STATUS_COMPLETED }
1005
+ end
1006
+
1007
+ # Shortcut for history(:policy_logs), but just the failes policies
1008
+ def failed_policies(refresh = false)
1009
+ policy_log(refresh).select { |pl| pl[:status] == POLICY_STATUS_FAILED }
1010
+ end
1011
+
1012
+ # Shortcut for history(:casper_remote_logs)
1013
+ def casper_remote_logs(refresh = false)
1014
+ history(subset: :casper_remote_logs, refresh: refresh)
1015
+ end
1016
+
1017
+ # Shortcut for history(:screen_sharing_logs)
1018
+ def screen_sharing_logs(refresh = false)
1019
+ history(subset: :screen_sharing_logs, refresh: refresh)
1020
+ end
1021
+
1022
+ # Shortcut for history(:casper_imaging_logs)
1023
+ def casper_imaging_logs(refresh = false)
1024
+ history(subset: :casper_imaging_logs, refresh: refresh)
1025
+ end
1026
+
1027
+ # Shortcut for history(:commands)
1028
+ def commands(refresh = false)
1029
+ history(subset: :commands, refresh: refresh)
1030
+ end
1031
+
1032
+ # Shortcut for history(:user_location)
1033
+ def user_location_history(refresh = false)
1034
+ history(subset: :user_location, refresh: refresh)
1035
+ end
1036
+
1037
+ # Shortcut for history(:mac_app_store_applications)
1038
+ def app_store_app_history(refresh = false)
1039
+ history(subset: :mac_app_store_applications, refresh: refresh)
1040
+ end
1041
+
624
1042
  # Set or unset management acct and password for this computer
625
1043
  #
626
1044
  # @param name[String] the name of the management acct.
@@ -629,7 +1047,7 @@ module JSS
629
1047
  #
630
1048
  # @return [void]
631
1049
  #
632
- # The changes will need to be pushed to the server with #update
1050
+ # The changes will need to be pushed to the server with {#update}
633
1051
  # before they take effect.
634
1052
  #
635
1053
  # CAUTION: this does nothing to confirm the name and password
@@ -656,10 +1074,8 @@ module JSS
656
1074
  def make_unmanaged
657
1075
  return nil unless managed?
658
1076
  set_management_to(nil, nil)
659
- begin
660
- self.class.send_mdm_command(@id, :unmanage_device)
661
- rescue
662
- end
1077
+ return unless mdm_capable
1078
+ self.class.send_mdm_command(@id, :unmanage_device)
663
1079
  end
664
1080
 
665
1081
  #
@@ -751,34 +1167,47 @@ module JSS
751
1167
  @software = nil
752
1168
  end # delete
753
1169
 
754
- # Not Functional until I get more docs from JAMF
755
- #
756
- # #
757
- # # Send a blank_push MDM command
758
- # #
759
- # def blank_push
760
- # self.class.send_mdm_command @id, :blank_push
761
- # end
762
- # alias noop blank_push
763
- # alias send_blank_push blank_push
764
- #
765
- # #
766
- # # Send a device_lock MDM command
767
- # #
768
- # def device_lock
769
- # self.class.send_mdm_command @id, :device_lock
770
- # end
771
- # alias lock device_lock
772
- # alias lock_device device_lock
773
- #
774
- # #
775
- # # Send an erase_device MDM command
776
- # #
777
- # def erase_device
778
- # self.class.send_mdm_command @id, :erase_device
779
- # end
780
- # alias erase erase_device
781
- # alias wipe erase_device
1170
+ # Send a blank_push MDM command
1171
+ #
1172
+ # See JSS::Computer.send_mdm_command
1173
+ #
1174
+ def blank_push
1175
+ self.class.send_mdm_command @id, :blank_push
1176
+ end
1177
+ alias noop blank_push
1178
+ alias send_blank_push blank_push
1179
+
1180
+ # Send a device_lock MDM command
1181
+ #
1182
+ # See JSS::Computer.send_mdm_command
1183
+ #
1184
+ def device_lock(passcode)
1185
+ self.class.send_mdm_command @id, :device_lock, passcode
1186
+ end
1187
+ alias lock device_lock
1188
+ alias lock_device device_lock
1189
+
1190
+ # Send an erase_device MDM command
1191
+ #
1192
+ # See JSS::Computer.send_mdm_command
1193
+ #
1194
+ def erase_device(passcode)
1195
+ self.class.send_mdm_command @id, :erase_device, passcode
1196
+ end
1197
+ alias erase erase_device
1198
+ alias wipe erase_device
1199
+
1200
+ # Remove MDM management profile without
1201
+ # un-enrolling from the JSS or
1202
+ # resetting the JSS management acct.
1203
+ #
1204
+ # To do those things as well, see {#make_unmanaged}
1205
+ #
1206
+ # See JSS::Computer.send_mdm_command
1207
+ #
1208
+ def remove_mdm_profile
1209
+ self.class.send_mdm_command(@id, :unmanage_device)
1210
+ end
782
1211
 
783
1212
  # aliases
784
1213
  alias alt_macaddress alt_mac_address
@@ -830,7 +1259,7 @@ module JSS
830
1259
  computer << purchasing_xml if has_purchasing?
831
1260
 
832
1261
  doc.to_s
833
- end
1262
+ end # rest_xml
834
1263
 
835
1264
  end # class Computer
836
1265