rails_admin_settings 1.4.0 → 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.
@@ -7,7 +7,6 @@ end
7
7
 
8
8
  module RailsAdminSettings
9
9
  class Setting
10
- #binding.pry
11
10
  if RailsAdminSettings.mongoid?
12
11
  include RailsAdminSettings::Mongoid
13
12
  end
@@ -1,4 +1,4 @@
1
- - if ['string', 'integer', 'phone', 'email', 'address', 'url', 'domain'].include?(@object.type)
1
+ - if ['string', 'integer', 'float', 'phone', 'email', 'address', 'url', 'domain'].include?(@object.type)
2
2
  = form.text_field :raw, :value => field.value
3
3
  - if @object.type == 'boolean'
4
4
  = form.check_box :raw, {:value => field.value}, 'true', 'false'
@@ -8,7 +8,6 @@
8
8
  = form.text_area :raw, :value => field.value, :rows => 10, :cols => 80
9
9
  - elsif @object.type == 'code' || @object.type == 'sanitize_code'
10
10
  :ruby
11
- #binding.pry
12
11
  js_data = {
13
12
  csspath: asset_path('ckeditor/plugins/codemirror/css/codemirror.min.css'),
14
13
  jspath: asset_path('ckeditor/plugins/codemirror/js/codemirror.min.js'),
@@ -15,6 +15,9 @@ class CreateRailsAdminSettings < ActiveRecord::Migration[5.0]
15
15
  t.attachment :file
16
16
  elsif defined?(CarrierWave)
17
17
  t.string :file
18
+ elsif defined?(Shrine)
19
+ t.text :file_data
20
+
18
21
  end
19
22
  t.timestamps
20
23
  end
@@ -6,6 +6,20 @@ module RailsAdminSettings
6
6
  end
7
7
  cattr_accessor :scrubber
8
8
 
9
+ class PersistenceException < Exception
10
+ end
11
+
12
+ autoload :Mongoid, "rails_admin_settings/mongoid"
13
+ autoload :Fallback, "rails_admin_settings/fallback"
14
+ autoload :Namespaced, "rails_admin_settings/namespaced"
15
+ autoload :Processing, "rails_admin_settings/processing"
16
+ autoload :Validation, "rails_admin_settings/validation"
17
+ autoload :RequireHelpers, "rails_admin_settings/require_helpers"
18
+ autoload :RailsAdminConfig, "rails_admin_settings/rails_admin_config"
19
+ autoload :Uploads, "rails_admin_settings/uploads"
20
+ autoload :HexColorValidator, "rails_admin_settings/hex_color_validator"
21
+ autoload :Dumper, "rails_admin_settings/dumper"
22
+
9
23
  class << self
10
24
  def orm
11
25
  if defined?(::Mongoid)
@@ -22,55 +36,74 @@ module RailsAdminSettings
22
36
  def active_record?
23
37
  orm == :active_record
24
38
  end
25
- end
26
-
27
- class PersistenceException < Exception
28
- end
29
-
30
- autoload :Mongoid, "rails_admin_settings/mongoid"
31
- autoload :Fallback, "rails_admin_settings/fallback"
32
- autoload :Namespaced, "rails_admin_settings/namespaced"
33
- autoload :Processing, "rails_admin_settings/processing"
34
- autoload :Validation, "rails_admin_settings/validation"
35
- autoload :RequireHelpers, "rails_admin_settings/require_helpers"
36
- autoload :RailsAdminConfig, "rails_admin_settings/rails_admin_config"
37
- autoload :Uploads, "rails_admin_settings/uploads"
38
- autoload :HexColorValidator, "rails_admin_settings/hex_color_validator"
39
- autoload :Dumper, "rails_admin_settings/dumper"
40
39
 
41
- def self.migrate!
42
- if RailsAdminSettings.mongoid?
43
- RailsAdminSettings::Setting.where(:ns.exists => false).update_all(ns: 'main')
44
- RailsAdminSettings::Setting.all.each do |s|
45
- s.kind = s.read_attribute(:type) if !s.read_attribute(:type).blank? && s.kind != s.read_attribute(:type)
46
- s.save! if s.changed?
47
- s.unset(:type)
48
- end
49
- else
50
- if Settings.table_exists?
51
- RailsAdminSettings::Setting.where("ns IS NULL").update_all(ns: 'main')
40
+ def apply_defaults!(file, verbose = false)
41
+ if File.file?(file)
42
+ puts "[settings] Loading from #{file}" if verbose
43
+ if defined?(Psych) && Psych.respond_to?(:safe_load)
44
+ yaml = Psych.safe_load(File.read(file))
45
+ else
46
+ yaml = YAML.load(File.read(file), safe: true)
47
+ end
48
+ yaml.each_pair do |namespace, vals|
49
+ process_defaults(namespace, vals, verbose)
50
+ end
52
51
  end
53
52
  end
54
- end
55
-
56
- def self.track_history!
57
- return false unless Settings.table_exists?
58
53
 
59
- if mongoid?
60
- if ::Mongoid.const_defined?('History')
61
- RailsAdminSettings::Setting.send(:include, ::Mongoid::History::Trackable)
62
- RailsAdminSettings::Setting.send(:track_history, {track_create: true, track_destroy: true})
63
- else
64
- puts "[rails_admin_settings] WARN unable to track_history: Mongoid::History not loaded!"
54
+ def process_defaults(namespace, vals, verbose = false)
55
+ vals.symbolize_keys!
56
+ n = Settings.ns(namespace)
57
+ vals.each_pair do |key, val|
58
+ val.symbolize_keys!
59
+ if !val[:kind].nil? && (val[:kind] == 'file' || val[:kind] == 'image')
60
+ unless Settings.file_uploads_supported
61
+ raise PersistenceException, "Fatal: setting #{key} is #{val[:type]} but file upload engine is not detected"
62
+ end
63
+ value = File.open(Settings.root_file_path.join(val.delete(:value)))
64
+ else
65
+ value = val.delete(:value)
66
+ end
67
+ puts "#{key} - default '#{value}' current '#{Settings.get(key).raw}'" if verbose
68
+ n.set(key, value, val.merge(overwrite: false))
65
69
  end
66
- if ::Mongoid.const_defined?('Userstamp')
67
- RailsAdminSettings::Setting.send(:include, ::Mongoid::Userstamp)
70
+ n.unload!
71
+ end
72
+
73
+ def migrate!
74
+ if RailsAdminSettings.mongoid?
75
+ RailsAdminSettings::Setting.where(:ns.exists => false).update_all(ns: 'main')
76
+ RailsAdminSettings::Setting.all.each do |s|
77
+ s.kind = s.read_attribute(:type) if !s.read_attribute(:type).blank? && s.kind != s.read_attribute(:type)
78
+ s.save! if s.changed?
79
+ s.unset(:type)
80
+ end
68
81
  else
69
- puts "[rails_admin_settings] WARN unable to track_history: Mongoid::Userstamp not loaded!"
82
+ if Settings.table_exists?
83
+ RailsAdminSettings::Setting.where("ns IS NULL").update_all(ns: 'main')
84
+ end
70
85
  end
71
- elsif active_record?
72
- if defined?(PaperTrail) && PaperTrail::Version.table_exists?
73
- RailsAdminSettings::Setting.send(:has_paper_trail)
86
+ end
87
+
88
+ def track_history!
89
+ return false unless Settings.table_exists?
90
+
91
+ if mongoid?
92
+ if ::Mongoid.const_defined?('History')
93
+ RailsAdminSettings::Setting.send(:include, ::Mongoid::History::Trackable)
94
+ RailsAdminSettings::Setting.send(:track_history, {track_create: true, track_destroy: true})
95
+ else
96
+ puts "[rails_admin_settings] WARN unable to track_history: Mongoid::History not loaded!"
97
+ end
98
+ if ::Mongoid.const_defined?('Userstamp')
99
+ RailsAdminSettings::Setting.send(:include, ::Mongoid::Userstamp)
100
+ else
101
+ puts "[rails_admin_settings] WARN unable to track_history: Mongoid::Userstamp not loaded!"
102
+ end
103
+ elsif active_record?
104
+ if defined?(PaperTrail) && PaperTrail::Version.table_exists?
105
+ RailsAdminSettings::Setting.send(:has_paper_trail)
106
+ end
74
107
  end
75
108
  end
76
109
  end
@@ -0,0 +1,193 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Shrine
4
+ module Plugins
5
+ # Documentation can be found on https://shrinerb.com/docs/plugins/determine_mime_type
6
+ module DetermineMimeType
7
+ LOG_SUBSCRIBER = -> (event) do
8
+ Shrine.logger.info "MIME Type (#{event.duration}ms) – #{{
9
+ io: event[:io].class,
10
+ uploader: event[:uploader],
11
+ }.inspect}"
12
+ end
13
+
14
+ def self.configure(uploader, log_subscriber: LOG_SUBSCRIBER, **opts)
15
+ uploader.opts[:determine_mime_type] ||= { analyzer: :file, analyzer_options: {} }
16
+ uploader.opts[:determine_mime_type].merge!(opts)
17
+
18
+ # instrumentation plugin integration
19
+ uploader.subscribe(:mime_type, &log_subscriber) if uploader.respond_to?(:subscribe)
20
+ end
21
+
22
+ module ClassMethods
23
+ # Determines the MIME type of the IO object by calling the specified
24
+ # analyzer.
25
+ def determine_mime_type(io)
26
+ analyzer = opts[:determine_mime_type][:analyzer]
27
+
28
+ analyzer = mime_type_analyzer(analyzer) if analyzer.is_a?(Symbol)
29
+ args = if analyzer.is_a?(Proc)
30
+ [io, mime_type_analyzers].take(analyzer.arity.abs)
31
+ else
32
+ [io, opts[:determine_mime_type][:analyzer_options]]
33
+ end
34
+
35
+ mime_type = instrument_mime_type(io) { analyzer.call(*args) }
36
+ io.rewind
37
+
38
+ mime_type
39
+ end
40
+ alias mime_type determine_mime_type
41
+
42
+ # Returns a hash of built-in MIME type analyzers, where keys are
43
+ # analyzer names and values are `#call`-able objects which accepts the
44
+ # IO object.
45
+ def mime_type_analyzers
46
+ @mime_type_analyzers ||= MimeTypeAnalyzer::SUPPORTED_TOOLS.inject({}) do |hash, tool|
47
+ hash.merge!(tool => mime_type_analyzer(tool))
48
+ end
49
+ end
50
+
51
+ # Returns callable mime type analyzer object.
52
+ def mime_type_analyzer(name)
53
+ MimeTypeAnalyzer.new(name)
54
+ end
55
+
56
+ private
57
+
58
+ # Sends a `mime_type.shrine` event for instrumentation plugin.
59
+ def instrument_mime_type(io, &block)
60
+ return yield unless respond_to?(:instrument)
61
+
62
+ instrument(:mime_type, io: io, &block)
63
+ end
64
+ end
65
+
66
+ module InstanceMethods
67
+ private
68
+
69
+ # Calls the configured MIME type analyzer.
70
+ def extract_mime_type(io)
71
+ self.class.determine_mime_type(io)
72
+ end
73
+ end
74
+
75
+ class MimeTypeAnalyzer
76
+ SUPPORTED_TOOLS = [:fastimage, :file, :filemagic, :mimemagic, :marcel, :mime_types, :mini_mime, :content_type]
77
+ MAGIC_NUMBER = 256 * 1024
78
+
79
+ def initialize(tool)
80
+ raise Error, "unknown mime type analyzer #{tool.inspect}, supported analyzers are: #{SUPPORTED_TOOLS.join(",")}" unless SUPPORTED_TOOLS.include?(tool)
81
+
82
+ @tool = tool
83
+ end
84
+
85
+ def call(io, options = {})
86
+ mime_type = send(:"extract_with_#{@tool}", io, options)
87
+ io.rewind
88
+
89
+ mime_type
90
+ end
91
+
92
+ private
93
+
94
+ def extract_with_file(io, options)
95
+ require "open3"
96
+
97
+ return nil if io.eof? # file command returns "application/x-empty" for empty files
98
+
99
+ Open3.popen3(*%W[file --mime-type --brief -]) do |stdin, stdout, stderr, thread|
100
+ begin
101
+ IO.copy_stream(io, stdin.binmode)
102
+ rescue Errno::EPIPE
103
+ end
104
+ stdin.close
105
+
106
+ status = thread.value
107
+
108
+ raise Error, "file command failed to spawn: #{stderr.read}" if status.nil?
109
+ raise Error, "file command failed: #{stderr.read}" unless status.success?
110
+
111
+ $stderr.print(stderr.read)
112
+
113
+ output = stdout.read.strip
114
+
115
+ raise Error, "file command failed: #{output}" if output.include?("cannot open")
116
+
117
+ output
118
+ end
119
+ rescue Errno::ENOENT
120
+ raise Error, "file command-line tool is not installed"
121
+ end
122
+
123
+ def extract_with_fastimage(io, options)
124
+ require "fastimage"
125
+
126
+ type = FastImage.type(io)
127
+ "image/#{type}" if type
128
+ end
129
+
130
+ def extract_with_filemagic(io, options)
131
+ require "filemagic"
132
+
133
+ return nil if io.eof? # FileMagic returns "application/x-empty" for empty files
134
+
135
+ FileMagic.open(FileMagic::MAGIC_MIME_TYPE) do |filemagic|
136
+ filemagic.buffer(io.read(MAGIC_NUMBER))
137
+ end
138
+ end
139
+
140
+ def extract_with_mimemagic(io, options)
141
+ require "mimemagic"
142
+
143
+ mime = MimeMagic.by_magic(io)
144
+ mime&.type
145
+ end
146
+
147
+ def extract_with_marcel(io, options)
148
+ require "marcel"
149
+
150
+ return nil if io.eof? # marcel returns "application/octet-stream" for empty files
151
+
152
+ filename = (options[:filename_fallback] ? extract_filename(io) : nil)
153
+ Marcel::MimeType.for(io, name: filename)
154
+ end
155
+
156
+ def extract_with_mime_types(io, options)
157
+ require "mime/types"
158
+
159
+ if filename = extract_filename(io)
160
+ mime_type = MIME::Types.of(filename).first
161
+ mime_type&.content_type
162
+ end
163
+ end
164
+
165
+ def extract_with_mini_mime(io, options)
166
+ require "mini_mime"
167
+
168
+ if filename = extract_filename(io)
169
+ info = MiniMime.lookup_by_filename(filename)
170
+ info&.content_type
171
+ end
172
+ end
173
+
174
+ def extract_with_content_type(io, options)
175
+ if io.respond_to?(:content_type) && io.content_type
176
+ io.content_type.split(";").first
177
+ end
178
+ end
179
+
180
+ def extract_filename(io)
181
+ if io.respond_to?(:original_filename)
182
+ io.original_filename
183
+ elsif io.respond_to?(:path)
184
+ File.basename(io.path)
185
+ end
186
+ end
187
+ end
188
+
189
+ end
190
+
191
+ register_plugin(:determine_mime_type, DetermineMimeType)
192
+ end
193
+ end
@@ -4,6 +4,7 @@ module RailsAdminSettings
4
4
  'string',
