ruby-jss 0.6.4 → 0.6.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.
Potentially problematic release.
This version of ruby-jss might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/jss.rb +3 -0
- data/lib/jss/api_object.rb +27 -23
- data/lib/jss/api_object/account.rb +160 -0
- data/lib/jss/api_object/computer.rb +30 -12
- data/lib/jss/api_object/computer_invitation.rb +206 -0
- data/lib/jss/api_object/creatable.rb +7 -6
- data/lib/jss/api_object/distribution_point.rb +11 -6
- data/lib/jss/api_object/group.rb +13 -13
- data/lib/jss/api_object/osx_configuration_profile.rb +61 -59
- data/lib/jss/api_object/package.rb +41 -12
- data/lib/jss/api_object/restricted_software.rb +86 -0
- data/lib/jss/api_object/script.rb +1 -1
- data/lib/jss/api_object/self_servable.rb +55 -58
- data/lib/jss/api_object/updatable.rb +1 -1
- data/lib/jss/client.rb +1 -1
- data/lib/jss/version.rb +1 -1
- metadata +5 -2
@@ -86,11 +86,6 @@ module JSS
|
|
86
86
|
### The possible values for cpu_type (required_processor) in a JSS package
|
87
87
|
CPU_TYPES = ["None", "x86", "ppc"]
|
88
88
|
|
89
|
-
# TO DO - this is redundant with DEFAULT_PROCESSOR, but both are in use
|
90
|
-
# clean them up!
|
91
|
-
### which is default? there must be one to make a new pkg
|
92
|
-
DEFAULT_CPU_TYPE = "None"
|
93
|
-
|
94
89
|
### the possible priorities
|
95
90
|
PRIORITIES = (1..20)
|
96
91
|
|
@@ -190,7 +185,7 @@ module JSS
|
|
190
185
|
|
191
186
|
@priority = @init_data[:priority] || DEFAULT_PRIORITY
|
192
187
|
@reboot_required = @init_data[:reboot_required]
|
193
|
-
@required_processor = @init_data[:required_processor] ||
|
188
|
+
@required_processor = @init_data[:required_processor] || DEFAULT_PROCESSOR
|
194
189
|
@required_processor = nil if @required_processor.to_s.casecmp('none') == 0
|
195
190
|
@send_notification = @init_data[:send_notification]
|
196
191
|
@switch_with_package = @init_data[:switch_with_package] || DO_NOT_INSTALL
|
@@ -270,7 +265,7 @@ module JSS
|
|
270
265
|
new_val = nil if new_val == ''
|
271
266
|
new_val ||= @name
|
272
267
|
return nil if new_val == @filename
|
273
|
-
$stderr.puts "WARNING: you must
|
268
|
+
$stderr.puts "WARNING: you must change the filename on the master Distribution Point. See JSS::Package.update_master_filename." if @in_jss
|
274
269
|
@filename = new_val
|
275
270
|
@need_to_update = true
|
276
271
|
end
|
@@ -573,6 +568,39 @@ module JSS
|
|
573
568
|
end # upload
|
574
569
|
|
575
570
|
|
571
|
+
### Change the name of a package file on the master distribution point.
|
572
|
+
###
|
573
|
+
### @param new_file_name[String]
|
574
|
+
###
|
575
|
+
### @param old_file_name[default: @filename, String]
|
576
|
+
###
|
577
|
+
### @param unmount[Boolean] whether or not ot unount the distribution point when finished.
|
578
|
+
###
|
579
|
+
### @param rw_pw[String,Symbol] the password for the read/write account on the master Distribution Point,
|
580
|
+
### or :prompt, or :stdin# where # is the line of stdin containing the password See {JSS::DistributionPoint#mount}
|
581
|
+
###
|
582
|
+
### @return [nil]
|
583
|
+
###
|
584
|
+
def update_master_filename(old_file_name, new_file_name, rw_pw , unmount = true )
|
585
|
+
raise JSS::NoSuchItemError, "#{old_file_name} does not exist in the jss." unless @in_jss
|
586
|
+
mdp = JSS::DistributionPoint.master_distribution_point
|
587
|
+
pkgs_dir = mdp.mount(rw_pw, :rw) + "#{DIST_POINT_PKGS_FOLDER}"
|
588
|
+
old_file = pkgs_dir + old_file_name
|
589
|
+
new_file = pkgs_dir + new_file_name
|
590
|
+
if new_file.extname.empty?
|
591
|
+
### use the extension of the original file.
|
592
|
+
new_file = pkgs_dir + (new_file_name + old_file.extname)
|
593
|
+
end
|
594
|
+
if old_file.exist?
|
595
|
+
old_file.rename new_file
|
596
|
+
else
|
597
|
+
raise JSS::NoSuchItemError, "Original file not found on the master distribution point at #{DIST_POINT_PKGS_FOLDER}/#{old_file_name}."
|
598
|
+
end # if exist
|
599
|
+
mdp.unmount if unmount
|
600
|
+
return nil
|
601
|
+
end # update_master_filename
|
602
|
+
|
603
|
+
|
576
604
|
### Delete the filename from the master distribution point, if it exists.
|
577
605
|
###
|
578
606
|
### If you'll be uploading several files you can specify unmount as false, and do it manually when all
|
@@ -692,8 +720,8 @@ module JSS
|
|
692
720
|
raise JSS::MissingDataError, "No password provided for http download" unless ro_pw
|
693
721
|
raise JSS::InvaldDatatError, "Incorrect password for http access to distribution point." unless mdp.check_pw(:http, ro_pw)
|
694
722
|
# insert the name and pw into the uri
|
695
|
-
reserved_chars = Regexp.new("[^#{URI::REGEXP::PATTERN::UNRESERVED}]") # we'll escape all the chars that aren't unreserved
|
696
|
-
src_path = src_path.sub(%r{(https?://)(\S)}, "#{$1}#{
|
723
|
+
# reserved_chars = Regexp.new("[^#{URI::REGEXP::PATTERN::UNRESERVED}]") # we'll escape all the chars that aren't unreserved
|
724
|
+
src_path = src_path.sub(%r{(https?://)(\S)}, "#{$1}#{CGI.escape mdp.http_username}:#{CGI.escape ro_pw}@#{$2}")
|
697
725
|
end
|
698
726
|
|
699
727
|
# or with filesharing?
|
@@ -703,11 +731,12 @@ module JSS
|
|
703
731
|
end
|
704
732
|
|
705
733
|
# look at the pkgs folder
|
706
|
-
src_path += "#{DIST_POINT_PKGS_FOLDER}"
|
734
|
+
src_path += "#{DIST_POINT_PKGS_FOLDER}/"
|
707
735
|
end # if args[:alt_download_url]
|
708
736
|
|
709
|
-
|
710
|
-
|
737
|
+
if using_http
|
738
|
+
src_path += "#{@filename}" unless no_filename_in_url
|
739
|
+
end
|
711
740
|
|
712
741
|
### are we doing "fill existing users" or "fill user template"?
|
713
742
|
do_feu = args[:feu] ? "-feu" : ""
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module JSS
|
2
|
+
#####################################
|
3
|
+
### Classes
|
4
|
+
#####################################
|
5
|
+
|
6
|
+
###
|
7
|
+
### Restricted Software in the JSS.
|
8
|
+
###
|
9
|
+
### This class only supports showing of object data.
|
10
|
+
###
|
11
|
+
### @see JSS::APIObject
|
12
|
+
###
|
13
|
+
class RestrictedSoftware < JSS::APIObject
|
14
|
+
|
15
|
+
#####################################
|
16
|
+
### Mix-Ins
|
17
|
+
#####################################
|
18
|
+
|
19
|
+
include JSS::Scopable
|
20
|
+
|
21
|
+
#####################################
|
22
|
+
### Class Constants
|
23
|
+
#####################################
|
24
|
+
|
25
|
+
### The base for REST resources of this class
|
26
|
+
RSRC_BASE = "restrictedsoftware"
|
27
|
+
|
28
|
+
### the hash key used for the JSON list output of all objects in the JSS
|
29
|
+
RSRC_LIST_KEY = :restricted_software
|
30
|
+
|
31
|
+
### The hash key used for the JSON object output.
|
32
|
+
### It's also used in various error messages
|
33
|
+
RSRC_OBJECT_KEY = :restricted_software
|
34
|
+
|
35
|
+
### these keys, as well as :id and :name, are present in valid API JSON data for this class
|
36
|
+
VALID_DATA_KEYS = [:scope]
|
37
|
+
|
38
|
+
### Our scopes deal with computers
|
39
|
+
SCOPE_TARGET_KEY = :computers
|
40
|
+
|
41
|
+
#####################################
|
42
|
+
### Attributes
|
43
|
+
#####################################
|
44
|
+
|
45
|
+
### The values returned in the General, Location, and Purchasing subsets are stored as direct attributes
|
46
|
+
### Location and Purchasing are defined in the Locatable and Purchasable mixin modules.
|
47
|
+
### Here's General, in alphabetical order
|
48
|
+
|
49
|
+
### @return [String] the process name
|
50
|
+
attr_reader :process_name
|
51
|
+
|
52
|
+
### @return [Boolean] whether to return match exact process name
|
53
|
+
attr_reader :match_exact_process_name
|
54
|
+
|
55
|
+
### @return [Boolean] whether to send a notification
|
56
|
+
attr_reader :send_notification
|
57
|
+
|
58
|
+
### @return [Boolean] whether to kill the running process
|
59
|
+
attr_reader :kill_process
|
60
|
+
|
61
|
+
### @return [Boolean] whether to delete the executable
|
62
|
+
attr_reader :delete_executable
|
63
|
+
|
64
|
+
### @return [String] message displayed to the user
|
65
|
+
attr_reader :display_message
|
66
|
+
|
67
|
+
### @return [Hash] the :name and :id of the site for this machine
|
68
|
+
attr_reader :site
|
69
|
+
|
70
|
+
#####################################
|
71
|
+
### Instance Methods
|
72
|
+
#####################################
|
73
|
+
|
74
|
+
def initialize(args = {})
|
75
|
+
super args, []
|
76
|
+
|
77
|
+
@process_name = @init_data[:general][:process_name]
|
78
|
+
@match_exact_process_name = @init_data[:general][:match_exact_process_name]
|
79
|
+
@send_notification = @init_data[:general][:send_notification]
|
80
|
+
@kill_process = @init_data[:general][:kill_process]
|
81
|
+
@delete_executable = @init_data[:general][:delete_executable]
|
82
|
+
@display_message = @init_data[:general][:display_message]
|
83
|
+
@site = JSS::APIObject.get_name(@init_data[:general][:site])
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -193,7 +193,7 @@ module JSS
|
|
193
193
|
@name = new_val
|
194
194
|
|
195
195
|
### if our REST resource is based on the name, update that too
|
196
|
-
@rest_rsrc = "#{RSRC_BASE}/name/#{
|
196
|
+
@rest_rsrc = "#{RSRC_BASE}/name/#{CGI.escape @name}" if @rest_rsrc.include? '/name/'
|
197
197
|
@need_to_update = true
|
198
198
|
end #name=
|
199
199
|
|
@@ -1,25 +1,25 @@
|
|
1
1
|
### Copyright 2016 Pixar
|
2
|
-
###
|
2
|
+
###
|
3
3
|
### Licensed under the Apache License, Version 2.0 (the "Apache License")
|
4
4
|
### with the following modification; you may not use this file except in
|
5
5
|
### compliance with the Apache License and the following modification to it:
|
6
6
|
### Section 6. Trademarks. is deleted and replaced with:
|
7
|
-
###
|
7
|
+
###
|
8
8
|
### 6. Trademarks. This License does not grant permission to use the trade
|
9
9
|
### names, trademarks, service marks, or product names of the Licensor
|
10
10
|
### and its affiliates, except as required to comply with Section 4(c) of
|
11
11
|
### the License and to reproduce the content of the NOTICE file.
|
12
|
-
###
|
12
|
+
###
|
13
13
|
### You may obtain a copy of the Apache License at
|
14
|
-
###
|
14
|
+
###
|
15
15
|
### http://www.apache.org/licenses/LICENSE-2.0
|
16
|
-
###
|
16
|
+
###
|
17
17
|
### Unless required by applicable law or agreed to in writing, software
|
18
18
|
### distributed under the Apache License with the above modification is
|
19
19
|
### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
20
20
|
### KIND, either express or implied. See the Apache License for the specific
|
21
21
|
### language governing permissions and limitations under the Apache License.
|
22
|
-
###
|
22
|
+
###
|
23
23
|
###
|
24
24
|
|
25
25
|
###
|
@@ -41,19 +41,19 @@ module JSS
|
|
41
41
|
###
|
42
42
|
### The JSS objects that have Self Service data return it in a :self_service subset,
|
43
43
|
### which all have similar data, a hash with at least these keys:
|
44
|
-
### - :self_service_description
|
45
|
-
### - :self_service_icon
|
44
|
+
### - :self_service_description
|
45
|
+
### - :self_service_icon
|
46
46
|
###
|
47
47
|
### Most also have:
|
48
48
|
### - :feature_on_main_page
|
49
49
|
### - :self_service_categories
|
50
50
|
###
|
51
|
-
### iOS Profiles in self service have this key:
|
52
|
-
### - :security
|
51
|
+
### iOS Profiles in self service have this key:
|
52
|
+
### - :security
|
53
53
|
###
|
54
54
|
### Additionally, items that apper in OS X SlfSvc have these keys:
|
55
55
|
### - :install_button_text
|
56
|
-
### - :force_users_to_view_description
|
56
|
+
### - :force_users_to_view_description
|
57
57
|
###
|
58
58
|
### See the attribute definitions for details of these values and structures.
|
59
59
|
###
|
@@ -71,14 +71,14 @@ module JSS
|
|
71
71
|
### - Define the constant SELF_SERVICE_PAYLOAD which contains one of :policy, :profile, or :app
|
72
72
|
### - Call {#parse_self_service} in the subclass's constructor after calling super
|
73
73
|
### - Include the result of {#self_service_xml} in their #rest_xml output
|
74
|
-
### - Define the method #in_self_service? which returns a Boolean indicating that the item is
|
74
|
+
### - Define the method #in_self_service? which returns a Boolean indicating that the item is
|
75
75
|
### available in self service. Different API objects indicate this in different ways.
|
76
76
|
### - Define the method #user_removable? which returns Boolean indicating that the item (a profile)
|
77
77
|
### can be removed by the user in SSvc. OS X profiles store this in the :user_removable key of the
|
78
78
|
### :general subset as a boolean, whereas iOS profiles stor it in :security as one of 3 strings
|
79
79
|
###
|
80
80
|
###
|
81
|
-
### Notes:
|
81
|
+
### Notes:
|
82
82
|
### - Self service icons cannot be modified via this code. Use the Web UI.
|
83
83
|
### - There an API bug in handling categories, and all but the last one are ommitted. Until this is fixed, categories
|
84
84
|
### cannot be saved via this code since that would cause data-loss when more than one category is applied.
|
@@ -90,19 +90,19 @@ module JSS
|
|
90
90
|
#####################################
|
91
91
|
|
92
92
|
SELF_SERVABLE = true
|
93
|
-
|
93
|
+
|
94
94
|
IOS_PROFILE_REMOVAL_OPTIONS = ["Always", "With Authorization", "Never"]
|
95
|
-
|
95
|
+
|
96
96
|
#####################################
|
97
97
|
### Variables
|
98
98
|
#####################################
|
99
|
-
|
100
|
-
|
99
|
+
|
100
|
+
|
101
101
|
#####################################
|
102
102
|
### Attribtues
|
103
103
|
#####################################
|
104
104
|
|
105
|
-
|
105
|
+
|
106
106
|
### @return [String] The verbage that appears in SelfSvc for this item
|
107
107
|
attr_reader :self_service_description
|
108
108
|
|
@@ -127,7 +127,7 @@ module JSS
|
|
127
127
|
### - :display_in => [Boolean] should the item be displayed in this category in SSvc? (OSX SSvc only)
|
128
128
|
### - :feature_in => [Boolean] should the item be featured in this category in SSVC? (OSX SSvc only)
|
129
129
|
###
|
130
|
-
### NOTE: as of Casper 9.61 there's a bug in the JSON output from the API, and only the last
|
130
|
+
### NOTE: as of Casper 9.61 there's a bug in the JSON output from the API, and only the last
|
131
131
|
### category is returned, if more than one are set.
|
132
132
|
###
|
133
133
|
attr_reader :self_service_categories
|
@@ -145,7 +145,7 @@ module JSS
|
|
145
145
|
|
146
146
|
### @return [String] The text label on the install button in SSvc (OSX SSvc only)
|
147
147
|
attr_reader :self_service_install_button_text
|
148
|
-
|
148
|
+
|
149
149
|
### @return [Boolean] Should an extra window appear before the user can install the item? (OSX SSvc only)
|
150
150
|
attr_reader :self_service_force_users_to_view_description
|
151
151
|
|
@@ -165,32 +165,29 @@ module JSS
|
|
165
165
|
def parse_self_service
|
166
166
|
@init_data[:self_service] ||= {}
|
167
167
|
ss_data = @init_data[:self_service]
|
168
|
-
|
168
|
+
|
169
169
|
@self_service_description = ss_data[:self_service_description]
|
170
170
|
@self_service_icon = ss_data[:self_service_icon]
|
171
|
-
|
171
|
+
|
172
172
|
@self_service_feature_on_main_page = ss_data[:feature_on_main_page]
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
ss_data[:self_service_categories][:category]
|
177
|
-
]
|
178
|
-
|
173
|
+
|
174
|
+
@self_service_categories = ss_data[:self_service_categories]
|
175
|
+
|
179
176
|
# make this an empty hash if needed
|
180
177
|
@self_service_security = ss_data[:security] || {}
|
181
|
-
|
182
|
-
# if this is an osx profile, set @self_service_security[:removal_disallowed] to "Always" or "Never"
|
178
|
+
|
179
|
+
# if this is an osx profile, set @self_service_security[:removal_disallowed] to "Always" or "Never"
|
183
180
|
# to indicate the boolean :user_removable
|
184
181
|
if @init_data[:general].keys.include? :user_removable
|
185
182
|
@self_service_security[:removal_disallowed] = @init_data[:general][:user_removable] ? "Always" : "Never"
|
186
183
|
end
|
187
|
-
|
184
|
+
|
188
185
|
@self_service_install_button_text = ss_data[:install_button_text]
|
189
186
|
@self_service_force_users_to_view_description = ss_data[:force_users_to_view_description]
|
190
187
|
|
191
188
|
end
|
192
189
|
|
193
|
-
|
190
|
+
|
194
191
|
###
|
195
192
|
###
|
196
193
|
### Setters
|
@@ -218,9 +215,9 @@ module JSS
|
|
218
215
|
@self_service_install_button_text = new_val.strip
|
219
216
|
@need_to_update = true
|
220
217
|
end
|
221
|
-
|
218
|
+
|
222
219
|
###
|
223
|
-
### @param new_val[Boolean] should this appear on the main SelfSvc page?
|
220
|
+
### @param new_val[Boolean] should this appear on the main SelfSvc page?
|
224
221
|
###
|
225
222
|
### @return [void]
|
226
223
|
###
|
@@ -230,9 +227,9 @@ module JSS
|
|
230
227
|
@self_service_feature_on_main_page = new_val
|
231
228
|
@need_to_update = true
|
232
229
|
end
|
233
|
-
|
230
|
+
|
234
231
|
###
|
235
|
-
### @param new_val[Boolean] should this appear on the main SelfSvc page?
|
232
|
+
### @param new_val[Boolean] should this appear on the main SelfSvc page?
|
236
233
|
###
|
237
234
|
### @return [void]
|
238
235
|
###
|
@@ -243,7 +240,7 @@ module JSS
|
|
243
240
|
@self_service_force_users_to_view_description = new_val
|
244
241
|
@need_to_update = true
|
245
242
|
end
|
246
|
-
|
243
|
+
|
247
244
|
###
|
248
245
|
### Add or change one of the categories for this item in SSvc.
|
249
246
|
###
|
@@ -260,21 +257,21 @@ module JSS
|
|
260
257
|
raise JSS::NoSuchItemError, "No category '#{new_cat}' in the JSS" unless JSS::Category.all_names(:refresh).include? new_cat
|
261
258
|
raise JSS::InvalidDataError, "display_in must be true or false" unless JSS::TRUE_FALSE.include? display_in
|
262
259
|
raise JSS::InvalidDataError, "feature_in must be true or false" unless JSS::TRUE_FALSE.include? feature_in
|
263
|
-
|
260
|
+
|
264
261
|
new_data = {:name => new_cat, :display_in => display_in, :feature_in => feature_in }
|
265
|
-
|
262
|
+
|
266
263
|
# see if this category is already among our categories.
|
267
264
|
idx = @self_service_categories.index{|c| c[new_cat]}
|
268
|
-
|
269
|
-
if idx
|
265
|
+
|
266
|
+
if idx
|
270
267
|
@self_service_categories[idx] = new_data
|
271
268
|
else
|
272
269
|
@self_service_categories << new_data
|
273
270
|
end
|
274
|
-
|
271
|
+
|
275
272
|
@need_to_update = true
|
276
273
|
end
|
277
|
-
|
274
|
+
|
278
275
|
###
|
279
276
|
### Remove a category from those for this item in SSvc
|
280
277
|
###
|
@@ -296,17 +293,17 @@ module JSS
|
|
296
293
|
### @return [void]
|
297
294
|
###
|
298
295
|
def profile_can_be_removed (new_val)
|
299
|
-
|
300
|
-
new_val = "Always" if new_val === true
|
296
|
+
|
297
|
+
new_val = "Always" if new_val === true
|
301
298
|
new_val = "Never" if new_val === false
|
302
|
-
|
299
|
+
|
303
300
|
return nil if new_val == @self_service_security[:removal_disallowed]
|
304
301
|
raise JSS::InvalidDataError, "" unless IOS_PROFILE_REMOVAL_OPTIONS.include? new_val
|
305
|
-
|
302
|
+
|
306
303
|
@self_service_security[:removal_disallowed] = new_val
|
307
304
|
end
|
308
|
-
|
309
|
-
|
305
|
+
|
306
|
+
|
310
307
|
###
|
311
308
|
### @api private
|
312
309
|
###
|
@@ -317,16 +314,16 @@ module JSS
|
|
317
314
|
### @return [REXML::Element]
|
318
315
|
###
|
319
316
|
def self_service_xml
|
320
|
-
|
317
|
+
|
321
318
|
ssvc = REXML::Element.new('self_service')
|
322
|
-
|
319
|
+
|
323
320
|
return ssvc unless self.in_self_service?
|
324
|
-
|
321
|
+
|
325
322
|
ssvc.add_element('self_service_description').text = @self_service_description
|
326
323
|
ssvc.add_element('feature_on_main_page').text = @self_service_feature_on_main_page
|
327
|
-
|
324
|
+
|
328
325
|
### TEMPORARY - re-enable this when the category bug is fixed.
|
329
|
-
|
326
|
+
|
330
327
|
# cats = ssvc.add_element('self_service_categories')
|
331
328
|
# @self_service_categories.each do |cat|
|
332
329
|
# catelem = cats.add_element('category')
|
@@ -334,22 +331,22 @@ module JSS
|
|
334
331
|
# catelem.add_element('display_in').text = cat[:display_in] if cat.keys.include? :display_in
|
335
332
|
# catelem.add_element('feature_in').text = cat[:feature_in] if cat.keys.include? :feature_in
|
336
333
|
# end
|
337
|
-
|
334
|
+
|
338
335
|
unless @self_service_security.empty?
|
339
336
|
sec = ssvc.add_element('security')
|
340
337
|
sec.add_element('removal_disallowed').text = @self_service_security[:removal_disallowed] if @self_service_security[:removal_disallowed]
|
341
338
|
sec.add_element('password').text = @self_service_security[:password] if @self_service_security[:password]
|
342
339
|
end
|
343
|
-
|
340
|
+
|
344
341
|
ssvc.add_element('install_button_text').text = @self_service_install_button_text if @self_service_install_button_text
|
345
342
|
ssvc.add_element('force_users_to_view_description').text = @self_service_force_users_to_view_description unless @self_service_force_users_to_view_description.nil?
|
346
343
|
|
347
344
|
return ssvc
|
348
345
|
end
|
349
|
-
|
346
|
+
|
350
347
|
### aliases
|
351
348
|
alias change_self_service_category add_self_service_category
|
352
|
-
|
349
|
+
|
353
350
|
end # module SelfServable
|
354
351
|
|
355
352
|
end # module JSS
|