netzke-basepack 0.12.9 → 1.0.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +75 -44
- data/Gemfile +4 -2
- data/LICENSE +2 -6
- data/README.md +22 -24
- data/javascripts/basepack.js +0 -8
- data/javascripts/{columns.js → grid/columns.js} +59 -71
- data/javascripts/grid/event_handlers.js +218 -0
- data/javascripts/netzkeremotecombo.js +5 -13
- data/javascripts/tristate.js +62 -0
- data/javascripts/xdatetime.js +8 -37
- data/lib/netzke-basepack.rb +3 -2
- data/lib/netzke/basepack.rb +1 -1
- data/lib/netzke/basepack/action_column.rb +6 -23
- data/lib/netzke/basepack/active_record.rb +0 -6
- data/lib/netzke/basepack/attr_config.rb +20 -11
- data/lib/netzke/basepack/attribute_config.rb +10 -0
- data/lib/netzke/basepack/attributes.rb +196 -0
- data/lib/netzke/basepack/column_config.rb +47 -39
- data/lib/netzke/basepack/columns.rb +127 -97
- data/lib/netzke/basepack/data_accessor.rb +7 -48
- data/lib/netzke/basepack/data_adapters/abstract_adapter.rb +15 -17
- data/lib/netzke/basepack/data_adapters/active_record_adapter.rb +111 -90
- data/lib/netzke/basepack/dynamic_tab_panel.rb +3 -5
- data/lib/netzke/basepack/dynamic_tab_panel/{javascripts → client}/dynamic_tab_panel.js +6 -5
- data/lib/netzke/basepack/field_config.rb +30 -19
- data/lib/netzke/basepack/fields.rb +22 -12
- data/lib/netzke/basepack/grid_live_search.rb +1 -4
- data/lib/netzke/basepack/grid_live_search/{javascripts → client}/grid_live_search.js +9 -7
- data/lib/netzke/basepack/item_persistence.rb +3 -3
- data/lib/netzke/basepack/item_persistence/events_plugin.rb +8 -10
- data/lib/netzke/basepack/paging_form.rb +7 -11
- data/lib/netzke/basepack/paging_form/{javascripts → client}/paging_form.js +4 -4
- data/lib/netzke/basepack/query_builder.rb +12 -10
- data/lib/netzke/basepack/query_builder/{javascripts → client}/query_builder.js +14 -12
- data/lib/netzke/basepack/record_form_window.rb +8 -8
- data/lib/netzke/basepack/search_panel.rb +4 -6
- data/lib/netzke/basepack/search_panel/{javascripts → client}/condition_field.js +13 -16
- data/lib/netzke/basepack/search_panel/{javascripts → client}/search_panel.js +7 -7
- data/lib/netzke/basepack/search_window.rb +6 -6
- data/lib/netzke/basepack/simple_app/{javascripts → client}/statusbar_ext.js +0 -0
- data/lib/netzke/basepack/version.rb +1 -1
- data/lib/netzke/form/base.rb +166 -0
- data/lib/netzke/{basepack/form/javascripts/form.js → form/base/client/base.js} +77 -38
- data/lib/netzke/form/base/client/readonly_mode.css +4 -0
- data/lib/netzke/{basepack/form/javascripts → form/base/client}/readonly_mode.js +5 -5
- data/lib/netzke/form/endpoints.rb +33 -0
- data/lib/netzke/form/services.rb +74 -0
- data/lib/netzke/grid/actions.rb +52 -0
- data/lib/netzke/grid/base.rb +289 -0
- data/lib/netzke/{basepack/grid/javascripts → grid/base/client}/advanced_search.js +5 -1
- data/lib/netzke/{basepack/grid/javascripts/grid.js → grid/base/client/base.js} +61 -53
- data/lib/netzke/{basepack/grid/javascripts → grid/base/client}/extensions.js +19 -13
- data/lib/netzke/{basepack/grid/javascripts → grid/base/client}/remember_selection.js +0 -1
- data/lib/netzke/grid/client.rb +8 -0
- data/lib/netzke/grid/components.rb +55 -0
- data/lib/netzke/grid/configuration.rb +72 -0
- data/lib/netzke/grid/endpoints.rb +99 -0
- data/lib/netzke/grid/permissions.rb +18 -0
- data/lib/netzke/grid/services.rb +141 -0
- data/lib/netzke/tree/base.rb +173 -0
- data/lib/netzke/{basepack/tree/javascripts/tree.js → tree/base/client/base.js} +55 -26
- data/lib/netzke/{basepack/tree/javascripts → tree/base/client}/extensions.js +7 -7
- data/lib/netzke/tree/endpoints.rb +34 -0
- data/lib/netzke/{basepack/viewport.rb → viewport/base.rb} +3 -3
- data/lib/netzke/{basepack/window.rb → window/base.rb} +7 -8
- data/lib/netzke/window/base/client/base.js +26 -0
- data/locales/de.yml +49 -33
- data/locales/en.yml +32 -39
- data/locales/es.yml +39 -25
- data/locales/nl.yml +39 -25
- data/locales/ru.yml +38 -25
- data/locales/uk.yml +40 -26
- data/stylesheets/basepack.css +10 -0
- metadata +48 -45
- data/javascripts/mixins/grid_event_handlers.js +0 -139
- data/lib/netzke/basepack/accordion.rb +0 -45
- data/lib/netzke/basepack/active_record/relation_extensions.rb +0 -27
- data/lib/netzke/basepack/form.rb +0 -131
- data/lib/netzke/basepack/form/endpoints.rb +0 -35
- data/lib/netzke/basepack/form/services.rb +0 -74
- data/lib/netzke/basepack/form/stylesheets/readonly_mode.css +0 -14
- data/lib/netzke/basepack/grid.rb +0 -570
- data/lib/netzke/basepack/grid/endpoints.rb +0 -111
- data/lib/netzke/basepack/grid/javascripts/edit_in_form.js +0 -51
- data/lib/netzke/basepack/grid/services.rb +0 -148
- data/lib/netzke/basepack/tab_panel.rb +0 -22
- data/lib/netzke/basepack/tab_panel/javascripts/tab_panel.js +0 -11
- data/lib/netzke/basepack/tree.rb +0 -269
- data/lib/netzke/basepack/window/javascripts/window.js +0 -26
- data/lib/netzke/basepack/wrap_lazy_loaded.rb +0 -29
@@ -1,45 +0,0 @@
|
|
1
|
-
module Netzke
|
2
|
-
module Basepack
|
3
|
-
# A panel with the 'accordion' layout. By default, lazily loads its nested components. For example:
|
4
|
-
#
|
5
|
-
# class MyAccordion < Netzke::Basepack::Accordion
|
6
|
-
# def configure(c)
|
7
|
-
# super
|
8
|
-
# c.items = [{
|
9
|
-
# # just an Ext panel
|
10
|
-
# :html => "I'm a simple Ext.Panel",
|
11
|
-
# :title => "Panel One"
|
12
|
-
# },{
|
13
|
-
# # a Netzke component
|
14
|
-
# :component => :my_component,
|
15
|
-
# :title => "Panel Two"
|
16
|
-
# }]
|
17
|
-
# end
|
18
|
-
#
|
19
|
-
# component :my_component
|
20
|
-
# end
|
21
|
-
class Accordion < Netzke::Base
|
22
|
-
|
23
|
-
include WrapLazyLoaded
|
24
|
-
|
25
|
-
js_configure do |c|
|
26
|
-
c.layout = :accordion
|
27
|
-
|
28
|
-
c.init_component = <<-JS
|
29
|
-
function(params){
|
30
|
-
this.callParent();
|
31
|
-
this.items.each(function(item){
|
32
|
-
item.on('expand', function(i){
|
33
|
-
if (i && i.wrappedComponent && !i.items.first() && !i.beingLoaded) {
|
34
|
-
i.beingLoaded = true; // prevent more than one request per panel in case of fast clicking
|
35
|
-
this.netzkeLoadComponent(i.wrappedComponent, {container: i.id, callback: function(){i.beingLoaded = false}});
|
36
|
-
}
|
37
|
-
}, this);
|
38
|
-
}, this);
|
39
|
-
}
|
40
|
-
JS
|
41
|
-
end
|
42
|
-
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
module Netzke
|
2
|
-
module Basepack
|
3
|
-
module ActiveRecord
|
4
|
-
module RelationExtensions
|
5
|
-
def extend_with(*params)
|
6
|
-
scope = params.shift
|
7
|
-
case scope
|
8
|
-
when Symbol # model's scope
|
9
|
-
self.send(scope, *params)
|
10
|
-
when String # SQL query or SQL query with params (e.g. ["created_at < ?", 1.day.ago])
|
11
|
-
params.empty? ? self.where(scope) : self.where([scope, *params])
|
12
|
-
when Array
|
13
|
-
self.extend_with(*scope)
|
14
|
-
when Hash # conditions hash
|
15
|
-
self.where(scope)
|
16
|
-
when Proc # receives a relation, must return a relation
|
17
|
-
scope.call(self)
|
18
|
-
when nil
|
19
|
-
self
|
20
|
-
else
|
21
|
-
raise ArgumentError, "Wrong parameter type for ActiveRecord::Relation#extend_with (#{scope.class.name})"
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
data/lib/netzke/basepack/form.rb
DELETED
@@ -1,131 +0,0 @@
|
|
1
|
-
module Netzke
|
2
|
-
module Basepack
|
3
|
-
# Ext.form.Panel-based component
|
4
|
-
#
|
5
|
-
# == Netzke-specific config options
|
6
|
-
#
|
7
|
-
# * +model+ - name of the ActiveRecord model that provides data to this Grid.
|
8
|
-
# * +record+ - record to be displayd in the form. Takes precedence over +:record_id+
|
9
|
-
# * +record_id+ - id of the record to be displayd in the form. Also see +:record+
|
10
|
-
# * +items+ - the layout of the fields as an array. See "Layout configuration".
|
11
|
-
# * +mode+ - render mode, accepted options:
|
12
|
-
# * +lockable+ - makes the form panel load initially in "display mode", then lets "unlock" it, change the values, and "lock" it again, while updating the values on the server
|
13
|
-
# * +updateMask+ - +Ext.LoadMask+ config options for the mask shown while the form is submitting its values
|
14
|
-
#
|
15
|
-
# === Layout configuration
|
16
|
-
#
|
17
|
-
# The layout of the form is configured by supplying the +item+ config option, same way it would be configured in Ext (thus allowing for complex form layouts). Form will expand fields by looking at their names (unless +no_binding+ set to +true+ is specified for a specific field).
|
18
|
-
#
|
19
|
-
# == Endpoints
|
20
|
-
# Form implements the following endpoints:
|
21
|
-
#
|
22
|
-
# * +netzke_load+ - loads a record with a given id from the server, e.g.:
|
23
|
-
#
|
24
|
-
# someForm.netzkeLoad({id: 100});
|
25
|
-
#
|
26
|
-
# * +netzke_submit+ - gets called when the form gets submitted (e.g. by pressing the Apply button, or by calling onApply)
|
27
|
-
# * +get_combobox_options+ - gets called when a 'remote' combobox field gets expanded
|
28
|
-
class Form < Netzke::Base
|
29
|
-
include self::Endpoints
|
30
|
-
include self::Services
|
31
|
-
include Netzke::Basepack::Fields
|
32
|
-
include Netzke::Basepack::DataAccessor
|
33
|
-
include Netzke::Core::ConfigToDslDelegator
|
34
|
-
|
35
|
-
js_configure do |c|
|
36
|
-
c.extend = "Ext.form.Panel"
|
37
|
-
c.mixin
|
38
|
-
c.require :readonly_mode
|
39
|
-
end
|
40
|
-
|
41
|
-
delegates_to_dsl :model, :record_id
|
42
|
-
|
43
|
-
def js_configure(c)
|
44
|
-
super
|
45
|
-
|
46
|
-
configure_locked(c)
|
47
|
-
configure_bbar(c)
|
48
|
-
configure_apply_on_return(c)
|
49
|
-
|
50
|
-
if data_adapter
|
51
|
-
c.pri = data_adapter.primary_key
|
52
|
-
end
|
53
|
-
|
54
|
-
if !c.multi_edit
|
55
|
-
c.record = js_record_data if record
|
56
|
-
else
|
57
|
-
c.record_id = c.record = nil if c.multi_edit # never set record_id in multi-edit mode
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
action :apply do |a|
|
62
|
-
a.icon = :tick
|
63
|
-
end
|
64
|
-
|
65
|
-
action :edit do |a|
|
66
|
-
a.icon = :pencil
|
67
|
-
end
|
68
|
-
|
69
|
-
action :cancel do |a|
|
70
|
-
a.icon = :cancel
|
71
|
-
end
|
72
|
-
|
73
|
-
def configure_locked(c)
|
74
|
-
c[:locked] = c[:locked].nil? ? (c[:mode] == :lockable) : c[:locked]
|
75
|
-
end
|
76
|
-
|
77
|
-
def configure_bbar(c)
|
78
|
-
c[:bbar] = ["->", :apply] if c[:bbar].nil? && !c[:read_only]
|
79
|
-
end
|
80
|
-
|
81
|
-
def configure_apply_on_return(c)
|
82
|
-
c[:apply_on_return] = c[:apply_on_return].nil? ? true : !!c[:apply_on_return]
|
83
|
-
end
|
84
|
-
|
85
|
-
# Extra JavaScripts and stylesheets
|
86
|
-
css_configure do |c|
|
87
|
-
c.require :readonly_mode
|
88
|
-
end
|
89
|
-
|
90
|
-
# A hash of record data including the meta field
|
91
|
-
def js_record_data
|
92
|
-
data_adapter.record_to_hash(record, fields.values).merge(:meta => meta_field).netzke_literalize_keys
|
93
|
-
end
|
94
|
-
|
95
|
-
def record
|
96
|
-
@record ||= config[:record] || config[:record_id] && data_adapter.find_record(config[:record_id])
|
97
|
-
end
|
98
|
-
|
99
|
-
protected
|
100
|
-
|
101
|
-
def normalize_config
|
102
|
-
config.items = items
|
103
|
-
@fields_from_items = {} # will be built during execution of `super`
|
104
|
-
super
|
105
|
-
end
|
106
|
-
|
107
|
-
def self.server_side_config_options
|
108
|
-
super + [:scope]
|
109
|
-
end
|
110
|
-
|
111
|
-
def meta_field
|
112
|
-
{}.tap do |res|
|
113
|
-
assoc_values = get_association_values
|
114
|
-
res[:association_values] = assoc_values.netzke_literalize_keys if record && !assoc_values.empty?
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def get_association_values
|
119
|
-
fields_that_need_associated_values = fields.select{ |k,v| k.to_s.index("__") && !fields[k][:nested_attribute] }
|
120
|
-
# Take care of Ruby 1.8.7
|
121
|
-
if fields_that_need_associated_values.is_a?(Array)
|
122
|
-
fields_that_need_associated_values = fields_that_need_associated_values.inject({}){|r,(k,v)| r.merge(k => v)}
|
123
|
-
end
|
124
|
-
|
125
|
-
fields_that_need_associated_values.each_pair.inject({}) do |r,(k,v)|
|
126
|
-
r.merge(k => data_adapter.record_value_for_attribute(record, fields_that_need_associated_values[k], true))
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
@@ -1,35 +0,0 @@
|
|
1
|
-
module Netzke
|
2
|
-
module Basepack
|
3
|
-
class Form < Netzke::Base
|
4
|
-
module Endpoints
|
5
|
-
extend ActiveSupport::Concern
|
6
|
-
|
7
|
-
included do
|
8
|
-
# Called when the form gets submitted (e.g. by pressing the Apply button)
|
9
|
-
endpoint :netzke_submit do |params, this|
|
10
|
-
data = ActiveSupport::JSON.decode(params[:data])
|
11
|
-
submit(data, this)
|
12
|
-
end
|
13
|
-
|
14
|
-
# Can be called when the form needs to load a record with given ID. E.g.:
|
15
|
-
#
|
16
|
-
# someForm.netzkeLoad({id: 100});
|
17
|
-
endpoint :netzke_load do |params, this|
|
18
|
-
@record = data_class && data_adapter.find_record(params[:id])
|
19
|
-
this.set_form_values js_record_data
|
20
|
-
end
|
21
|
-
|
22
|
-
# Returns options for a combobox
|
23
|
-
# params receive:
|
24
|
-
# +attr+ - column's name
|
25
|
-
# +query+ - what's typed-in in the combobox
|
26
|
-
# +id+ - selected record id
|
27
|
-
endpoint :get_combobox_options do |params, this|
|
28
|
-
attr = fields[params[:attr].to_sym]
|
29
|
-
this.data = data_adapter.combo_data(attr, params[:query])
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
@@ -1,74 +0,0 @@
|
|
1
|
-
module Netzke
|
2
|
-
module Basepack
|
3
|
-
class Form < Netzke::Base
|
4
|
-
module Services
|
5
|
-
extend ActiveSupport::Concern
|
6
|
-
|
7
|
-
def submit(data, this)
|
8
|
-
# File uploads are in raw params instead of "data" hash, so, mix them in into "data"
|
9
|
-
controller.params.each_pair do |k,v|
|
10
|
-
data[k] = v if v.is_a?(ActionDispatch::Http::UploadedFile)
|
11
|
-
end
|
12
|
-
|
13
|
-
success = create_or_update_record(data)
|
14
|
-
|
15
|
-
if success
|
16
|
-
this.set_form_values(js_record_data)
|
17
|
-
this.success = true # respond to classic form submission with {success: true}
|
18
|
-
this.on_submit_success # inform the Netzke endpoint caller about success
|
19
|
-
else
|
20
|
-
# flash eventual errors
|
21
|
-
data_adapter.errors_array(@record).each do |error|
|
22
|
-
flash :error => error
|
23
|
-
end
|
24
|
-
this.netzke_feedback(@flash)
|
25
|
-
this.apply_form_errors(build_form_errors(record))
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def values
|
30
|
-
record && record.netzke_hash(fields)
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
# Builds the form errors
|
36
|
-
def build_form_errors(record)
|
37
|
-
form_errors = {}
|
38
|
-
foreign_keys = data_adapter.hash_fk_model
|
39
|
-
record.errors.to_hash.map{|field, error|
|
40
|
-
# some ORM return an array for error
|
41
|
-
error = error.join ', ' if error.kind_of? Array
|
42
|
-
# Get the correct field name for the errors on foreign keys
|
43
|
-
if foreign_keys.has_key?(field)
|
44
|
-
fields.each do |k, v|
|
45
|
-
# Hack to stop to_nifty_json from camalizing model__field
|
46
|
-
field = k.to_s.gsub('__', '____') if k.to_s.split('__').first == foreign_keys[field].to_s
|
47
|
-
end
|
48
|
-
end
|
49
|
-
form_errors[field] ||= []
|
50
|
-
form_errors[field] << error
|
51
|
-
}
|
52
|
-
form_errors
|
53
|
-
end
|
54
|
-
|
55
|
-
# Creates/updates a record from hash
|
56
|
-
def create_or_update_record(hsh)
|
57
|
-
hsh.merge!(config[:strong_default_attrs]) if config[:strong_default_attrs]
|
58
|
-
@record ||= data_adapter.find_record hsh.delete(data_class.primary_key.to_s) # only pick up the record specified in the params if it was not provided in the configuration
|
59
|
-
#data_class.find(:first, :conditions => {data_class.primary_key => hsh.delete(data_class.primary_key)})
|
60
|
-
success = true
|
61
|
-
|
62
|
-
@record = data_class.new if @record.nil?
|
63
|
-
|
64
|
-
hsh.each_pair do |k,v|
|
65
|
-
data_adapter.set_record_value_for_attribute(@record, fields[k.to_sym].nil? ? {:name => k} : fields[k.to_sym], v)
|
66
|
-
end
|
67
|
-
|
68
|
-
# did we have complete success?
|
69
|
-
success && data_adapter.save_record(@record)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
data/lib/netzke/basepack/grid.rb
DELETED
@@ -1,570 +0,0 @@
|
|
1
|
-
module Netzke
|
2
|
-
module Basepack
|
3
|
-
# Ext.grid.Panel-based component with the following features:
|
4
|
-
#
|
5
|
-
# * automatic column configuration based on used ORM
|
6
|
-
# * pagination
|
7
|
-
# * multi-line CRUD operations
|
8
|
-
# * (multe-record) editing and adding records through a form
|
9
|
-
# * one-to-many association support
|
10
|
-
# * server-side sorting
|
11
|
-
# * filtering
|
12
|
-
# * complex query search with preset management
|
13
|
-
# * persistent column resizing, moving and hiding
|
14
|
-
# * permissions
|
15
|
-
# * virtual attribute support
|
16
|
-
#
|
17
|
-
# == Instance configuration
|
18
|
-
#
|
19
|
-
# The following config options are supported:
|
20
|
-
#
|
21
|
-
# [model]
|
22
|
-
#
|
23
|
-
# Name of the ActiveRecord model that provides data to this Grid, e.g. "User"
|
24
|
-
#
|
25
|
-
# [columns]
|
26
|
-
#
|
27
|
-
# An array of columns to be displayed in the grid; each column may be represented by a symbol (representing the
|
28
|
-
# model's attribute name), or a hash (when extra configuration is needed). See the "Columns" section below.
|
29
|
-
#
|
30
|
-
# [scope]
|
31
|
-
#
|
32
|
-
# Specifies how the records should be scoped.
|
33
|
-
#
|
34
|
-
# When it's a symbol, it's used as a scope name.
|
35
|
-
# When it's a string, it's a SQL statement (passed directly to +where+).
|
36
|
-
# When it's a hash, it's a conditions hash (passed directly to +where+).
|
37
|
-
# When it's an array, it's expanded into an SQL statement with arguments (passed directly to +where+), e.g.:
|
38
|
-
#
|
39
|
-
# scope: ["id > ?", 100])
|
40
|
-
#
|
41
|
-
# When it's a Proc, it gets the current relation passed as the only parameter and is expected to return a
|
42
|
-
# relation, e.g.:
|
43
|
-
#
|
44
|
-
# scope: ->(relation) { relation.where(user_id: 100) }
|
45
|
-
#
|
46
|
-
# [role]
|
47
|
-
#
|
48
|
-
# Role for ActiveModel mass-assignment security
|
49
|
-
#
|
50
|
-
# [strong_default_attrs]
|
51
|
-
#
|
52
|
-
# (defaults to {}) a hash of attributes to be merged atop of every created/updated record, e.g. +role_id: 1+
|
53
|
-
#
|
54
|
-
# [enable_column_filters]
|
55
|
-
#
|
56
|
-
# (defaults to true) enable filters in column's context menu
|
57
|
-
#
|
58
|
-
# [enable_edit_in_form]
|
59
|
-
#
|
60
|
-
# (defaults to true) provide buttons into the toolbar that activate editing/adding records via a form
|
61
|
-
#
|
62
|
-
# [enable_extended_search]
|
63
|
-
#
|
64
|
-
# (defaults to true) provide a button into the toolbar that shows configurable search form
|
65
|
-
#
|
66
|
-
# [enable_context_menu]
|
67
|
-
#
|
68
|
-
# (defaults to true) enable rows context menu
|
69
|
-
#
|
70
|
-
# [context_menu]
|
71
|
-
#
|
72
|
-
# An array of actions (e.g. [:edit, "-", :del] - see the Actions section) or +false+ to disable the context menu
|
73
|
-
#
|
74
|
-
# [enable_pagination]
|
75
|
-
#
|
76
|
-
# (defaults to true) enable pagination
|
77
|
-
#
|
78
|
-
# [rows_per_page]
|
79
|
-
#
|
80
|
-
# (defaults to 30) number of rows per page (ignored when +enable_pagination+ is set to +false+)
|
81
|
-
#
|
82
|
-
# [disable_dirty_page_warning]
|
83
|
-
#
|
84
|
-
# (defaults to false) do not warn the user about dirty records on the page when changing the page
|
85
|
-
#
|
86
|
-
# [data_store]
|
87
|
-
#
|
88
|
-
# (defaults to empty Hash) extra configuration for the JS class's internal store (see
|
89
|
-
# {Ext.data.Store}[http://docs.sencha.com/ext-js/4-1/#!/api/Ext.data.Store] ). For example, to disable auto
|
90
|
-
# loading of data, do:
|
91
|
-
#
|
92
|
-
# data_store: {auto_load: false}
|
93
|
-
#
|
94
|
-
# To enable (multi) sorting, do:
|
95
|
-
#
|
96
|
-
# data_store: {sorters: [:title, {property: :author__first_name, direction: :DESC}]}
|
97
|
-
#
|
98
|
-
# [prohibit_create]
|
99
|
-
#
|
100
|
-
# when set to +true+ prevents user from adding data
|
101
|
-
#
|
102
|
-
# [prohibit_update]
|
103
|
-
#
|
104
|
-
# when set to +true+ prevents user from editing data
|
105
|
-
#
|
106
|
-
# [prohibit_read]
|
107
|
-
#
|
108
|
-
# when set to +true+ prevents user from reading data
|
109
|
-
#
|
110
|
-
# [prohibit_delete]
|
111
|
-
#
|
112
|
-
# when set to +true+ prevents user from deleting data
|
113
|
-
#
|
114
|
-
# == Columns
|
115
|
-
#
|
116
|
-
# Columns are configured by passing an array to the +columns+ option. Each element in the array is either the name
|
117
|
-
# of model's (virtual) attribute (in which case the configuration will be fully automatic), or a hash that may
|
118
|
-
# contain the following configuration options as keys:
|
119
|
-
#
|
120
|
-
# [name]
|
121
|
-
#
|
122
|
-
# (required) name of the column, that may correspond to the model's (virtual) attribute
|
123
|
-
#
|
124
|
-
# [read_only]
|
125
|
-
#
|
126
|
-
# A boolean that defines if the cells in the column should be editable
|
127
|
-
#
|
128
|
-
# [filterable]
|
129
|
-
#
|
130
|
-
# Set to false to disable filtering on this column
|
131
|
-
#
|
132
|
-
# [getter]
|
133
|
-
#
|
134
|
-
# A lambda that receives a record as a parameter, and is expected to return a string that will be sent to the cell
|
135
|
-
# (can be HTML code), e.g.:
|
136
|
-
#
|
137
|
-
# getter: ->(r){ [r.first_name, r.last_name].join }
|
138
|
-
#
|
139
|
-
# [setter]
|
140
|
-
#
|
141
|
-
# A lambda that receives a record as first parameter, and the value passed from the cell as the second parameter,
|
142
|
-
# and is expected to modify the record accordingly, e.g.:
|
143
|
-
#
|
144
|
-
# setter: ->(r,v){ r.first_name, r.last_name = v.split(" ") }
|
145
|
-
#
|
146
|
-
# [scope]
|
147
|
-
#
|
148
|
-
# The scope for one-to-many association column. Same syntax applies as for scoping out records for the grid
|
149
|
-
# itself. See "One-to-many association support" for details.
|
150
|
-
#
|
151
|
-
# [sorting_scope]
|
152
|
-
#
|
153
|
-
# The name of the scope used for sorting the column. This can be useful for virtual columns for example. The scope
|
154
|
-
# will get one parameter specifying the direction (:asc or :desc). Example:
|
155
|
-
#
|
156
|
-
# columns => [{ name: "complete_user_name", sorting_scope: :sort_user_by_full_name }, ...]
|
157
|
-
#
|
158
|
-
# class User < ActiveRecord::Base
|
159
|
-
# scope :sort_user_by_full_name, ->(dir){
|
160
|
-
# order("users.first_name #{dir.to_s}, users.last_name #{dir.to_s}")
|
161
|
-
# }
|
162
|
-
# end
|
163
|
-
#
|
164
|
-
# [filter_with]
|
165
|
-
#
|
166
|
-
# A lambda that receives the relation, the value to filter by and the operator. This allows for more flexible
|
167
|
-
# handling of basic filters and enables filtering of virtual columns. Example:
|
168
|
-
#
|
169
|
-
# columns => [{ name: "complete_user_name", filter_with: lambda{|rel, value, op| rel.where("first_name like ? or last_name like ?", "%#{value}%", "%#{value}%" ) } }, ...]
|
170
|
-
#
|
171
|
-
# [format]
|
172
|
-
#
|
173
|
-
# The format to display data in case of date and datetime columns, e.g. 'Y-m-d g:i:s'.
|
174
|
-
#
|
175
|
-
# [excluded]
|
176
|
-
#
|
177
|
-
# When true, this column will not be used in the grid (not even in the hidden mode)
|
178
|
-
#
|
179
|
-
# [meta]
|
180
|
-
#
|
181
|
-
# When set to +true+, the data for this column will be available in the grid store, but the column won't be shown
|
182
|
-
# (as if +excluded+ were set to +true+).
|
183
|
-
#
|
184
|
-
# [blank_line]
|
185
|
-
#
|
186
|
-
# The blank line for one-to-many association columns, defaults to "---". Set to false to exclude completely.
|
187
|
-
#
|
188
|
-
# [editor]
|
189
|
-
#
|
190
|
-
# A hash that will override the automatic editor configuration. For example, for one-to-many association column
|
191
|
-
# you may set it to +{min_chars: 1}+, which will be passed to the combobox and make it query its remote data after
|
192
|
-
# entering 1 character (instead of default 4).
|
193
|
-
#
|
194
|
-
# Besides these options, a column can receive any meaningful config option understood by
|
195
|
-
# Ext.grid.column.Column[http://docs.sencha.com/ext-js/4-1/#!/api/Ext.grid.column.Column] (e.g. +hidden+)
|
196
|
-
#
|
197
|
-
# === Customizing columns by extending Grid
|
198
|
-
#
|
199
|
-
# Grid itself always uses the columns provided in the `columns` config option. But this behavior can be changed by
|
200
|
-
# overriding the `columns` method, which follows the same semantics as the `columns` config option. This can be
|
201
|
-
# used, for example, for extending the list of columns provided in the config:
|
202
|
-
#
|
203
|
-
# def columns
|
204
|
-
# super + [:extra_column]
|
205
|
-
# end
|
206
|
-
#
|
207
|
-
# === Configuring default filters on grid columns
|
208
|
-
#
|
209
|
-
# Default Filters can either be configured on the grid itself
|
210
|
-
#
|
211
|
-
# def configure(c)
|
212
|
-
# super
|
213
|
-
# c.default_filters = [{name: "Mark"}, {age: {gt: 10}}]
|
214
|
-
# end
|
215
|
-
#
|
216
|
-
# or as a component configuration
|
217
|
-
#
|
218
|
-
# component :tasks |c|
|
219
|
-
# c.klass = TaskGrid
|
220
|
-
# c.default_filters = [{due_date: {before: Time.now}}]
|
221
|
-
# end
|
222
|
-
#
|
223
|
-
# == One-to-many association support
|
224
|
-
#
|
225
|
-
# If the model bound to a grid +belongs_to+ another model, Grid can display an "assocition column" - where the user
|
226
|
-
# can select the associated record from a drop-down box. You can specify which method of the association should be
|
227
|
-
# used as the display value for the drop-down box options by using the double-underscore notation on the column
|
228
|
-
# name, where the association name is separated from the association method by "__" (double underscore). For
|
229
|
-
# example, let's say we have a Book that +belongs_to+ model Author, and Author responds to +first_name+. This way,
|
230
|
-
# the book grid can have a column defined as follows:
|
231
|
-
#
|
232
|
-
# {name: "author__first_name"}
|
233
|
-
#
|
234
|
-
# Grid will detect it to be an association column, and will use the drop-down box for selecting an author, where the
|
235
|
-
# list of authors will be represented by the author's first name.
|
236
|
-
#
|
237
|
-
# In order to scope out the records displayed in the drop-down box, the +scope+ column option can be used, e.g.:
|
238
|
-
#
|
239
|
-
# {name: "author__first_name", scope: ->(relation){relation.where(popular: true)}
|
240
|
-
#
|
241
|
-
# == Add/Edit/Search forms
|
242
|
-
#
|
243
|
-
# Add/Edit/Multi-edit/Search forms are each wrapped in a separate +Basepack::Window+-descending component (called
|
244
|
-
# +RecordFormWindow+ for the add/edit forms, and +SearchWindow+ for the search form), and can be overridden
|
245
|
-
# individually as any other child component.
|
246
|
-
#
|
247
|
-
# === Overriding windows
|
248
|
-
#
|
249
|
-
# Override the following direct child components to change the looks of the pop-up windows: +:add_window+,
|
250
|
-
# +:edit_window+, +:multi_edit_window+, and +:search_window+. For example, to override the title of the Add form,
|
251
|
-
# do:
|
252
|
-
#
|
253
|
-
# component :add_window do |c|
|
254
|
-
# super c
|
255
|
-
# c.title = "Adding new record"
|
256
|
-
# end
|
257
|
-
#
|
258
|
-
# === Modifying forms
|
259
|
-
#
|
260
|
-
# The forms will by default display the fields that correspond to the configured columns, taking over meaningful
|
261
|
-
# configuration options (e.g. +text+ will be converted into +fieldLabel+).
|
262
|
-
# You may override the default fields displayed in the all add/edit forms by overriding the
|
263
|
-
# +default_fields_for_forms+ method, which should return an array understood by the +items+ config property of the
|
264
|
-
# +Form+. If you need to use a custom +Basepack::Form+-descending class instead of +Form+, you need to override the
|
265
|
-
# +preconfigure_record_window+ method:
|
266
|
-
#
|
267
|
-
# def preconfigure_record_window(c)
|
268
|
-
# super
|
269
|
-
# c.form_config.klass = UserForm
|
270
|
-
# end
|
271
|
-
#
|
272
|
-
# To individually override forms, you should override the wrapping window components, as shown in the previous
|
273
|
-
# session. E.g., to modify the set of fields in the Add form:
|
274
|
-
#
|
275
|
-
# component :add_window do |c|
|
276
|
-
# super c
|
277
|
-
# c.form_config.items = [:title]
|
278
|
-
# end
|
279
|
-
#
|
280
|
-
# == Actions
|
281
|
-
# You can override Grid's actions to change their text, icons, and tooltips (see
|
282
|
-
# http://rdoc.info/github/netzke/netzke-core/Netzke/Core/Actions).
|
283
|
-
#
|
284
|
-
# Grid implements the following actions:
|
285
|
-
#
|
286
|
-
# [add]
|
287
|
-
#
|
288
|
-
# Inline adding of a record
|
289
|
-
#
|
290
|
-
# [del]
|
291
|
-
#
|
292
|
-
# Deletion of records
|
293
|
-
#
|
294
|
-
# [edit]
|
295
|
-
#
|
296
|
-
# Inline editing of a record
|
297
|
-
#
|
298
|
-
# [apply]
|
299
|
-
#
|
300
|
-
# Applying inline changes
|
301
|
-
#
|
302
|
-
# [add_in_form]
|
303
|
-
#
|
304
|
-
# Adding a record in a form
|
305
|
-
#
|
306
|
-
# [edit_in_form]
|
307
|
-
#
|
308
|
-
# (multi-record) editing in a forrm
|
309
|
-
#
|
310
|
-
# [search]
|
311
|
-
#
|
312
|
-
# Advanced searching
|
313
|
-
#
|
314
|
-
#
|
315
|
-
# == Class-level configuration
|
316
|
-
#
|
317
|
-
# Configuration on this level is effective during the life-time of the application. One place for setting these
|
318
|
-
# options is initializers:
|
319
|
-
#
|
320
|
-
# Netzke::Basepack::Grid.setup do |c|
|
321
|
-
# c.edit_in_form_available = false
|
322
|
-
# c.advanced_search_available = false
|
323
|
-
# c.column_filters_available = false
|
324
|
-
# c.remember_selection_available = false
|
325
|
-
# end
|
326
|
-
#
|
327
|
-
# Most of these options influence the amount of JavaScript code that is generated for this component's class, in the
|
328
|
-
# way that the less functionality is enabled, the less code is generated.
|
329
|
-
#
|
330
|
-
# The following class configuration options are available:
|
331
|
-
#
|
332
|
-
# [column_filters_available]
|
333
|
-
#
|
334
|
-
# (defaults to true) include code for the filters in the column's context menu
|
335
|
-
#
|
336
|
-
# [edit_in_form_available]
|
337
|
-
#
|
338
|
-
# (defaults to true) include code for (multi-record) editing and adding records through a form
|
339
|
-
#
|
340
|
-
# [advanced_search_available]
|
341
|
-
#
|
342
|
-
# (defaults to true) include code for extended configurable search
|
343
|
-
#
|
344
|
-
# [remember_selection_available]
|
345
|
-
#
|
346
|
-
# (defaults to true) include code for re-selecting records after grid reload
|
347
|
-
class Grid < Netzke::Base
|
348
|
-
include self::Endpoints
|
349
|
-
include self::Services
|
350
|
-
include Netzke::Basepack::Columns
|
351
|
-
include Netzke::Basepack::DataAccessor
|
352
|
-
include Netzke::Core::ConfigToDslDelegator
|
353
|
-
|
354
|
-
class_attribute :column_filters_available
|
355
|
-
self.column_filters_available = true
|
356
|
-
|
357
|
-
class_attribute :advanced_search_available
|
358
|
-
self.advanced_search_available = true
|
359
|
-
|
360
|
-
class_attribute :edit_in_form_available
|
361
|
-
self.edit_in_form_available = true
|
362
|
-
|
363
|
-
class_attribute :edit_inline_available
|
364
|
-
self.edit_inline_available = true
|
365
|
-
|
366
|
-
class_attribute :remember_selection_available
|
367
|
-
self.remember_selection_available = true
|
368
|
-
|
369
|
-
# JavaScript class configuration
|
370
|
-
js_configure do |c|
|
371
|
-
c.extend = "Ext.grid.Panel"
|
372
|
-
c.mixin :grid
|
373
|
-
c.mixin :advanced_search if advanced_search_available
|
374
|
-
c.mixin :edit_in_form if edit_in_form_available
|
375
|
-
c.mixin :remember_selection if remember_selection_available
|
376
|
-
|
377
|
-
c.mixins << "Netzke.mixins.Basepack.Columns"
|
378
|
-
c.mixins << "Netzke.mixins.Basepack.GridEventHandlers"
|
379
|
-
|
380
|
-
c.translate *%w[are_you_sure confirmation proceed_with_unapplied_changes]
|
381
|
-
|
382
|
-
# JavaScript includes
|
383
|
-
ex = Netzke::Core.ext_path.join("examples")
|
384
|
-
|
385
|
-
c.require :extensions
|
386
|
-
end
|
387
|
-
|
388
|
-
# Allows children classes to simply do:
|
389
|
-
#
|
390
|
-
# model "User"
|
391
|
-
delegates_to_dsl :model
|
392
|
-
|
393
|
-
def configure(c)
|
394
|
-
set_defaults(c)
|
395
|
-
super
|
396
|
-
end
|
397
|
-
|
398
|
-
def js_configure(c)
|
399
|
-
super
|
400
|
-
|
401
|
-
c.title = c.title || self.class.js_config.properties[:title] || data_class.name.pluralize
|
402
|
-
c.bbar = bbar
|
403
|
-
c.context_menu = context_menu
|
404
|
-
c.columns = {items: js_columns}
|
405
|
-
c.columns_order = columns_order
|
406
|
-
c.pri = data_adapter.primary_key
|
407
|
-
if c.default_filters
|
408
|
-
populate_cols_with_filters(c)
|
409
|
-
end
|
410
|
-
end
|
411
|
-
|
412
|
-
def populate_cols_with_filters(c)
|
413
|
-
c.default_filters.each do |f|
|
414
|
-
|
415
|
-
c.columns[:items].each do |col|
|
416
|
-
if col[:name].to_sym == f[:column].to_sym
|
417
|
-
if f[:value].is_a?(Hash)
|
418
|
-
val = {}
|
419
|
-
f[:value].each do |k,v|
|
420
|
-
val[k] = (v.is_a?(Time) || v.is_a?(Date) || v.is_a?(ActiveSupport::TimeWithZone)) ? Netzke::Core::JsonLiteral.new("new Date('#{v.strftime("%m/%d/%Y")}')") : v
|
421
|
-
end
|
422
|
-
else
|
423
|
-
val = f[:value]
|
424
|
-
end
|
425
|
-
new_filter = {value: val, active: true}
|
426
|
-
if col[:filter]
|
427
|
-
col[:filter].merge! new_filter
|
428
|
-
else
|
429
|
-
col[:filter] = new_filter
|
430
|
-
end
|
431
|
-
end
|
432
|
-
end
|
433
|
-
end
|
434
|
-
c.default_filters = nil
|
435
|
-
end
|
436
|
-
|
437
|
-
def config
|
438
|
-
@config ||= ActiveSupport::OrderedOptions.new.tap do |c|
|
439
|
-
# extend with data_store convenient config object
|
440
|
-
c.data_store = ActiveSupport::OrderedOptions.new
|
441
|
-
end
|
442
|
-
end
|
443
|
-
|
444
|
-
def bbar
|
445
|
-
config.has_key?(:bbar) ? config[:bbar] : default_bbar
|
446
|
-
end
|
447
|
-
|
448
|
-
def context_menu
|
449
|
-
config.has_key?(:context_menu) ? config[:context_menu] : default_context_menu
|
450
|
-
end
|
451
|
-
|
452
|
-
# Override to change the default bottom toolbar
|
453
|
-
def default_bbar
|
454
|
-
res = config[:enable_edit_inline] || config[:enable_edit_in_form] ? %w{ add edit }.map(&:to_sym) : []
|
455
|
-
res << :apply if config[:enable_edit_inline]
|
456
|
-
res << :del
|
457
|
-
res << "-" << :add_in_form << :edit_in_form if config[:enable_edit_inline] && config[:enable_edit_in_form]
|
458
|
-
res << "-" << :search if config[:enable_extended_search]
|
459
|
-
res
|
460
|
-
end
|
461
|
-
|
462
|
-
# Override to change the default context menu
|
463
|
-
def default_context_menu
|
464
|
-
res = config[:enable_edit_inline] || config[:enable_edit_in_form] ? [:edit] : []
|
465
|
-
res << :del if !config[:read_only]
|
466
|
-
res << "-" << :edit_in_form if config[:enable_edit_in_form] && config[:enable_edit_inline]
|
467
|
-
res
|
468
|
-
end
|
469
|
-
|
470
|
-
action :add do |a|
|
471
|
-
a.disabled = config[:prohibit_create]
|
472
|
-
a.handler = "onAddRecord" # overriding naming conventions as Ext 4 grid has its own onAdd method
|
473
|
-
a.icon = :add
|
474
|
-
end
|
475
|
-
|
476
|
-
action :edit do |a|
|
477
|
-
a.disabled = true
|
478
|
-
a.handler = config[:enable_edit_inline] ? "onEdit" : "onEditInForm"
|
479
|
-
a.icon = :table_edit
|
480
|
-
end
|
481
|
-
|
482
|
-
action :del do |a|
|
483
|
-
a.disabled = true
|
484
|
-
a.icon = :table_row_delete
|
485
|
-
end
|
486
|
-
|
487
|
-
action :apply do |a|
|
488
|
-
a.disabled = config[:prohibit_update] && config[:prohibit_create]
|
489
|
-
a.icon = :tick
|
490
|
-
end
|
491
|
-
|
492
|
-
action :add_in_form do |a|
|
493
|
-
a.disabled = config[:prohibit_create]
|
494
|
-
a.icon = :application_form_add
|
495
|
-
end
|
496
|
-
|
497
|
-
action :edit_in_form do |a|
|
498
|
-
a.disabled = true
|
499
|
-
a.icon = :application_form_edit
|
500
|
-
end
|
501
|
-
|
502
|
-
action :search do |a|
|
503
|
-
a.enable_toggle = true
|
504
|
-
a.icon = :find
|
505
|
-
end
|
506
|
-
|
507
|
-
component :add_window do |c|
|
508
|
-
preconfigure_record_window(c)
|
509
|
-
c.title = "Add #{data_class.model_name.human}"
|
510
|
-
c.items = [:add_form]
|
511
|
-
c.form_config.record = data_class.new(columns_default_values)
|
512
|
-
end
|
513
|
-
|
514
|
-
component :edit_window do |c|
|
515
|
-
preconfigure_record_window(c)
|
516
|
-
c.title = "Edit #{data_class.model_name.human}"
|
517
|
-
c.items = [:edit_form]
|
518
|
-
end
|
519
|
-
|
520
|
-
component :multi_edit_window do |c|
|
521
|
-
preconfigure_record_window(c)
|
522
|
-
c.title = "Edit #{data_class.model_name.human.pluralize}"
|
523
|
-
c.items = [:multi_edit_form]
|
524
|
-
end
|
525
|
-
|
526
|
-
component :search_window do |c|
|
527
|
-
c.klass = SearchWindow
|
528
|
-
c.model = config.model
|
529
|
-
c.fields = attributes_for_search
|
530
|
-
end
|
531
|
-
|
532
|
-
protected
|
533
|
-
|
534
|
-
# Override from Base. Ensures the model is provided.
|
535
|
-
def validate_config(c)
|
536
|
-
raise ArgumentError, "Grid requires a model" if c.model.nil?
|
537
|
-
end
|
538
|
-
|
539
|
-
def preconfigure_record_window(c)
|
540
|
-
c.klass = RecordFormWindow
|
541
|
-
|
542
|
-
c.form_config = ActiveSupport::OrderedOptions.new.tap do |f|
|
543
|
-
f.model = config[:model]
|
544
|
-
f.persistent_config = config[:persistent_config]
|
545
|
-
f.strong_default_attrs = config[:strong_default_attrs]
|
546
|
-
f.mode = config[:mode]
|
547
|
-
f.items = default_fields_for_forms
|
548
|
-
end
|
549
|
-
end
|
550
|
-
|
551
|
-
private
|
552
|
-
|
553
|
-
def set_defaults(c)
|
554
|
-
# The nil? checks are needed because these can be already set in a subclass
|
555
|
-
c.enable_edit_in_form = self.class.edit_in_form_available if c.enable_edit_in_form.nil?
|
556
|
-
c.enable_edit_inline = self.class.edit_inline_available if c.enable_edit_inline.nil?
|
557
|
-
c.enable_extended_search = self.class.advanced_search_available if c.enable_extended_search.nil?
|
558
|
-
c.enable_column_filters = self.class.column_filters_available if c.enable_column_filters.nil?
|
559
|
-
c.enable_pagination = true if c.enable_pagination.nil?
|
560
|
-
c.rows_per_page = 30 if c.rows_per_page.nil?
|
561
|
-
c.tools = %w{ refresh } if c.tools.nil?
|
562
|
-
end
|
563
|
-
|
564
|
-
def self.server_side_config_options
|
565
|
-
super + [:scope]
|
566
|
-
end
|
567
|
-
|
568
|
-
end
|
569
|
-
end
|
570
|
-
end
|