5
5
  'text',
6
6
  'integer',
7
+ 'float',
7
8
  'boolean',
8
9
  'html',
9
10
  'code',
@@ -10,7 +10,7 @@ module RailsAdminSettings
10
10
  end
11
11
 
12
12
  def text_kind?
13
- (RailsAdminSettings.kinds - ['phone', 'phones', 'integer', 'yaml', 'json', 'boolean']).include? kind
13
+ (RailsAdminSettings.kinds - ['phone', 'phones', 'integer', 'float', 'yaml', 'json', 'boolean']).include? kind
14
14
  end
15
15
 
16
16
  def upload_kind?
@@ -31,10 +31,14 @@ module RailsAdminSettings
31
31
 
32
32
  def value
33
33
  if upload_kind?
34
- if file?
35
- file.url
34
+ unless defined?(Shrine)
35
+ if file?
36
+ file.url
37
+ else
38
+ nil
39
+ end
36
40
  else
37
- nil
41
+ file.url if file.present?
38
42
  end
39
43
  elsif raw.blank? || disabled?
40
44
  default_value
@@ -54,7 +58,8 @@ module RailsAdminSettings
54
58
  end
55
59
 
56
60
  def to_s
57
- if yaml_kind? || json_kind? || phone_kind? || integer_kind?
61
+ if yaml_kind? || json_kind? || phone_kind? || integer_kind? || float_kind?
62
+
58
63
  raw
