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.
Files changed (90) hide show
  1. data/CHANGELOG.md +75 -44
  2. data/Gemfile +4 -2
  3. data/LICENSE +2 -6
  4. data/README.md +22 -24
  5. data/javascripts/basepack.js +0 -8
  6. data/javascripts/{columns.js → grid/columns.js} +59 -71
  7. data/javascripts/grid/event_handlers.js +218 -0
  8. data/javascripts/netzkeremotecombo.js +5 -13
  9. data/javascripts/tristate.js +62 -0
  10. data/javascripts/xdatetime.js +8 -37
  11. data/lib/netzke-basepack.rb +3 -2
  12. data/lib/netzke/basepack.rb +1 -1
  13. data/lib/netzke/basepack/action_column.rb +6 -23
  14. data/lib/netzke/basepack/active_record.rb +0 -6
  15. data/lib/netzke/basepack/attr_config.rb +20 -11
  16. data/lib/netzke/basepack/attribute_config.rb +10 -0
  17. data/lib/netzke/basepack/attributes.rb +196 -0
  18. data/lib/netzke/basepack/column_config.rb +47 -39
  19. data/lib/netzke/basepack/columns.rb +127 -97
  20. data/lib/netzke/basepack/data_accessor.rb +7 -48
  21. data/lib/netzke/basepack/data_adapters/abstract_adapter.rb +15 -17
  22. data/lib/netzke/basepack/data_adapters/active_record_adapter.rb +111 -90
  23. data/lib/netzke/basepack/dynamic_tab_panel.rb +3 -5
  24. data/lib/netzke/basepack/dynamic_tab_panel/{javascripts → client}/dynamic_tab_panel.js +6 -5
  25. data/lib/netzke/basepack/field_config.rb +30 -19
  26. data/lib/netzke/basepack/fields.rb +22 -12
  27. data/lib/netzke/basepack/grid_live_search.rb +1 -4
  28. data/lib/netzke/basepack/grid_live_search/{javascripts → client}/grid_live_search.js +9 -7
  29. data/lib/netzke/basepack/item_persistence.rb +3 -3
  30. data/lib/netzke/basepack/item_persistence/events_plugin.rb +8 -10
  31. data/lib/netzke/basepack/paging_form.rb +7 -11
  32. data/lib/netzke/basepack/paging_form/{javascripts → client}/paging_form.js +4 -4
  33. data/lib/netzke/basepack/query_builder.rb +12 -10
  34. data/lib/netzke/basepack/query_builder/{javascripts → client}/query_builder.js +14 -12
  35. data/lib/netzke/basepack/record_form_window.rb +8 -8
  36. data/lib/netzke/basepack/search_panel.rb +4 -6
  37. data/lib/netzke/basepack/search_panel/{javascripts → client}/condition_field.js +13 -16
  38. data/lib/netzke/basepack/search_panel/{javascripts → client}/search_panel.js +7 -7
  39. data/lib/netzke/basepack/search_window.rb +6 -6
  40. data/lib/netzke/basepack/simple_app/{javascripts → client}/statusbar_ext.js +0 -0
  41. data/lib/netzke/basepack/version.rb +1 -1
  42. data/lib/netzke/form/base.rb +166 -0
  43. data/lib/netzke/{basepack/form/javascripts/form.js → form/base/client/base.js} +77 -38
  44. data/lib/netzke/form/base/client/readonly_mode.css +4 -0
  45. data/lib/netzke/{basepack/form/javascripts → form/base/client}/readonly_mode.js +5 -5
  46. data/lib/netzke/form/endpoints.rb +33 -0
  47. data/lib/netzke/form/services.rb +74 -0
  48. data/lib/netzke/grid/actions.rb +52 -0
  49. data/lib/netzke/grid/base.rb +289 -0
  50. data/lib/netzke/{basepack/grid/javascripts → grid/base/client}/advanced_search.js +5 -1
  51. data/lib/netzke/{basepack/grid/javascripts/grid.js → grid/base/client/base.js} +61 -53
  52. data/lib/netzke/{basepack/grid/javascripts → grid/base/client}/extensions.js +19 -13
  53. data/lib/netzke/{basepack/grid/javascripts → grid/base/client}/remember_selection.js +0 -1
  54. data/lib/netzke/grid/client.rb +8 -0
  55. data/lib/netzke/grid/components.rb +55 -0
  56. data/lib/netzke/grid/configuration.rb +72 -0
  57. data/lib/netzke/grid/endpoints.rb +99 -0
  58. data/lib/netzke/grid/permissions.rb +18 -0
  59. data/lib/netzke/grid/services.rb +141 -0
  60. data/lib/netzke/tree/base.rb +173 -0
  61. data/lib/netzke/{basepack/tree/javascripts/tree.js → tree/base/client/base.js} +55 -26
  62. data/lib/netzke/{basepack/tree/javascripts → tree/base/client}/extensions.js +7 -7
  63. data/lib/netzke/tree/endpoints.rb +34 -0
  64. data/lib/netzke/{basepack/viewport.rb → viewport/base.rb} +3 -3
  65. data/lib/netzke/{basepack/window.rb → window/base.rb} +7 -8
  66. data/lib/netzke/window/base/client/base.js +26 -0
  67. data/locales/de.yml +49 -33
  68. data/locales/en.yml +32 -39
  69. data/locales/es.yml +39 -25
  70. data/locales/nl.yml +39 -25
  71. data/locales/ru.yml +38 -25
  72. data/locales/uk.yml +40 -26
  73. data/stylesheets/basepack.css +10 -0
  74. metadata +48 -45
  75. data/javascripts/mixins/grid_event_handlers.js +0 -139
  76. data/lib/netzke/basepack/accordion.rb +0 -45
  77. data/lib/netzke/basepack/active_record/relation_extensions.rb +0 -27
  78. data/lib/netzke/basepack/form.rb +0 -131
  79. data/lib/netzke/basepack/form/endpoints.rb +0 -35
  80. data/lib/netzke/basepack/form/services.rb +0 -74
  81. data/lib/netzke/basepack/form/stylesheets/readonly_mode.css +0 -14
  82. data/lib/netzke/basepack/grid.rb +0 -570
  83. data/lib/netzke/basepack/grid/endpoints.rb +0 -111
  84. data/lib/netzke/basepack/grid/javascripts/edit_in_form.js +0 -51
  85. data/lib/netzke/basepack/grid/services.rb +0 -148
  86. data/lib/netzke/basepack/tab_panel.rb +0 -22
  87. data/lib/netzke/basepack/tab_panel/javascripts/tab_panel.js +0 -11
  88. data/lib/netzke/basepack/tree.rb +0 -269
  89. data/lib/netzke/basepack/window/javascripts/window.js +0 -26
  90. 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
@@ -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
@@ -1,14 +0,0 @@
1
- input.readonly, textarea.readonly {
2
- background-color: #f5f5f5;
3
- background-image: none;
4
- }
5
-
6
- /*input.readonly, textarea.readonly {
7
- border: 0 solid #fff;
8
- background-image: none;
9
- }
10
-
11
- label.readonly {
12
- font-weight: bold;
13
- }
14
- */
@@ -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