xolo-server 1.0.1 → 2.0.2
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.
- checksums.yaml +4 -4
- data/data/client/xolo +152 -79
- data/lib/xolo/core/base_classes/title.rb +254 -18
- data/lib/xolo/core/base_classes/version.rb +47 -7
- data/lib/xolo/core/constants.rb +7 -3
- data/lib/xolo/core/security_cmd.rb +128 -0
- data/lib/xolo/core/version.rb +1 -1
- data/lib/xolo/core.rb +1 -0
- data/lib/xolo/server/app.rb +7 -0
- data/lib/xolo/server/configuration.rb +243 -37
- data/lib/xolo/server/constants.rb +10 -0
- data/lib/xolo/server/helpers/auth.rb +19 -2
- data/lib/xolo/server/helpers/autopkg.rb +157 -0
- data/lib/xolo/server/helpers/client_data.rb +90 -60
- data/lib/xolo/server/helpers/file_transfers.rb +412 -82
- data/lib/xolo/server/helpers/jamf_pro.rb +30 -7
- data/lib/xolo/server/helpers/log.rb +2 -0
- data/lib/xolo/server/helpers/maintenance.rb +1 -0
- data/lib/xolo/server/helpers/notification.rb +4 -3
- data/lib/xolo/server/helpers/pkg_signing.rb +16 -12
- data/lib/xolo/server/helpers/progress_streaming.rb +9 -12
- data/lib/xolo/server/helpers/subscriptions.rb +119 -0
- data/lib/xolo/server/helpers/titles.rb +27 -3
- data/lib/xolo/server/helpers/versions.rb +23 -11
- data/lib/xolo/server/mixins/changelog.rb +9 -16
- data/lib/xolo/server/mixins/title_jamf_access.rb +375 -385
- data/lib/xolo/server/mixins/title_ted_access.rb +29 -3
- data/lib/xolo/server/mixins/version_jamf_access.rb +95 -112
- data/lib/xolo/server/mixins/version_ted_access.rb +25 -0
- data/lib/xolo/server/object_locks.rb +2 -1
- data/lib/xolo/server/routes/auth.rb +2 -2
- data/lib/xolo/server/routes/jamf_pro.rb +11 -1
- data/lib/xolo/server/routes/maint.rb +2 -1
- data/lib/xolo/server/routes/subscriptions.rb +126 -0
- data/lib/xolo/server/routes/title_editor.rb +1 -1
- data/lib/xolo/server/routes/titles.rb +26 -11
- data/lib/xolo/server/routes/uploads.rb +0 -14
- data/lib/xolo/server/routes/versions.rb +14 -13
- data/lib/xolo/server/routes.rb +9 -0
- data/lib/xolo/server/title.rb +100 -77
- data/lib/xolo/server/version.rb +177 -15
- data/lib/xolo/server.rb +8 -0
- metadata +7 -9
|
@@ -27,7 +27,7 @@ module Xolo
|
|
|
27
27
|
|
|
28
28
|
DFT_EMAIL_FROM = 'xolo-server-do-not-reply'
|
|
29
29
|
|
|
30
|
-
ALERT_TOOL_EMAIL_PREFIX = '
|
|
30
|
+
ALERT_TOOL_EMAIL_PREFIX = 'mailto:'
|
|
31
31
|
|
|
32
32
|
# Module Methods
|
|
33
33
|
#######################
|
|
@@ -45,7 +45,8 @@ module Xolo
|
|
|
45
45
|
# Send a message thru the alert_tool, if one is defined in the config.
|
|
46
46
|
#
|
|
47
47
|
# Messages are prepended with "#{level} ALERT: "
|
|
48
|
-
#
|
|
48
|
+
#
|
|
49
|
+
# This should be called by passing 'alert: true' to one of the
|
|
49
50
|
# logging wrapper methods
|
|
50
51
|
#
|
|
51
52
|
# @param msg [String] the message to send
|
|
@@ -136,7 +137,7 @@ module Xolo
|
|
|
136
137
|
@server_fqdn ||= Addrinfo.getaddrinfo(Socket.gethostname, nil).first.getnameinfo.first
|
|
137
138
|
end
|
|
138
139
|
|
|
139
|
-
end #
|
|
140
|
+
end # Notification
|
|
140
141
|
|
|
141
142
|
end # Helpers
|
|
142
143
|
|
|
@@ -44,21 +44,25 @@ module Xolo
|
|
|
44
44
|
##############################
|
|
45
45
|
|
|
46
46
|
# do we need to sign a pkg?
|
|
47
|
-
# TODO: sign zipped bundle installers? prob not, they shouldn't be used anymore
|
|
48
|
-
# (I'm looking at YOU Adobe)
|
|
49
47
|
# @param pkg [Pathname] Path to a .pkg to see if it's signed.
|
|
48
|
+
# @param version [Xolo::Server::Version] the version that is being uploaded/re-uploaded
|
|
50
49
|
# @return [Boolean] should we sign it?
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
#############################
|
|
51
|
+
def need_to_sign?(pkg, version)
|
|
52
|
+
log_debug "Checking need to sign uploaded pkg '#{pkg}' for version '#{version.version}' of title '#{version.title}'"
|
|
53
|
+
|
|
54
|
+
# if an autopkg pkg and we are not configured to sign autopkg pkgs, then no need to sign
|
|
55
|
+
if version.pkg_is_from_autopkg && !Xolo::Server.config.sign_autopkg_pkgs
|
|
56
|
+
log_debug "No need to sign '#{pkg.basename}': version '#{version.version}' of title '#{version.title}' is from autopkg and server is not configured to sign autopkg pkgs."
|
|
55
57
|
return false
|
|
56
|
-
|
|
57
|
-
if pkg
|
|
58
|
-
|
|
58
|
+
|
|
59
|
+
# if not an autopkg pkg and we are not configured to sign non-autopkg pkgs, then no need to sign
|
|
60
|
+
elsif !version.pkg_is_from_autopkg && !Xolo::Server.config.sign_pkgs
|
|
61
|
+
log_debug "No need to sign '#{pkg.basename}': server is not configured to sign pkgs."
|
|
59
62
|
return false
|
|
60
63
|
end
|
|
61
64
|
|
|
65
|
+
# is the pkg already signed?
|
|
62
66
|
!pkg_signed?(pkg)
|
|
63
67
|
end
|
|
64
68
|
|
|
@@ -83,7 +87,7 @@ module Xolo
|
|
|
83
87
|
#
|
|
84
88
|
# @return [void]
|
|
85
89
|
#######################################################
|
|
86
|
-
def
|
|
90
|
+
def sign_pkg(unsigned_pkg, signed_pkg)
|
|
87
91
|
unlock_signing_keychain
|
|
88
92
|
|
|
89
93
|
sh_unsigned = Shellwords.escape unsigned_pkg.to_s
|
|
@@ -99,7 +103,7 @@ module Xolo
|
|
|
99
103
|
|
|
100
104
|
msg = "Failed to sign uploaded pkg: #{stdouterr}"
|
|
101
105
|
log_error msg
|
|
102
|
-
|
|
106
|
+
raise Xolo::Core::Exceptions::ServerError msg
|
|
103
107
|
end
|
|
104
108
|
|
|
105
109
|
# unlock the pkg signing keychain
|
|
@@ -129,7 +133,7 @@ module Xolo
|
|
|
129
133
|
|
|
130
134
|
msg = "Error unlocking signing keychain: #{outerrs}"
|
|
131
135
|
log_error msg
|
|
132
|
-
|
|
136
|
+
raise Xolo::Core::Exceptions::ServerError, msg
|
|
133
137
|
end
|
|
134
138
|
|
|
135
139
|
end # JamfPro
|
|
@@ -72,7 +72,7 @@ module Xolo
|
|
|
72
72
|
log_debug 'Thread with_streaming is starting and yielding to block'
|
|
73
73
|
yield
|
|
74
74
|
log_debug 'Thread with_streaming is finished'
|
|
75
|
-
rescue
|
|
75
|
+
rescue => e
|
|
76
76
|
progress "ERROR: #{e.class}: #{e}", log: :error
|
|
77
77
|
e.backtrace.each { |l| log_debug "..#{l}" }
|
|
78
78
|
ensure
|
|
@@ -141,29 +141,26 @@ module Xolo
|
|
|
141
141
|
#
|
|
142
142
|
# @return [void]
|
|
143
143
|
###################
|
|
144
|
-
def progress(msg, log: :nil)
|
|
144
|
+
def progress(msg, log: :nil, alert: false)
|
|
145
145
|
# log_debug "Progress method called from #{caller_locations.first}"
|
|
146
146
|
|
|
147
147
|
progress_stream_file.pix_append "#{msg.chomp}\n"
|
|
148
148
|
|
|
149
|
-
unless log
|
|
150
|
-
# log_debug 'Processed unlogged progress message'
|
|
151
|
-
return
|
|
152
|
-
end
|
|
149
|
+
return unless log
|
|
153
150
|
|
|
154
151
|
case log
|
|
155
152
|
when :debug
|
|
156
|
-
log_debug msg
|
|
153
|
+
log_debug msg, alert: alert
|
|
157
154
|
when :info
|
|
158
|
-
log_info msg
|
|
155
|
+
log_info msg, alert: alert
|
|
159
156
|
when :warn
|
|
160
|
-
log_warn msg
|
|
157
|
+
log_warn msg, alert: alert
|
|
161
158
|
when :error
|
|
162
|
-
log_error msg
|
|
159
|
+
log_error msg, alert: alert
|
|
163
160
|
when :fatal
|
|
164
|
-
log_fatal msg
|
|
161
|
+
log_fatal msg, alert: alert
|
|
165
162
|
when :unknown
|
|
166
|
-
log_unknown msg
|
|
163
|
+
log_unknown msg, alert: alert
|
|
167
164
|
end
|
|
168
165
|
end
|
|
169
166
|
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Copyright 2025 Pixar
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the terms set forth in the LICENSE.txt file available at
|
|
4
|
+
# at the root of this project.
|
|
5
|
+
#
|
|
6
|
+
#
|
|
7
|
+
|
|
8
|
+
# frozen_string_literal: true
|
|
9
|
+
|
|
10
|
+
# main module
|
|
11
|
+
module Xolo
|
|
12
|
+
|
|
13
|
+
module Server
|
|
14
|
+
|
|
15
|
+
module Helpers
|
|
16
|
+
|
|
17
|
+
# This is mixed in to Xolo::Server::App (as a helper, available in route processing)
|
|
18
|
+
#
|
|
19
|
+
# This holds methods and constants for working with subscribed titles - those
|
|
20
|
+
# that are managed by other Non-Xolo Patch Sources.
|
|
21
|
+
#
|
|
22
|
+
module Subscriptions
|
|
23
|
+
|
|
24
|
+
# Constants
|
|
25
|
+
#####################
|
|
26
|
+
#####################
|
|
27
|
+
|
|
28
|
+
# Module Methods
|
|
29
|
+
#######################
|
|
30
|
+
#######################
|
|
31
|
+
|
|
32
|
+
# when this module is included
|
|
33
|
+
def self.included(includer)
|
|
34
|
+
Xolo.verbose_include includer, self
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Instance Methods
|
|
38
|
+
#######################
|
|
39
|
+
######################
|
|
40
|
+
|
|
41
|
+
# All available (i.e. not yet subscribed) titles on all patch sources defined in Jamf.
|
|
42
|
+
# @return [Array<Hash>] the available titles and their sources
|
|
43
|
+
#####################################
|
|
44
|
+
def available_titles_for_subscription
|
|
45
|
+
available = []
|
|
46
|
+
|
|
47
|
+
Jamf::PatchSource.all(cnx: jamf_cnx).each do |ps|
|
|
48
|
+
log_debug "Checking Patch Source #{ps} for available titles"
|
|
49
|
+
ps = Jamf::PatchSource.fetch id: ps[:id], cnx: jamf_cnx
|
|
50
|
+
ps.available_titles.each do |t|
|
|
51
|
+
data = t.merge({ source_id: ps.id, source_name: ps.name })
|
|
52
|
+
available << data
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
available
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Process an incoming webhook event, possibly for a subscribed title
|
|
60
|
+
# Do this in a thread so that we can return a 200 response to the webhook immediately,
|
|
61
|
+
# and do the processing asynchronously (which may involve time-consuming tasks like autopkg runs)
|
|
62
|
+
#################################
|
|
63
|
+
def process_patch_title_updated_webhook(req_body)
|
|
64
|
+
@process_webhook_thread = Thread.new do
|
|
65
|
+
log_debug "Using a thread for processing PatchSoftwareTitleUpdated webhook event with body: #{req_body}"
|
|
66
|
+
|
|
67
|
+
event_data = parse_json(req_body)[:event]
|
|
68
|
+
|
|
69
|
+
title_name = event_data[:name]
|
|
70
|
+
title_id = event_data[:jssID]
|
|
71
|
+
new_version = event_data[:latestVersion]
|
|
72
|
+
|
|
73
|
+
log_debug "Received PatchSoftwareTitleUpdate webhook event for patch title '#{title_name}' (jamf id #{title_id}), new version '#{new_version}'"
|
|
74
|
+
|
|
75
|
+
subscribed_title = subscribed_title_objects.select { |tobj| tobj.jamf_patch_title_id.to_i == title_id.to_i }.first
|
|
76
|
+
|
|
77
|
+
if subscribed_title
|
|
78
|
+
msg = +"New version '#{new_version}' is available for subscribed title '#{subscribed_title.title}' (#{subscribed_title.display_name})."
|
|
79
|
+
|
|
80
|
+
msg << " Running autopkg recipe '#{subscribed_title.autopkg_recipe}'." if subscribed_title.autopkg_enabled?
|
|
81
|
+
|
|
82
|
+
log_info msg
|
|
83
|
+
|
|
84
|
+
Xolo::Server::Version.add_version_via_subscription(
|
|
85
|
+
title_object: subscribed_title,
|
|
86
|
+
new_version: new_version
|
|
87
|
+
)
|
|
88
|
+
else
|
|
89
|
+
log_debug "Title '#{title_name}' ID #{title_id} is not a subscribed title in Xolo. Ignoring webhook."
|
|
90
|
+
end
|
|
91
|
+
rescue => e
|
|
92
|
+
msg = "Error processing PatchSoftwareTitleUpdated webhook event: #{e.class}: #{e}"
|
|
93
|
+
log_error msg
|
|
94
|
+
raise e, msg
|
|
95
|
+
end # thread
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# TODO: Delete this method after confirming we don't want it
|
|
99
|
+
# It wasn't in use when it was commented out.
|
|
100
|
+
#
|
|
101
|
+
# Subscribe to a title on a given patch source.
|
|
102
|
+
# @param source_id [Integer] the id or name of the patch source in Jamf
|
|
103
|
+
# @param name_id [Integer] the name_id of the title on that patch source
|
|
104
|
+
# @param display_name [String] an display name for the title
|
|
105
|
+
# @return [Integer] the id of the new subscribed title, or false on failure
|
|
106
|
+
#####################################
|
|
107
|
+
# def subscribe_to_title(source_id:, name_id:, display_name:)
|
|
108
|
+
# new_sub = Jamf::PatchTitle.create name: display_name, source_id: source_id, name_id: name_id, cnx: jamf_cnx
|
|
109
|
+
|
|
110
|
+
# new_sub.save
|
|
111
|
+
# end
|
|
112
|
+
|
|
113
|
+
end # Subscriptions
|
|
114
|
+
|
|
115
|
+
end # Helpers
|
|
116
|
+
|
|
117
|
+
end # Server
|
|
118
|
+
|
|
119
|
+
end # module Xolo
|
|
@@ -40,8 +40,32 @@ module Xolo
|
|
|
40
40
|
# A an array of all server titles as Title objects
|
|
41
41
|
# @return [Array<Xolo::Server::Title>]
|
|
42
42
|
############
|
|
43
|
-
def all_title_objects
|
|
44
|
-
|
|
43
|
+
def all_title_objects(refresh: false)
|
|
44
|
+
@all_title_objects = nil if refresh
|
|
45
|
+
return @all_title_objects if @all_title_objects
|
|
46
|
+
|
|
47
|
+
log_debug 'Instantiating all titles...'
|
|
48
|
+
@all_title_objects = all_titles.map { |t| instantiate_title t }
|
|
49
|
+
log_debug "Instantiated #{@all_title_objects.length} titles."
|
|
50
|
+
@all_title_objects
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# A an array of server titles as Title objects, only for subscribed titles
|
|
54
|
+
# @return [Array<Xolo::Server::Title>]
|
|
55
|
+
def subscribed_title_objects(refresh: false)
|
|
56
|
+
@subscribed_title_objects = nil if refresh
|
|
57
|
+
return @subscribed_title_objects if @subscribed_title_objects
|
|
58
|
+
|
|
59
|
+
@subscribed_title_objects = all_title_objects(refresh: refresh).select { |t| t.subscribed? }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# A an array of server titles as Title objects, only for managed titles
|
|
63
|
+
# @return [Array<Xolo::Server::Title>]
|
|
64
|
+
def managed_title_objects(refresh: false)
|
|
65
|
+
@managed_title_objects = nil if refresh
|
|
66
|
+
return @managed_title_objects if @managed_title_objects
|
|
67
|
+
|
|
68
|
+
@managed_title_objects = all_title_objects(refresh: refresh).select { |t| t.managed? }
|
|
45
69
|
end
|
|
46
70
|
|
|
47
71
|
# Instantiate a Server::Title with access to the Sinatra app instance,
|
|
@@ -68,7 +92,7 @@ module Xolo
|
|
|
68
92
|
Xolo::Server::Title.load data
|
|
69
93
|
|
|
70
94
|
else
|
|
71
|
-
msg =
|
|
95
|
+
msg = "Invalid data to instantiate a Xolo::Server::Title: #{data.class}:#{data} "
|
|
72
96
|
log_error msg
|
|
73
97
|
|
|
74
98
|
halt 400, { status: 400, error: msg }
|
|
@@ -53,7 +53,7 @@ module Xolo
|
|
|
53
53
|
# A list of all known versions of a title
|
|
54
54
|
# @return [Array<Xolo::Server::Version>]
|
|
55
55
|
############
|
|
56
|
-
def
|
|
56
|
+
def all_version_objects(title)
|
|
57
57
|
all_versions(title).map { |v| instantiate_version title: title, version: v }
|
|
58
58
|
end
|
|
59
59
|
|
|
@@ -69,24 +69,33 @@ module Xolo
|
|
|
69
69
|
# to use for access from the version object to the Sinatra App instance
|
|
70
70
|
# for the session and api connection objects
|
|
71
71
|
#
|
|
72
|
-
# @param data [Hash] hash to use with .new
|
|
73
72
|
# @param title [String, Xolo::Server::Title] title to use with .load
|
|
74
73
|
# @param version [String] version to use with .load
|
|
74
|
+
# @param data [Hash] hash to use with .new
|
|
75
75
|
#
|
|
76
76
|
# @return [Xolo::Server::Version]
|
|
77
77
|
#################
|
|
78
|
-
def instantiate_version(
|
|
79
|
-
|
|
78
|
+
def instantiate_version(title: nil, version: nil, **data)
|
|
79
|
+
log_debug "Instantiating version with title: '#{title}' (#{title.class}), version: '#{version},' data: #{data}"
|
|
80
80
|
|
|
81
|
-
if
|
|
82
|
-
title = data[:title]
|
|
83
|
-
elsif title.is_a?(Xolo::Server::Title)
|
|
81
|
+
if title.is_a?(Xolo::Server::Title)
|
|
84
82
|
title_obj = title
|
|
85
83
|
title = title_obj.title
|
|
84
|
+
elsif data[:title]
|
|
85
|
+
title = data[:title]
|
|
86
|
+
title_obj = instantiate_title(title)
|
|
87
|
+
elsif title
|
|
88
|
+
title_obj = instantiate_title(title)
|
|
89
|
+
else
|
|
90
|
+
halt 400, { status: 400, error: 'Missing title to instantiate a Xolo::Server::Version' }
|
|
91
|
+
log_error 'Missing title to instantiate a Xolo::Server::Version'
|
|
86
92
|
end
|
|
87
93
|
|
|
88
94
|
vers =
|
|
89
|
-
if data.
|
|
95
|
+
if !data.empty?
|
|
96
|
+
# ensure the title and version are in the data
|
|
97
|
+
data[:title] ||= title
|
|
98
|
+
data[:version] ||= version
|
|
90
99
|
Xolo::Server::Version.new data
|
|
91
100
|
|
|
92
101
|
elsif title && version
|
|
@@ -95,12 +104,12 @@ module Xolo
|
|
|
95
104
|
|
|
96
105
|
Xolo::Server::Version.load title, version
|
|
97
106
|
else
|
|
98
|
-
msg =
|
|
107
|
+
msg = "Invalid title, version, or data to instantiate a Xolo::Server::Version: #{data.class}:#{data} "
|
|
99
108
|
log_error msg
|
|
100
109
|
halt 400, { status: 400, error: msg }
|
|
101
110
|
end
|
|
102
111
|
|
|
103
|
-
vers.title_object = title_obj
|
|
112
|
+
vers.title_object = title_obj
|
|
104
113
|
vers.server_app_instance = self
|
|
105
114
|
vers
|
|
106
115
|
end
|
|
@@ -110,6 +119,7 @@ module Xolo
|
|
|
110
119
|
# @return [void]
|
|
111
120
|
##################
|
|
112
121
|
def halt_on_missing_version(title, version)
|
|
122
|
+
log_debug "Checking for missing version '#{version}' of title '#{title}'"
|
|
113
123
|
return if all_versions(title).include? version
|
|
114
124
|
|
|
115
125
|
msg = "No version '#{version}' for title '#{title}'."
|
|
@@ -127,6 +137,8 @@ module Xolo
|
|
|
127
137
|
# @return [void]
|
|
128
138
|
##################
|
|
129
139
|
def halt_on_existing_version(title, version)
|
|
140
|
+
log_debug "Checking for existing version '#{version}' of title '#{title}'"
|
|
141
|
+
|
|
130
142
|
return unless all_versions(title).include? version
|
|
131
143
|
|
|
132
144
|
msg = "Version '#{version}' of title '#{title}' already exists."
|
|
@@ -151,7 +163,7 @@ module Xolo
|
|
|
151
163
|
halt 409, { status: 409, error: msg }
|
|
152
164
|
end
|
|
153
165
|
|
|
154
|
-
end #
|
|
166
|
+
end # Versions
|
|
155
167
|
|
|
156
168
|
end # Helpers
|
|
157
169
|
|
|
@@ -67,7 +67,11 @@ module Xolo
|
|
|
67
67
|
# This is so that the changelog can be accessed after the title is deleted.
|
|
68
68
|
################
|
|
69
69
|
def self.backup_file_dir
|
|
70
|
-
@backup_file_dir
|
|
70
|
+
return @backup_file_dir if @backup_file_dir&.exist?
|
|
71
|
+
|
|
72
|
+
@backup_file_dir = Xolo::Server::BACKUPS_DIR + 'changelogs'
|
|
73
|
+
@backup_file_dir.mkpath unless @backup_file_dir.exist?
|
|
74
|
+
@backup_file_dir
|
|
71
75
|
end
|
|
72
76
|
|
|
73
77
|
# A hash of the read-write locks for each title's changelog file
|
|
@@ -184,8 +188,10 @@ module Xolo
|
|
|
184
188
|
# @return [void]
|
|
185
189
|
#######################
|
|
186
190
|
def log_change(attrib: nil, old_val: nil, new_val: nil, msg: nil)
|
|
191
|
+
log_debug "Preparing to log change for #{title}: attrib=#{attrib.inspect}, old_val=#{old_val.inspect}, new_val=#{new_val.inspect}, msg=#{msg.inspect}"
|
|
192
|
+
|
|
187
193
|
raise ArgumentError, 'Must provide attrib: or action:' if !msg && !attrib
|
|
188
|
-
raise ArgumentError, 'Must provide old: or new: or both with attrib:' if attrib &&
|
|
194
|
+
raise ArgumentError, 'Must provide old: or new: or both with attrib:' if attrib && old_val.nil? && new_val.nil?
|
|
189
195
|
|
|
190
196
|
# if action, attrib, old, and new are ignored
|
|
191
197
|
attrib, old_val, new_val = nil if msg
|
|
@@ -262,19 +268,6 @@ module Xolo
|
|
|
262
268
|
def log_update_changes
|
|
263
269
|
return unless changes_for_update
|
|
264
270
|
|
|
265
|
-
# self.class::ATTRIBUTES.each do |attr, deets|
|
|
266
|
-
# next unless deets[:changelog]
|
|
267
|
-
|
|
268
|
-
# new_val = deets[:type] == :time ? Time.parse(new_data_for_update[attr]) : new_data_for_update[attr]
|
|
269
|
-
# old_val = send attr
|
|
270
|
-
|
|
271
|
-
# new_val = "'#{new_val.sort.join("', '")}'" if new_val.is_a? Array
|
|
272
|
-
# old_val = "'#{old_val.sort.join("', '")}'" if old_val.is_a? Array
|
|
273
|
-
# next if new_val == old_val
|
|
274
|
-
|
|
275
|
-
# log_change attrib: attr, old_val: old_val, new_val: new_val
|
|
276
|
-
# end
|
|
277
|
-
|
|
278
271
|
changes_for_update.each do |attr, vals|
|
|
279
272
|
log_change attrib: attr, old_val: vals[:old], new_val: vals[:new]
|
|
280
273
|
end
|
|
@@ -302,7 +295,7 @@ module Xolo
|
|
|
302
295
|
|
|
303
296
|
# final backup
|
|
304
297
|
changelog_backup_file.delete if changelog_backup_file.exist?
|
|
305
|
-
changelog_file.rename
|
|
298
|
+
changelog_file.rename(changelog_backup_file)
|
|
306
299
|
end
|
|
307
300
|
end
|
|
308
301
|
|