59
64
  else
60
65
  value
@@ -94,6 +99,8 @@ module RailsAdminSettings
94
99
  ''
95
100
  elsif integer_kind?
96
101
  0
102
+ elsif float_kind?
103
+ 0
97
104
  elsif yaml_kind?
98
105
  nil
99
106
  elsif json_kind?
@@ -181,6 +188,8 @@ module RailsAdminSettings
181
188
  process_text
182
189
  elsif integer_kind?
183
190
  raw.to_i
191
+ elsif float_kind?
192
+ raw.to_f
184
193
  elsif yaml_kind?
185
194
  load_yaml
186
195
  elsif json_kind?
@@ -4,7 +4,6 @@ module RailsAdminSettings
4
4
  if base.respond_to?(:rails_admin)
5
5
  base.rails_admin do
6
6
  navigation_label I18n.t('admin.settings.label')
7
-
8
7
  list do
9
8
  if Object.const_defined?('RailsAdminToggleable')
10
9
  field :enabled, :toggle
@@ -16,9 +15,9 @@ module RailsAdminSettings
16
15
  field :name
17
16
  field :raw do
18
17
  pretty_value do
19
- if bindings[:object].file_kind?
18
+ if bindings[:object].file_kind? and !defined?(Shrine) and bindings[:object].to_path.present?
20
19
  "<a href='#{CGI::escapeHTML(bindings[:object].file.url)}'>#{CGI::escapeHTML(bindings[:object].to_path)}</a>".html_safe
