goodmin 0.0.1
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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +1359 -0
- data/Rakefile +56 -0
- data/app/assets/stylesheets/goodmin/application.css +75 -0
- data/app/controllers/goodmin/application_controller.rb +43 -0
- data/app/controllers/goodmin/resource_controller.rb +235 -0
- data/app/helpers/goodmin/application_helper.rb +45 -0
- data/app/javascript/goodmin/application.js +8 -0
- data/app/javascript/goodmin/controllers/batch_actions_controller.js +101 -0
- data/app/javascript/goodmin/controllers/datetimepicker_controller.js +24 -0
- data/app/javascript/goodmin/controllers/navigation_controller.js +30 -0
- data/app/jobs/goodmin/application_job.rb +4 -0
- data/app/mailers/goodmin/application_mailer.rb +6 -0
- data/app/models/goodmin/application_record.rb +5 -0
- data/app/views/goodmin/application/welcome.html.erb +17 -0
- data/app/views/goodmin/fields/association/_form.html.erb +19 -0
- data/app/views/goodmin/fields/association/_index.html.erb +6 -0
- data/app/views/goodmin/fields/association/_show.html.erb +6 -0
- data/app/views/goodmin/fields/boolean/_form.html.erb +7 -0
- data/app/views/goodmin/fields/boolean/_index.html.erb +1 -0
- data/app/views/goodmin/fields/boolean/_show.html.erb +1 -0
- data/app/views/goodmin/fields/date/_form.html.erb +6 -0
- data/app/views/goodmin/fields/date/_index.html.erb +1 -0
- data/app/views/goodmin/fields/date/_show.html.erb +1 -0
- data/app/views/goodmin/fields/date_time/_form.html.erb +6 -0
- data/app/views/goodmin/fields/date_time/_index.html.erb +1 -0
- data/app/views/goodmin/fields/date_time/_show.html.erb +1 -0
- data/app/views/goodmin/fields/enum/_index.html.erb +1 -0
- data/app/views/goodmin/fields/enum/_show.html.erb +1 -0
- data/app/views/goodmin/fields/nested_has_one/_form.html.erb +6 -0
- data/app/views/goodmin/fields/nested_has_one/_index.html.erb +1 -0
- data/app/views/goodmin/fields/nested_has_one/_show.html.erb +1 -0
- data/app/views/goodmin/fields/number/_form.html.erb +5 -0
- data/app/views/goodmin/fields/number/_index.html.erb +1 -0
- data/app/views/goodmin/fields/number/_show.html.erb +1 -0
- data/app/views/goodmin/fields/password/_form.html.erb +5 -0
- data/app/views/goodmin/fields/password/_index.html.erb +1 -0
- data/app/views/goodmin/fields/password/_show.html.erb +1 -0
- data/app/views/goodmin/fields/select/_form.html.erb +5 -0
- data/app/views/goodmin/fields/select/_index.html.erb +1 -0
- data/app/views/goodmin/fields/select/_show.html.erb +1 -0
- data/app/views/goodmin/fields/string/_form.html.erb +5 -0
- data/app/views/goodmin/fields/string/_index.html.erb +1 -0
- data/app/views/goodmin/fields/string/_show.html.erb +1 -0
- data/app/views/goodmin/fields/text/_form.html.erb +5 -0
- data/app/views/goodmin/fields/text/_index.html.erb +1 -0
- data/app/views/goodmin/fields/text/_show.html.erb +1 -0
- data/app/views/goodmin/resource/_actions.html.erb +9 -0
- data/app/views/goodmin/resource/_batch_actions.html.erb +12 -0
- data/app/views/goodmin/resource/_breadcrumb.html.erb +33 -0
- data/app/views/goodmin/resource/_breadcrumb_actions.html.erb +41 -0
- data/app/views/goodmin/resource/_button_actions.html.erb +3 -0
- data/app/views/goodmin/resource/_errors.html.erb +9 -0
- data/app/views/goodmin/resource/_export_actions.html.erb +15 -0
- data/app/views/goodmin/resource/_filters.html.erb +22 -0
- data/app/views/goodmin/resource/_form.html.erb +26 -0
- data/app/views/goodmin/resource/_pagination.html.erb +40 -0
- data/app/views/goodmin/resource/_scopes.html.erb +14 -0
- data/app/views/goodmin/resource/_table.html.erb +45 -0
- data/app/views/goodmin/resource/columns/_actions.html.erb +28 -0
- data/app/views/goodmin/resource/edit.html.erb +5 -0
- data/app/views/goodmin/resource/index.csv.csvbuilder +5 -0
- data/app/views/goodmin/resource/index.html.erb +10 -0
- data/app/views/goodmin/resource/index.json.jbuilder +3 -0
- data/app/views/goodmin/resource/new.html.erb +5 -0
- data/app/views/goodmin/resource/show.html.erb +11 -0
- data/app/views/goodmin/resource/show.json.jbuilder +1 -0
- data/app/views/goodmin/sessions/new.html.erb +11 -0
- data/app/views/goodmin/shared/_navigation.html.erb +0 -0
- data/app/views/goodmin/shared/_navigation_aside.html.erb +7 -0
- data/app/views/layouts/goodmin/_content.html.erb +13 -0
- data/app/views/layouts/goodmin/_layout.html.erb +22 -0
- data/app/views/layouts/goodmin/application.html.erb +28 -0
- data/app/views/layouts/goodmin/login.html.erb +18 -0
- data/config/importmap.rb +5 -0
- data/config/locales/en.yml +49 -0
- data/config/locales/pl-BR.yml +49 -0
- data/config/locales/pt-BR.yml +49 -0
- data/config/locales/sv.yml +49 -0
- data/config/routes.rb +3 -0
- data/lib/generators/goodmin/authentication/authentication_generator.rb +41 -0
- data/lib/generators/goodmin/authentication/templates/sessions_controller.rb +9 -0
- data/lib/generators/goodmin/install/install_generator.rb +41 -0
- data/lib/generators/goodmin/policy/policy_generator.rb +7 -0
- data/lib/generators/goodmin/policy/templates/policy.rb +23 -0
- data/lib/generators/goodmin/resource/resource_generator.rb +31 -0
- data/lib/generators/goodmin/resource/templates/resource.rb +25 -0
- data/lib/generators/goodmin/resource/templates/resource_controller.rb +9 -0
- data/lib/generators/goodmin/resource/templates/resource_model.rb +4 -0
- data/lib/generators/goodmin/resource/templates/resource_service.rb +23 -0
- data/lib/goodmin/authentication/sessions_controller.rb +46 -0
- data/lib/goodmin/authentication/user.rb +27 -0
- data/lib/goodmin/authentication.rb +35 -0
- data/lib/goodmin/authorization/policy.rb +41 -0
- data/lib/goodmin/authorization.rb +69 -0
- data/lib/goodmin/engine.rb +30 -0
- data/lib/goodmin/fields/association.rb +62 -0
- data/lib/goodmin/fields/base.rb +57 -0
- data/lib/goodmin/fields/boolean.rb +6 -0
- data/lib/goodmin/fields/date.rb +6 -0
- data/lib/goodmin/fields/date_time.rb +6 -0
- data/lib/goodmin/fields/enum.rb +15 -0
- data/lib/goodmin/fields/nested_has_one.rb +41 -0
- data/lib/goodmin/fields/number.rb +6 -0
- data/lib/goodmin/fields/password.rb +6 -0
- data/lib/goodmin/fields/select.rb +19 -0
- data/lib/goodmin/fields/string.rb +6 -0
- data/lib/goodmin/fields/text.rb +6 -0
- data/lib/goodmin/generators/base.rb +49 -0
- data/lib/goodmin/generators/named_base.rb +31 -0
- data/lib/goodmin/helpers/application.rb +47 -0
- data/lib/goodmin/helpers/batch_actions.rb +21 -0
- data/lib/goodmin/helpers/filters.rb +123 -0
- data/lib/goodmin/helpers/forms.rb +42 -0
- data/lib/goodmin/helpers/navigation.rb +52 -0
- data/lib/goodmin/helpers/tables.rb +25 -0
- data/lib/goodmin/helpers/translations.rb +19 -0
- data/lib/goodmin/paginator.rb +55 -0
- data/lib/goodmin/resolver.rb +141 -0
- data/lib/goodmin/resources/attribute.rb +46 -0
- data/lib/goodmin/resources/form_builder.rb +96 -0
- data/lib/goodmin/resources/form_component.rb +65 -0
- data/lib/goodmin/resources/form_components/col.rb +33 -0
- data/lib/goodmin/resources/form_components/row.rb +25 -0
- data/lib/goodmin/resources/form_components/section.rb +51 -0
- data/lib/goodmin/resources/form_components/tab.rb +49 -0
- data/lib/goodmin/resources/resource/associations.rb +23 -0
- data/lib/goodmin/resources/resource/batch_actions.rb +56 -0
- data/lib/goodmin/resources/resource/filters.rb +44 -0
- data/lib/goodmin/resources/resource/ordering.rb +41 -0
- data/lib/goodmin/resources/resource/pagination.rb +22 -0
- data/lib/goodmin/resources/resource/scopes.rb +61 -0
- data/lib/goodmin/resources/resource.rb +199 -0
- data/lib/goodmin/resources/resource_controller/batch_actions.rb +49 -0
- data/lib/goodmin/resources/resource_service/associations.rb +23 -0
- data/lib/goodmin/resources/resource_service/batch_actions.rb +52 -0
- data/lib/goodmin/resources/resource_service/filters.rb +44 -0
- data/lib/goodmin/resources/resource_service/ordering.rb +41 -0
- data/lib/goodmin/resources/resource_service/pagination.rb +22 -0
- data/lib/goodmin/resources/resource_service/scopes.rb +61 -0
- data/lib/goodmin/resources/resource_service.rb +199 -0
- data/lib/goodmin/service_locator.rb +25 -0
- data/lib/goodmin/version.rb +3 -0
- data/lib/goodmin.rb +44 -0
- data/lib/tasks/goodmin_tasks.rake +4 -0
- metadata +461 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module Goodmin
|
|
2
|
+
module Fields
|
|
3
|
+
class Association < Base
|
|
4
|
+
def belongs_to?
|
|
5
|
+
macro == :belongs_to
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def many_to_many?
|
|
9
|
+
reflection = record.class.reflect_on_association(attribute)
|
|
10
|
+
return false unless reflection
|
|
11
|
+
reflection.macro == :has_and_belongs_to_many ||
|
|
12
|
+
(reflection.macro == :has_many && reflection.options[:through].present?)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def has_many?
|
|
16
|
+
return false unless reflection
|
|
17
|
+
macro == :has_many && !many_to_many?
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def select_attribute
|
|
21
|
+
belongs_to? ? :"#{attribute}_id" : :"#{attribute.to_s.singularize}_ids"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def collection
|
|
25
|
+
klass = reflection&.klass
|
|
26
|
+
return [] unless klass
|
|
27
|
+
|
|
28
|
+
if associated_service
|
|
29
|
+
klass.all.map { |a| [display_name_for(a), a.id] }
|
|
30
|
+
else
|
|
31
|
+
method_name = resource_service.option_text_for_association(attribute)
|
|
32
|
+
klass.all.map { |a| [a.public_send(method_name), a.id] }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def display_name_for(associated_record)
|
|
37
|
+
return nil unless associated_record
|
|
38
|
+
service = associated_service
|
|
39
|
+
service ? service.display_name(associated_record) : associated_record.to_s
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def associated_service
|
|
43
|
+
@associated_service ||= begin
|
|
44
|
+
service_class = find_associated_service_class(reflection&.klass)
|
|
45
|
+
service_class&.new
|
|
46
|
+
rescue StandardError
|
|
47
|
+
nil
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def macro
|
|
54
|
+
reflection&.macro
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def reflection
|
|
58
|
+
record.class.reflect_on_association(attribute)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module Goodmin
|
|
2
|
+
module Fields
|
|
3
|
+
class Base
|
|
4
|
+
attr_reader :attribute, :record, :resource_service, :options
|
|
5
|
+
|
|
6
|
+
def initialize(attribute:, record:, resource_service:, **options)
|
|
7
|
+
@attribute = attribute
|
|
8
|
+
@record = record
|
|
9
|
+
@resource_service = resource_service
|
|
10
|
+
@options = options
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def value
|
|
14
|
+
record.public_send(attribute)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def hint
|
|
18
|
+
hint_option = options[:hint]
|
|
19
|
+
result = if hint_option.is_a?(Proc)
|
|
20
|
+
hint_option.call(record)
|
|
21
|
+
elsif hint_option.present?
|
|
22
|
+
hint_option
|
|
23
|
+
else
|
|
24
|
+
i18n_hint
|
|
25
|
+
end
|
|
26
|
+
result&.html_safe
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def find_associated_service_class(klass)
|
|
30
|
+
Goodmin::ServiceLocator.find_service_class_for(klass, context_service_class: resource_service.class)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.partial_index
|
|
34
|
+
"goodmin/fields/#{field_type}/index"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.partial_show
|
|
38
|
+
"goodmin/fields/#{field_type}/show"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.partial_form
|
|
42
|
+
"goodmin/fields/#{field_type}/form"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private_class_method def self.field_type
|
|
46
|
+
name.demodulize.underscore
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def i18n_hint
|
|
52
|
+
model_key = record.class.model_name.i18n_key
|
|
53
|
+
I18n.t("activerecord.hints.#{model_key}.#{attribute}", default: nil)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Goodmin
|
|
2
|
+
module Fields
|
|
3
|
+
class Enum < Select
|
|
4
|
+
def self.partial_form
|
|
5
|
+
Fields::Select.partial_form
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def collection
|
|
9
|
+
(record.class.defined_enums[attribute.to_s] || {}).keys.map do |key|
|
|
10
|
+
[key.humanize, key]
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Goodmin
|
|
2
|
+
module Fields
|
|
3
|
+
class NestedHasOne < Base
|
|
4
|
+
def associated_record
|
|
5
|
+
@associated_record ||= begin
|
|
6
|
+
existing = record.public_send(attribute)
|
|
7
|
+
return existing if existing
|
|
8
|
+
|
|
9
|
+
build_method = :"build_#{attribute}"
|
|
10
|
+
record.public_send(build_method) if record.respond_to?(build_method)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def associated_service
|
|
15
|
+
@associated_service ||= associated_service_class&.new
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def nested_attributes
|
|
19
|
+
associated_service&.attrs_for_form || []
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def nested_form_nodes
|
|
23
|
+
associated_service&.form_nodes || []
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def reflection
|
|
29
|
+
record.class.reflect_on_association(attribute)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def associated_model_class
|
|
33
|
+
reflection&.klass
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def associated_service_class
|
|
37
|
+
find_associated_service_class(associated_model_class)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Goodmin
|
|
2
|
+
module Fields
|
|
3
|
+
class Select < Base
|
|
4
|
+
def collection
|
|
5
|
+
col = options[:collection]
|
|
6
|
+
return [] if col.nil?
|
|
7
|
+
col.respond_to?(:call) ? col.call(record) : col
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def select_options
|
|
11
|
+
options.fetch(:options, {})
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def select_html_options
|
|
15
|
+
{ class: "form-control" }.merge(options.fetch(:html_options, {}))
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require "active_support/all"
|
|
2
|
+
|
|
3
|
+
module Goodmin
|
|
4
|
+
module Generators
|
|
5
|
+
class Base < Rails::Generators::Base
|
|
6
|
+
def self.source_paths
|
|
7
|
+
%w[authentication install policy resource].map do |path|
|
|
8
|
+
File.expand_path("../../../generators/goodmin/#{path}/templates", __FILE__)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def namespace
|
|
15
|
+
@_namespace ||= Rails::Generators.namespace
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def namespaced?
|
|
19
|
+
@_namespaced ||= namespace.present?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def namespaced_path
|
|
23
|
+
@_namespaced_path ||= begin
|
|
24
|
+
if namespaced?
|
|
25
|
+
namespace.name.split("::").map(&:underscore)
|
|
26
|
+
else
|
|
27
|
+
[]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def module_namespacing(&block)
|
|
33
|
+
content = capture(&block)
|
|
34
|
+
content = wrap_with_namespace(content) if namespaced?
|
|
35
|
+
concat(content)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def indent(content, multiplier = 2)
|
|
39
|
+
spaces = " " * multiplier
|
|
40
|
+
content.each_line.map { |line| line.blank? ? line : "#{spaces}#{line}" }.join
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def wrap_with_namespace(content)
|
|
44
|
+
content = indent(content).chomp
|
|
45
|
+
"module #{namespace.name}\n#{content}\nend\n"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require "goodmin/generators/base"
|
|
2
|
+
|
|
3
|
+
module Goodmin
|
|
4
|
+
module Generators
|
|
5
|
+
class NamedBase < Base
|
|
6
|
+
argument :name, type: :string
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def full_class_name
|
|
11
|
+
if namespaced?
|
|
12
|
+
"#{namespace}::#{class_name}"
|
|
13
|
+
else
|
|
14
|
+
class_name
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def class_name
|
|
19
|
+
@_class_name ||= name.classify
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def class_path
|
|
23
|
+
@_class_path ||= namespaced_path + name.classify.deconstantize.split("::").map(&:underscore)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def file_name
|
|
27
|
+
@_file_name ||= class_name.demodulize.underscore
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module Goodmin
|
|
2
|
+
module Helpers
|
|
3
|
+
module Application
|
|
4
|
+
# Renders the provided partial with locals if it exists, otherwise
|
|
5
|
+
# yields the given block. The lookup context call is cached for
|
|
6
|
+
# each partial.
|
|
7
|
+
# Returns the display name for a resource record by looking up its service
|
|
8
|
+
# class via ServiceLocator and calling display_name on it. Falls back to
|
|
9
|
+
# record.to_s if no service class is found.
|
|
10
|
+
def resource_display_name(record)
|
|
11
|
+
service_class = Goodmin::ServiceLocator.find_service_class_for(
|
|
12
|
+
record.class,
|
|
13
|
+
context_service_class: @resource_service&.class
|
|
14
|
+
)
|
|
15
|
+
service = service_class&.new
|
|
16
|
+
service ? service.display_name(record) : record.to_s
|
|
17
|
+
rescue StandardError
|
|
18
|
+
record.to_s
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def partial_override(partial, locals = {}, &block)
|
|
22
|
+
@_partial_override ||= {}
|
|
23
|
+
|
|
24
|
+
unless @_partial_override.key?(partial)
|
|
25
|
+
@_partial_override[partial] = lookup_context.exists?(partial, nil, true)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
if @_partial_override[partial]
|
|
29
|
+
render partial: partial, locals: locals
|
|
30
|
+
else
|
|
31
|
+
capture(&block)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Wraps the policy helper so that it is always accessible, even when
|
|
36
|
+
# authorization is not enabled. When that is the case, it returns a
|
|
37
|
+
# policy that always returns true.
|
|
38
|
+
def policy(resource)
|
|
39
|
+
if authorization_enabled?
|
|
40
|
+
controller.policy(resource)
|
|
41
|
+
else
|
|
42
|
+
Authorization::Policy.new(nil, nil, default: true)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Goodmin
|
|
2
|
+
module Helpers
|
|
3
|
+
module BatchActions
|
|
4
|
+
def batch_action_link(name, options)
|
|
5
|
+
return unless @resource_service.include_batch_action?(name)
|
|
6
|
+
|
|
7
|
+
link_to(
|
|
8
|
+
translate_scoped("batch_actions.labels.#{name}", default: name.to_s.titleize),
|
|
9
|
+
[*@resource_parents, @resource_class],
|
|
10
|
+
class: "btn btn-default hidden",
|
|
11
|
+
data: {
|
|
12
|
+
batch_actions_target: "actionLink",
|
|
13
|
+
action: "click->batch-actions#triggerAction",
|
|
14
|
+
confirm: options[:confirm] ? translate_scoped("batch_actions.confirm_message") : false,
|
|
15
|
+
value: name
|
|
16
|
+
}
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
module Goodmin
|
|
2
|
+
module Helpers
|
|
3
|
+
module Filters
|
|
4
|
+
def filter_form(url: params.to_unsafe_h)
|
|
5
|
+
builder = FormBuilders::FilterFormBuilder.new("", nil, self, {})
|
|
6
|
+
form_tag(url, method: :get, class: "form-inline") do
|
|
7
|
+
yield(builder)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
module FormBuilders
|
|
14
|
+
class FilterFormBuilder < ActionView::Helpers::FormBuilder
|
|
15
|
+
def filter_field(name, options, html_options = {})
|
|
16
|
+
case options[:as]
|
|
17
|
+
when :string
|
|
18
|
+
string_filter_field(name, options, html_options)
|
|
19
|
+
when :select
|
|
20
|
+
select_filter_field(name, options, html_options)
|
|
21
|
+
when :multiselect
|
|
22
|
+
multiselect_filter_field(name, options, html_options)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def string_filter_field(name, _options, html_options = {})
|
|
27
|
+
label_text = @template.translate_scoped("filters.labels.#{name}", default: name.to_s.titleize)
|
|
28
|
+
@template.content_tag(:div, class: "form-group filter") do
|
|
29
|
+
@template.concat(@template.label_tag(name, label_text, class: "control-label"))
|
|
30
|
+
@template.concat(@template.text_field_tag(
|
|
31
|
+
"filter[#{name}]",
|
|
32
|
+
default_filter_value(name),
|
|
33
|
+
{ placeholder: label_text, class: "form-control", id: name }.merge(html_options)
|
|
34
|
+
))
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def select_filter_field(name, options, html_options = {})
|
|
39
|
+
filter_select(
|
|
40
|
+
name, options, {
|
|
41
|
+
name: "filter[#{name}]",
|
|
42
|
+
data: {
|
|
43
|
+
placeholder: @template.translate_scoped("filters.select.placeholder.one")
|
|
44
|
+
}
|
|
45
|
+
}.deep_merge(html_options)
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def multiselect_filter_field(name, options, html_options = {})
|
|
50
|
+
filter_select(
|
|
51
|
+
name, {
|
|
52
|
+
include_hidden: false
|
|
53
|
+
}.deep_merge(options), {
|
|
54
|
+
name: "filter[#{name}][]",
|
|
55
|
+
multiple: true,
|
|
56
|
+
data: {
|
|
57
|
+
placeholder: @template.translate_scoped("filters.select.placeholder.many")
|
|
58
|
+
}
|
|
59
|
+
}.deep_merge(html_options)
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def apply_filters_button
|
|
64
|
+
submit @template.translate_scoped("filters.buttons.apply"), class: "btn btn-primary"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def clear_filters_button
|
|
68
|
+
@template.link_to(
|
|
69
|
+
@template.translate_scoped("filters.buttons.clear"),
|
|
70
|
+
@template.url_for(
|
|
71
|
+
@template.params.to_unsafe_h.slice(:scope, :order)
|
|
72
|
+
),
|
|
73
|
+
class: "btn btn-default"
|
|
74
|
+
)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def filter_select(name, options, html_options)
|
|
80
|
+
unless options[:collection].is_a? Proc
|
|
81
|
+
raise "A collection proc must be specified for select filters"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
options = options.dup
|
|
85
|
+
|
|
86
|
+
collection = options.delete(:collection).call
|
|
87
|
+
options.delete(:include_hidden)
|
|
88
|
+
|
|
89
|
+
label_text = @template.translate_scoped("filters.labels.#{name}", default: name.to_s.titleize)
|
|
90
|
+
|
|
91
|
+
choices =
|
|
92
|
+
if collection.is_a? ActiveRecord::Relation
|
|
93
|
+
@template.options_from_collection_for_select(
|
|
94
|
+
collection,
|
|
95
|
+
options.delete(:option_value),
|
|
96
|
+
options.delete(:option_text),
|
|
97
|
+
selected: default_filter_value(name)
|
|
98
|
+
)
|
|
99
|
+
else
|
|
100
|
+
@template.options_for_select(
|
|
101
|
+
collection,
|
|
102
|
+
selected: default_filter_value(name)
|
|
103
|
+
)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
blank_option = @template.content_tag(:option, "", value: "")
|
|
107
|
+
|
|
108
|
+
@template.content_tag(:div, class: "form-group filter") do
|
|
109
|
+
@template.concat(@template.label_tag(name, label_text, class: "control-label"))
|
|
110
|
+
@template.concat(@template.select_tag(
|
|
111
|
+
html_options[:name] || "filter[#{name}]",
|
|
112
|
+
@template.safe_join([blank_option, choices]),
|
|
113
|
+
{ class: "form-control", id: name }.merge(html_options.except(:name))
|
|
114
|
+
))
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def default_filter_value(name)
|
|
119
|
+
@template.params[:filter] ? @template.params[:filter][name] : nil
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module Goodmin
|
|
2
|
+
module Helpers
|
|
3
|
+
module Forms
|
|
4
|
+
def form_for(record, options = {}, &block)
|
|
5
|
+
super(record, {
|
|
6
|
+
url: [*@resource_parents, record]
|
|
7
|
+
}.merge(options), &block)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def render_form_nodes(nodes, f)
|
|
11
|
+
safe_join(nodes.map { |node| render_form_node(node, f) })
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def render_form_nodes_for(nodes, f, record:, resource_service:)
|
|
15
|
+
old_resource = @resource
|
|
16
|
+
old_resource_service = @resource_service
|
|
17
|
+
@resource = record
|
|
18
|
+
@resource_service = resource_service
|
|
19
|
+
render_form_nodes(nodes, f)
|
|
20
|
+
ensure
|
|
21
|
+
@resource = old_resource
|
|
22
|
+
@resource_service = old_resource_service
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def render_form_node(node, f)
|
|
28
|
+
case node
|
|
29
|
+
when Resources::AttributeNode
|
|
30
|
+
field = node.attribute.to_field(record: @resource, resource_service: @resource_service)
|
|
31
|
+
render partial: field.class.partial_form, locals: { field: field, f: f }
|
|
32
|
+
when Resources::HtmlNode
|
|
33
|
+
content_tag(node.tag, node.html_attrs) do
|
|
34
|
+
render_form_nodes(node.children, f)
|
|
35
|
+
end
|
|
36
|
+
when Resources::ComponentNode
|
|
37
|
+
node.component.render(self, f)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module Goodmin
|
|
2
|
+
module Helpers
|
|
3
|
+
module Navigation
|
|
4
|
+
def navbar_item(resource, url = resource, show: nil, icon: nil, **options)
|
|
5
|
+
show ||= lambda do
|
|
6
|
+
resource.is_a?(String) ? true : policy(resource).index?
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
return unless show.call
|
|
10
|
+
|
|
11
|
+
link_text =
|
|
12
|
+
if block_given?
|
|
13
|
+
capture do
|
|
14
|
+
yield
|
|
15
|
+
end
|
|
16
|
+
else
|
|
17
|
+
resource.respond_to?(:model_name) ? resource.model_name.human(count: :many) : resource
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
content_tag :li do
|
|
21
|
+
link_to url, options do
|
|
22
|
+
if icon.present?
|
|
23
|
+
concat content_tag :span, "", class: "glyphicon glyphicon-#{icon}"
|
|
24
|
+
concat " "
|
|
25
|
+
end
|
|
26
|
+
concat link_text
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def navbar_dropdown(title)
|
|
32
|
+
dropdown_toggle = link_to "#", class: "dropdown-toggle", data: { toggle: "dropdown" } do
|
|
33
|
+
concat "#{title} "
|
|
34
|
+
concat content_tag :span, "", class: "caret"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
dropdown_menu = content_tag :ul, class: "dropdown-menu" do
|
|
38
|
+
yield
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
content_tag :li, class: "dropdown" do
|
|
42
|
+
concat dropdown_toggle
|
|
43
|
+
concat dropdown_menu
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def navbar_divider
|
|
48
|
+
content_tag :li, "", class: "divider"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Goodmin
|
|
2
|
+
module Helpers
|
|
3
|
+
module Tables
|
|
4
|
+
def column_header(attribute)
|
|
5
|
+
if @resource_service.orderable_column?(attribute.name.to_s)
|
|
6
|
+
direction =
|
|
7
|
+
if params[:order].present?
|
|
8
|
+
if params[:order].match(/\A#{attribute.name.to_s}_(asc|desc)\z/)
|
|
9
|
+
$1 == "desc" ? "asc" : "desc"
|
|
10
|
+
elsif params[:order].match(/\A\w+_(asc|desc)\z/)
|
|
11
|
+
$1
|
|
12
|
+
else
|
|
13
|
+
"desc"
|
|
14
|
+
end
|
|
15
|
+
else
|
|
16
|
+
"desc"
|
|
17
|
+
end
|
|
18
|
+
link_to @resource_class.human_attribute_name(attribute.name.to_s), url_for(params.to_unsafe_h.merge(order: "#{attribute.name}_#{direction}"))
|
|
19
|
+
else
|
|
20
|
+
@resource_class.human_attribute_name(attribute.name.to_s)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Goodmin
|
|
2
|
+
module Helpers
|
|
3
|
+
module Translations
|
|
4
|
+
def translate_scoped(translate, scope: nil, default: nil, **options)
|
|
5
|
+
if @resource_class
|
|
6
|
+
scope ||= @resource_class.to_s.underscore
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
defaults = [
|
|
10
|
+
["goodmin", scope, translate].compact.join(".").to_sym,
|
|
11
|
+
["goodmin", translate].compact.join(".").to_sym,
|
|
12
|
+
default
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
t(defaults.shift, default: defaults, **options)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|