trestle 0.8.11 → 0.8.12
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/.travis.yml +11 -1
- data/README.md +8 -3
- data/app/assets/javascripts/trestle/components/_confirmation.js +22 -22
- data/app/assets/stylesheets/trestle/components/_dropdown.scss +28 -0
- data/app/assets/stylesheets/trestle/components/_fields.scss +5 -0
- data/app/assets/stylesheets/trestle/core/_defaults.scss +2 -0
- data/app/controllers/trestle/dashboard_controller.rb +1 -1
- data/app/helpers/trestle/i18n_helper.rb +6 -0
- data/app/helpers/trestle/panel_helper.rb +1 -1
- data/app/helpers/trestle/toolbars_helper.rb +2 -1
- data/app/views/layouts/trestle/admin.html.erb +5 -3
- data/app/views/trestle/application/_dialog.html.erb +2 -2
- data/app/views/trestle/application/_header.html.erb +2 -2
- data/app/views/trestle/shared/_sidebar.html.erb +1 -1
- data/config/locales/en.yml +1 -0
- data/config/locales/ko.rb +18 -0
- data/config/locales/ko.yml +96 -0
- data/gemfiles/rails-4.2.gemfile +1 -1
- data/gemfiles/rails-5.0.gemfile +1 -1
- data/gemfiles/rails-5.1.gemfile +1 -1
- data/gemfiles/rails-5.2.gemfile +1 -1
- data/gemfiles/rails-edge.gemfile +1 -1
- data/lib/generators/trestle/install/templates/trestle.rb.erb +4 -0
- data/lib/trestle.rb +5 -3
- data/lib/trestle/adapters/adapter.rb +2 -17
- data/lib/trestle/configuration.rb +3 -0
- data/lib/trestle/evaluation_context.rb +24 -0
- data/lib/trestle/form/fields/password_field.rb +4 -0
- data/lib/trestle/form/fields/select.rb +3 -1
- data/lib/trestle/form/renderer.rb +1 -0
- data/lib/trestle/navigation.rb +6 -6
- data/lib/trestle/navigation/block.rb +6 -6
- data/lib/trestle/resource.rb +6 -1
- data/lib/trestle/resource/builder.rb +6 -5
- data/lib/trestle/scopes.rb +23 -0
- data/lib/trestle/scopes/block.rb +38 -0
- data/lib/trestle/scopes/scope.rb +49 -0
- data/lib/trestle/toolbar.rb +11 -0
- data/lib/trestle/toolbar/builder.rb +8 -27
- data/lib/trestle/toolbar/item.rb +118 -0
- data/lib/trestle/toolbar/menu.rb +63 -0
- data/lib/trestle/version.rb +1 -1
- data/trestle.gemspec +4 -4
- metadata +34 -28
- data/lib/trestle/scope.rb +0 -47
@@ -15,6 +15,10 @@ Trestle.configure do |config|
|
|
15
15
|
#
|
16
16
|
# config.site_logo_small = "logo-small.png"
|
17
17
|
|
18
|
+
# Speficy a favicon to be used within the admin.
|
19
|
+
#
|
20
|
+
# config.favicon = "favicon.ico"
|
21
|
+
|
18
22
|
# Set the text shown in the page footer within the admin.
|
19
23
|
# Defaults to 'Powered by Trestle'.
|
20
24
|
#
|
data/lib/trestle.rb
CHANGED
@@ -17,6 +17,7 @@ module Trestle
|
|
17
17
|
autoload :Configurable
|
18
18
|
autoload :Configuration
|
19
19
|
autoload :Display
|
20
|
+
autoload :EvaluationContext
|
20
21
|
autoload :Form
|
21
22
|
autoload :Hook
|
22
23
|
autoload :ModelName
|
@@ -24,7 +25,7 @@ module Trestle
|
|
24
25
|
autoload :Options
|
25
26
|
autoload :Reloader
|
26
27
|
autoload :Resource
|
27
|
-
autoload :
|
28
|
+
autoload :Scopes
|
28
29
|
autoload :Tab
|
29
30
|
autoload :Table
|
30
31
|
autoload :Toolbar
|
@@ -57,8 +58,9 @@ module Trestle
|
|
57
58
|
config.configure(&block)
|
58
59
|
end
|
59
60
|
|
60
|
-
def self.navigation
|
61
|
-
|
61
|
+
def self.navigation(context)
|
62
|
+
blocks = config.menus + admins.values.map(&:menu).compact
|
63
|
+
Navigation.build(blocks, context)
|
62
64
|
end
|
63
65
|
end
|
64
66
|
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module Trestle
|
2
2
|
module Adapters
|
3
3
|
class Adapter
|
4
|
+
include EvaluationContext
|
5
|
+
|
4
6
|
attr_reader :admin
|
5
7
|
delegate :model, to: :admin
|
6
8
|
|
@@ -179,23 +181,6 @@ module Trestle
|
|
179
181
|
def default_form_attributes
|
180
182
|
raise NotImplementedError
|
181
183
|
end
|
182
|
-
|
183
|
-
protected
|
184
|
-
# Missing methods are called on the given context if available.
|
185
|
-
#
|
186
|
-
# We include private methods as methods such as current_user
|
187
|
-
# are usually declared as private or protected.
|
188
|
-
def method_missing(name, *args, &block)
|
189
|
-
if @context && @context.respond_to?(name, true)
|
190
|
-
@context.send(name, *args, &block)
|
191
|
-
else
|
192
|
-
super
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
def respond_to_missing?(name, include_private=false)
|
197
|
-
(@context && @context.respond_to?(name, true)) || super
|
198
|
-
end
|
199
184
|
end
|
200
185
|
end
|
201
186
|
end
|
@@ -13,6 +13,9 @@ module Trestle
|
|
13
13
|
# Custom image for the collapsed/tablet navigation
|
14
14
|
option :site_logo_small
|
15
15
|
|
16
|
+
# Favicon used within the admin
|
17
|
+
option :favicon
|
18
|
+
|
16
19
|
# Text shown in the admin page footer
|
17
20
|
option :footer, -> { I18n.t("trestle.footer", default: "Powered by Trestle") }
|
18
21
|
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Trestle
|
2
|
+
# This module facilitiates the delegation of missing methods to a given @context variable.
|
3
|
+
#
|
4
|
+
# This allows code such as adapter and navigation blocks to be evaluated with access to methods from
|
5
|
+
# both the Adapter/Navigation instance, as well as the controller/view from where they are invoked.
|
6
|
+
module EvaluationContext
|
7
|
+
protected
|
8
|
+
# Missing methods are called on the given context if available.
|
9
|
+
#
|
10
|
+
# We include private methods as methods such as current_user
|
11
|
+
# are usually declared as private or protected.
|
12
|
+
def method_missing(name, *args, &block)
|
13
|
+
if @context && @context.respond_to?(name, true)
|
14
|
+
@context.send(name, *args, &block)
|
15
|
+
else
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def respond_to_missing?(name, include_private=false)
|
21
|
+
(@context && @context.respond_to?(name, true)) || super
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -2,6 +2,10 @@ class Trestle::Form::Fields::PasswordField < Trestle::Form::Fields::FormControl
|
|
2
2
|
def field
|
3
3
|
builder.raw_password_field(name, options)
|
4
4
|
end
|
5
|
+
|
6
|
+
def defaults
|
7
|
+
super.merge(autocomplete: "new-password")
|
8
|
+
end
|
5
9
|
end
|
6
10
|
|
7
11
|
Trestle::Form::Builder.register(:password_field, Trestle::Form::Fields::PasswordField)
|
@@ -7,7 +7,9 @@ module Trestle
|
|
7
7
|
def initialize(builder, template, name, choices=nil, options={}, html_options={}, &block)
|
8
8
|
super(builder, template, name, options, &block)
|
9
9
|
|
10
|
-
@choices =
|
10
|
+
@choices = choices || default_choices
|
11
|
+
@choices = Choices.new(@choices) if @choices.nil? || @choices.is_a?(Enumerable)
|
12
|
+
|
11
13
|
@html_options = default_html_options.merge(html_options)
|
12
14
|
end
|
13
15
|
|
@@ -3,6 +3,7 @@ module Trestle
|
|
3
3
|
class Renderer
|
4
4
|
include ::ActionView::Context
|
5
5
|
include ::ActionView::Helpers::CaptureHelper
|
6
|
+
include HookHelper
|
6
7
|
|
7
8
|
# Whitelisted helpers will concatenate their result to the output buffer when called.
|
8
9
|
WHITELISTED_HELPERS = [:row, :col, :render, :tab, :table, :divider, :h1, :h2, :h3, :h4, :h5, :h6, :panel, :well]
|
data/lib/trestle/navigation.rb
CHANGED
@@ -29,12 +29,12 @@ module Trestle
|
|
29
29
|
sorted.first.first if sorted.any?
|
30
30
|
end
|
31
31
|
|
32
|
-
def
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
32
|
+
def self.build(blocks, context)
|
33
|
+
new(blocks.map { |block|
|
34
|
+
block.items(context)
|
35
|
+
}.flatten.select { |item|
|
36
|
+
item.visible?(context)
|
37
|
+
})
|
38
38
|
end
|
39
39
|
|
40
40
|
private
|
@@ -8,19 +8,19 @@ module Trestle
|
|
8
8
|
@block = block
|
9
9
|
end
|
10
10
|
|
11
|
-
def items
|
12
|
-
context =
|
11
|
+
def items(context)
|
12
|
+
context = Evaluator.new(@admin, context)
|
13
13
|
context.instance_exec(@admin, &block)
|
14
14
|
context.items
|
15
15
|
end
|
16
16
|
|
17
|
-
class
|
18
|
-
include
|
17
|
+
class Evaluator
|
18
|
+
include EvaluationContext
|
19
19
|
|
20
20
|
attr_reader :items
|
21
21
|
|
22
|
-
def initialize(admin=nil)
|
23
|
-
@admin = admin
|
22
|
+
def initialize(admin=nil, context=nil)
|
23
|
+
@admin, @context = admin, context
|
24
24
|
@items = []
|
25
25
|
end
|
26
26
|
|
data/lib/trestle/resource.rb
CHANGED
@@ -50,6 +50,11 @@ module Trestle
|
|
50
50
|
Collection.new(self, options).prepare(params)
|
51
51
|
end
|
52
52
|
|
53
|
+
# Evaluates the admin's scope block(s) and returns a hash of Scope objects keyed by the scope name
|
54
|
+
def scopes
|
55
|
+
@scopes ||= self.class.scopes.evaluate(@context)
|
56
|
+
end
|
57
|
+
|
53
58
|
class << self
|
54
59
|
# Deprecated: use instance method instead
|
55
60
|
def prepare_collection(params, options={})
|
@@ -57,7 +62,7 @@ module Trestle
|
|
57
62
|
end
|
58
63
|
|
59
64
|
def scopes
|
60
|
-
@scopes ||=
|
65
|
+
@scopes ||= Scopes.new(self)
|
61
66
|
end
|
62
67
|
|
63
68
|
def column_sorts
|
@@ -83,13 +83,14 @@ module Trestle
|
|
83
83
|
admin.define_adapter_method(:count, &block)
|
84
84
|
end
|
85
85
|
|
86
|
+
def scopes(&block)
|
87
|
+
admin.scopes.append(&block)
|
88
|
+
end
|
89
|
+
|
86
90
|
def scope(name, scope=nil, options={}, &block)
|
87
|
-
|
88
|
-
options
|
89
|
-
scope = nil
|
91
|
+
scopes do
|
92
|
+
scope(name, scope, options, &block)
|
90
93
|
end
|
91
|
-
|
92
|
-
admin.scopes[name] = Scope.new(admin, name, options, &(scope || block))
|
93
94
|
end
|
94
95
|
|
95
96
|
def return_to(options={}, &block)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Trestle
|
2
|
+
class Scopes
|
3
|
+
extend ActiveSupport::Autoload
|
4
|
+
|
5
|
+
autoload :Block
|
6
|
+
autoload :Scope
|
7
|
+
|
8
|
+
attr_reader :admin, :blocks
|
9
|
+
|
10
|
+
def initialize(admin)
|
11
|
+
@admin = admin
|
12
|
+
@blocks = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def append(&block)
|
16
|
+
@blocks << Block.new(admin, &block)
|
17
|
+
end
|
18
|
+
|
19
|
+
def evaluate(context)
|
20
|
+
@blocks.map { |block| block.scopes(context) }.flatten.index_by(&:name)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Trestle
|
2
|
+
class Scopes
|
3
|
+
class Block
|
4
|
+
attr_reader :block, :admin
|
5
|
+
|
6
|
+
def initialize(admin=nil, &block)
|
7
|
+
@admin = admin
|
8
|
+
@block = block
|
9
|
+
end
|
10
|
+
|
11
|
+
def scopes(context)
|
12
|
+
context = Evaluator.new(@admin, context)
|
13
|
+
context.instance_exec(@admin, &block)
|
14
|
+
context.scopes
|
15
|
+
end
|
16
|
+
|
17
|
+
class Evaluator
|
18
|
+
include EvaluationContext
|
19
|
+
|
20
|
+
attr_reader :scopes
|
21
|
+
|
22
|
+
def initialize(admin, context=nil)
|
23
|
+
@admin, @context = admin, context
|
24
|
+
@scopes = []
|
25
|
+
end
|
26
|
+
|
27
|
+
def scope(name, scope=nil, options={}, &block)
|
28
|
+
if scope.is_a?(Hash)
|
29
|
+
options = scope
|
30
|
+
scope = nil
|
31
|
+
end
|
32
|
+
|
33
|
+
scopes << Scope.new(@admin, name, options, &(scope || block))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Trestle
|
2
|
+
class Scopes
|
3
|
+
class Scope
|
4
|
+
attr_reader :name, :options, :block
|
5
|
+
|
6
|
+
def initialize(admin, name, options={}, &block)
|
7
|
+
@admin, @name, @options, @block = admin, name, options, block
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_param
|
11
|
+
name
|
12
|
+
end
|
13
|
+
|
14
|
+
def label
|
15
|
+
@options[:label] || I18n.t("admin.scopes.#{name}", default: name.to_s.humanize.titleize)
|
16
|
+
end
|
17
|
+
|
18
|
+
def default?
|
19
|
+
@options[:default] == true
|
20
|
+
end
|
21
|
+
|
22
|
+
def apply(collection)
|
23
|
+
if @block
|
24
|
+
if @block.arity == 1
|
25
|
+
@admin.instance_exec(collection, &@block)
|
26
|
+
else
|
27
|
+
@admin.instance_exec(&@block)
|
28
|
+
end
|
29
|
+
else
|
30
|
+
collection.public_send(name)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def count(collection)
|
35
|
+
@admin.count(@admin.merge_scopes(collection, apply(collection)))
|
36
|
+
end
|
37
|
+
|
38
|
+
def active?(params)
|
39
|
+
active_scopes = Array(params[:scope])
|
40
|
+
|
41
|
+
if active_scopes.any?
|
42
|
+
active_scopes.include?(to_param.to_s)
|
43
|
+
else
|
44
|
+
default?
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/trestle/toolbar.rb
CHANGED
@@ -4,9 +4,20 @@ module Trestle
|
|
4
4
|
|
5
5
|
autoload :Builder
|
6
6
|
autoload :Context
|
7
|
+
autoload :Menu
|
8
|
+
|
9
|
+
autoload_at "trestle/toolbar/item" do
|
10
|
+
autoload :Button
|
11
|
+
autoload :Dropdown
|
12
|
+
autoload :Link
|
13
|
+
end
|
7
14
|
|
8
15
|
def initialize(builder=Builder)
|
9
16
|
@builder = builder
|
17
|
+
clear!
|
18
|
+
end
|
19
|
+
|
20
|
+
def clear!
|
10
21
|
@blocks = []
|
11
22
|
end
|
12
23
|
|
@@ -5,17 +5,16 @@ module Trestle
|
|
5
5
|
@template = template
|
6
6
|
end
|
7
7
|
|
8
|
-
def button(
|
9
|
-
|
10
|
-
|
11
|
-
button_tag(button_label(content, options), options)
|
8
|
+
def button(label, options={}, &block)
|
9
|
+
Button.new(@template, label, options, &block)
|
12
10
|
end
|
13
11
|
|
14
|
-
def link(
|
15
|
-
|
16
|
-
|
12
|
+
def link(label, instance_or_url={}, options={}, &block)
|
13
|
+
Link.new(@template, label, instance_or_url, options, &block)
|
14
|
+
end
|
17
15
|
|
18
|
-
|
16
|
+
def dropdown(label=nil, options={}, &block)
|
17
|
+
Dropdown.new(@template, label, options, &block)
|
19
18
|
end
|
20
19
|
|
21
20
|
# Only methods explicitly tagged as builder methods will be automatically
|
@@ -28,25 +27,7 @@ module Trestle
|
|
28
27
|
self.builder_methods += methods
|
29
28
|
end
|
30
29
|
|
31
|
-
builder_method :button, :link
|
32
|
-
|
33
|
-
private
|
34
|
-
delegate :admin_link_to, :button_tag, :content_tag, :safe_join, :icon, to: :@template
|
35
|
-
|
36
|
-
def button_classes_from_options(options)
|
37
|
-
classes = (options[:class] || "").split("\s")
|
38
|
-
classes.unshift("btn-#{options.delete(:style) || "default"}")
|
39
|
-
classes.unshift("btn") unless classes.include?("btn")
|
40
|
-
classes.push("has-icon") if options[:icon]
|
41
|
-
classes
|
42
|
-
end
|
43
|
-
|
44
|
-
def button_label(content, options)
|
45
|
-
icon = icon(options.delete(:icon)) if options[:icon]
|
46
|
-
label = content_tag(:span, content, class: "btn-label")
|
47
|
-
|
48
|
-
safe_join([icon, label].compact, " ")
|
49
|
-
end
|
30
|
+
builder_method :button, :link, :dropdown
|
50
31
|
end
|
51
32
|
end
|
52
33
|
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module Trestle
|
2
|
+
class Toolbar
|
3
|
+
class Item
|
4
|
+
attr_reader :label, :menu
|
5
|
+
|
6
|
+
delegate :admin_link_to, :button_tag, :content_tag, :safe_join, :icon, to: :@template
|
7
|
+
|
8
|
+
def initialize(template, label, options={}, &block)
|
9
|
+
@template = template
|
10
|
+
@label, @options, @block = label, options
|
11
|
+
|
12
|
+
@menu = Menu.new(template)
|
13
|
+
@menu.build(&block) if block_given?
|
14
|
+
|
15
|
+
@icon = options.delete(:icon)
|
16
|
+
@style = options.delete(:style)
|
17
|
+
end
|
18
|
+
|
19
|
+
def ==(other)
|
20
|
+
to_s == other.to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
if menu.items.any?
|
25
|
+
content_tag(:div, class: "btn-group", role: "group") do
|
26
|
+
safe_join([render, render_menu], "\n")
|
27
|
+
end
|
28
|
+
else
|
29
|
+
render
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def render
|
34
|
+
raise NotImplementedError
|
35
|
+
end
|
36
|
+
|
37
|
+
def render_menu
|
38
|
+
[
|
39
|
+
menu.render_toggle(class: button_style_classes),
|
40
|
+
menu.render_items
|
41
|
+
]
|
42
|
+
end
|
43
|
+
|
44
|
+
def options
|
45
|
+
@options.merge(class: button_classes)
|
46
|
+
end
|
47
|
+
|
48
|
+
def button_classes
|
49
|
+
classes = (@options[:class] || "").split(/\s/)
|
50
|
+
classes.push(*button_style_classes)
|
51
|
+
classes.push("has-icon") if @icon
|
52
|
+
classes.uniq
|
53
|
+
end
|
54
|
+
|
55
|
+
def button_label(content, options)
|
56
|
+
icon = icon(@icon) if @icon
|
57
|
+
label = content_tag(:span, content, class: "btn-label")
|
58
|
+
|
59
|
+
safe_join([icon, label].compact, " ")
|
60
|
+
end
|
61
|
+
|
62
|
+
def button_style
|
63
|
+
@style || "default"
|
64
|
+
end
|
65
|
+
|
66
|
+
def button_style_classes
|
67
|
+
["btn", "btn-#{button_style}"]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class Button < Item
|
72
|
+
def render
|
73
|
+
button_tag(button_label(label, options), options)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class Link < Item
|
78
|
+
attr_reader :instance_or_url
|
79
|
+
|
80
|
+
def initialize(template, label, instance_or_url={}, options={}, &block)
|
81
|
+
if instance_or_url.is_a?(Hash)
|
82
|
+
super(template, label, instance_or_url, &block)
|
83
|
+
else
|
84
|
+
super(template, label, options, &block)
|
85
|
+
@instance_or_url = instance_or_url
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def render
|
90
|
+
if @instance_or_url
|
91
|
+
admin_link_to(button_label(label, options), instance_or_url, options)
|
92
|
+
else
|
93
|
+
admin_link_to(button_label(label, options), options)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class Dropdown < Button
|
99
|
+
def options
|
100
|
+
super.merge(type: "button", data: { toggle: "dropdown" })
|
101
|
+
end
|
102
|
+
|
103
|
+
def label
|
104
|
+
safe_join([
|
105
|
+
super, content_tag(:span, "", class: "caret")
|
106
|
+
], " ")
|
107
|
+
end
|
108
|
+
|
109
|
+
def button_style_classes
|
110
|
+
super + ["dropdown-toggle"]
|
111
|
+
end
|
112
|
+
|
113
|
+
def render_menu
|
114
|
+
[menu.render_items]
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|