21
- elsif bindings[:object].image_kind?
20
+ elsif bindings[:object].image_kind? and !defined?(Shrine) and !bindings[:object].file.nil?
22
21
  "<a href='#{CGI::escapeHTML(bindings[:object].file.url)}'><img src='#{CGI::escapeHTML(bindings[:object].file.url)}' /></a>".html_safe
23
22
  else
24
23
  value
@@ -39,6 +39,8 @@ class Settings < BasicObject
39
39
 
40
40
  def table_exists?
41
41
  RailsAdminSettings.mongoid? || RailsAdminSettings::Setting.table_exists?
42
+ rescue ActiveRecord::NoDatabaseError
43
+ false
42
44
  end
43
45
 
44
46
  def unload!
@@ -64,32 +66,7 @@ class Settings < BasicObject
64
66
  end
65
67
 
66
68
  def apply_defaults!(file, verbose = false)
67
- if File.file?(file)
68
- puts "[settings] Loading from #{file}" if verbose
69
- if defined?(Psych) && Psych.respond_to?(:safe_load)
70
- yaml = Psych.safe_load(File.read(file))
71
- else
72
- yaml = YAML.load(File.read(file), safe: true)
73
- end
74
- yaml.each_pair do |namespace, vals|
75
- vals.symbolize_keys!
76
- n = ns(namespace)
77
- vals.each_pair do |key, val|
78
- val.symbolize_keys!
79
- if !val[:kind].nil? && (val[:kind] == 'file' || val[:kind] == 'image')
80
- unless @@file_uploads_supported
81
- ::Kernel.raise ::RailsAdminSettings::PersistenceException, "Fatal: setting #{key} is #{val[:type]} but file upload engine is not detected"
82
- end
83
- value = File.open(root_file_path.join(val.delete(:value)))
84
- else
85
- value = val.delete(:value)
86
- end
87
- puts "#{key} - default '#{value}' current '#{Settings.get(key).raw}'" if verbose
88
- n.set(key, value, val.merge(overwrite: false))
89
- end
90
- n.unload!
91
- end
92
- end
69
+ RailsAdminSettings.apply_defaults!(file, verbose)
93
70
  end
94
71
 
95
72
  def get(key, options = {})