netzke-basepack 0.6.3 → 0.6.4
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.
- data/CHANGELOG.rdoc +17 -0
- data/README.rdoc +1 -1
- data/Rakefile +3 -0
- data/TODO.rdoc +1 -4
- data/features/form_panel.feature +2 -1
- data/features/grid_panel.feature +134 -5
- data/features/nested_attributes.feature +4 -1
- data/features/paging_form_panel.feature +41 -0
- data/features/search_in_grid.feature +20 -7
- data/features/step_definitions/form_panel_steps.rb +35 -0
- data/features/step_definitions/generic_steps.rb +20 -1
- data/features/step_definitions/grid_panel_steps.rb +63 -0
- data/features/support/env.rb +4 -0
- data/features/validations_in_grid.feature +13 -0
- data/features/virtual_attributes.feature +5 -9
- data/javascripts/basepack.js +21 -650
- data/javascripts/datetimefield.js +24 -0
- data/lib/generators/netzke/basepack_generator.rb +10 -0
- data/{generators/netzke_basepack/templates/public_assets → lib/generators/netzke/templates/assets}/ts-checkbox.gif +0 -0
- data/{generators/netzke_basepack → lib/generators/netzke}/templates/create_netzke_field_lists.rb +0 -0
- data/lib/netzke-basepack.rb +3 -41
- data/lib/netzke/active_record/attributes.rb +17 -21
- data/lib/netzke/basepack.rb +6 -1
- data/lib/netzke/basepack/data_accessor.rb +166 -0
- data/lib/netzke/basepack/form_panel.rb +69 -20
- data/lib/netzke/basepack/form_panel/fields.rb +15 -17
- data/lib/netzke/basepack/form_panel/javascripts/comma_list_cbg.js +7 -0
- data/lib/netzke/basepack/form_panel/javascripts/display_mode.js +6 -0
- data/lib/netzke/basepack/form_panel/javascripts/{main.js → form_panel.js} +42 -8
- data/lib/netzke/basepack/form_panel/javascripts/n_radio_group.js +24 -7
- data/lib/netzke/basepack/form_panel/javascripts/readonly_mode.js +52 -0
- data/lib/netzke/basepack/form_panel/services.rb +28 -2
- data/lib/netzke/basepack/form_panel/stylesheets/readonly_mode.css +14 -0
- data/lib/netzke/basepack/grid_panel.rb +151 -181
- data/lib/netzke/basepack/grid_panel/columns.rb +122 -84
- data/lib/netzke/basepack/grid_panel/javascripts/advanced_search.js +22 -27
- data/lib/netzke/basepack/grid_panel/javascripts/{main.js → grid_panel.js} +182 -108
- data/lib/netzke/basepack/grid_panel/record_form_window.rb +7 -2
- data/lib/netzke/basepack/grid_panel/services.rb +58 -39
- data/lib/netzke/basepack/paging_form_panel.rb +86 -25
- data/lib/netzke/basepack/query_builder.rb +105 -0
- data/lib/netzke/basepack/query_builder/javascripts/query_builder.js +140 -0
- data/lib/netzke/basepack/search_panel.rb +61 -44
- data/lib/netzke/basepack/search_panel/javascripts/condition_field.js +153 -0
- data/lib/netzke/basepack/search_panel/javascripts/search_panel.js +64 -0
- data/lib/netzke/basepack/search_window.rb +64 -0
- data/lib/netzke/basepack/simple_app.rb +1 -1
- data/lib/netzke/basepack/simple_app/javascripts/{main.js → simple_app.js} +0 -0
- data/lib/netzke/basepack/tab_panel.rb +1 -2
- data/lib/netzke/basepack/tab_panel/javascripts/{main.js → tab_panel.js} +0 -0
- data/lib/netzke/basepack/version.rb +1 -1
- data/locales/en.yml +71 -4
- data/netzke-basepack.gemspec +47 -22
- data/spec/active_record/attributes_spec.rb +6 -6
- data/spec/components/form_panel_spec.rb +2 -13
- data/stylesheets/datetimefield.css +54 -0
- data/test/rails_app/Gemfile +3 -3
- data/test/rails_app/Gemfile.lock +67 -57
- data/test/rails_app/README +1 -256
- data/test/rails_app/app/components/book_form.rb +1 -3
- data/test/rails_app/app/components/book_form_with_custom_fields.rb +20 -0
- data/test/rails_app/app/components/book_form_with_nested_attributes.rb +18 -0
- data/test/rails_app/app/components/book_grid.rb +3 -1
- data/test/rails_app/app/components/book_grid_loader.rb +24 -0
- data/test/rails_app/app/components/book_grid_with_custom_columns.rb +28 -0
- data/test/rails_app/app/components/book_grid_with_default_values.rb +1 -1
- data/test/rails_app/app/components/book_grid_with_virtual_attributes.rb +0 -1
- data/test/rails_app/app/components/book_paging_form_panel.rb +3 -2
- data/test/rails_app/app/components/book_presentation.rb +3 -3
- data/test/rails_app/app/components/book_query_builder.rb +8 -0
- data/test/rails_app/app/components/book_search_panel.rb +5 -0
- data/test/rails_app/app/components/book_search_panel/javascripts/i18n_de.js +6 -0
- data/test/rails_app/app/components/double_book_grid.rb +18 -0
- data/test/rails_app/app/components/form_without_model.rb +1 -1
- data/test/rails_app/app/components/paging_form_with_search.rb +39 -0
- data/test/rails_app/app/components/user_grid.rb +1 -1
- data/test/rails_app/app/models/author.rb +1 -0
- data/test/rails_app/config/application.rb +6 -1
- data/test/rails_app/config/database.yml +7 -5
- data/test/rails_app/config/environments/test.rb +1 -1
- data/test/rails_app/config/locales/de.yml +35 -0
- data/test/rails_app/config/locales/es.yml +84 -8
- data/test/rails_app/db/migrate/20101026190021_create_books.rb +2 -2
- data/test/rails_app/db/migrate/20110213213050_create_netzke_component_states.rb +20 -0
- data/test/rails_app/db/schema.rb +2 -18
- data/test/rails_app/public/netzke/basepack/ts-checkbox.gif +0 -0
- data/test/unit/active_record_basepack_test.rb +1 -1
- metadata +46 -45
- data/generators/netzke_basepack/netzke_basepack_generator.rb +0 -13
- data/lib/netzke/basepack/grid_panel/search_window.rb +0 -56
- data/lib/netzke/data_accessor.rb +0 -113
- data/lib/netzke/ext.rb +0 -7
- data/lib/netzke/fields_configurator.rb +0 -170
- data/lib/netzke/json_array_editor.rb +0 -67
- data/lib/netzke/masquerade_selector.rb +0 -53
- data/test/rails_app/app/components/some_search_panel.rb +0 -34
- data/test/rails_app/db/development_structure.sql +0 -93
- data/test/rails_app/db/migrate/20100905214933_create_netzke_preferences.rb +0 -16
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ext.ux.DateTimePicker & Ext.ux.form.DateTimeField
|
|
3
|
+
* http://www.sencha.com/forum/showthread.php?98292-DateTime-field-again-and-again-)
|
|
4
|
+
* Copyright(c) 2011, Andrew Pleshkov andrew.pleshkov@gmail.com
|
|
5
|
+
*
|
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
* in the Software without restriction, including without limitation the rights
|
|
9
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
* furnished to do so, subject to the following conditions:
|
|
12
|
+
*
|
|
13
|
+
* The above copyright notice and this permission notice shall be included in
|
|
14
|
+
* all copies or substantial portions of the Software.
|
|
15
|
+
*
|
|
16
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
22
|
+
* THE SOFTWARE.
|
|
23
|
+
*/
|
|
24
|
+
Ext.namespace("Ext.ux");(function(){var a=Ext.ux;a.BaseTimePicker=Ext.extend(Ext.Panel,{timeFormat:"g:i A",header:true,nowText:"Now",doneText:"Done",hourIncrement:1,minIncrement:1,hoursLabel:"Hours",minsLabel:"Minutes",cls:"ux-base-time-picker",width:210,layout:"form",labelAlign:"top",initComponent:function(){this.addEvents("select");this.hourSlider=new Ext.slider.SingleSlider({increment:this.hourIncrement,minValue:0,maxValue:23,fieldLabel:this.hoursLabel,listeners:{change:this._updateTimeValue,scope:this},plugins:new Ext.slider.Tip()});this.minSlider=new Ext.slider.SingleSlider({increment:this.minIncrement,minValue:0,maxValue:59,fieldLabel:this.minsLabel,listeners:{change:this._updateTimeValue,scope:this},plugins:new Ext.slider.Tip()});this.setCurrentTime(false);this.items=[this.hourSlider,this.minSlider];this.bbar=[{text:this.nowText,handler:this.setCurrentTime,scope:this},"->",{text:this.doneText,handler:this.onDone,scope:this}];a.BaseTimePicker.superclass.initComponent.call(this)},setCurrentTime:function(b){this.setValue(new Date(),!!b)},onDone:function(){this.fireEvent("select",this,this.getValue())},setValue:function(c,b){this.hourSlider.setValue(c.getHours(),b);this.minSlider.setValue(c.getMinutes(),b);this._updateTimeValue()},_extractValue:function(){var b=new Date();b.setHours(this.hourSlider.getValue());b.setMinutes(this.minSlider.getValue());return b},getValue:function(){return this._extractValue()},_updateTimeValue:function(){var b=this._extractValue().format(this.timeFormat);if(this.rendered){this.setTitle(b)}},afterRender:function(){a.BaseTimePicker.superclass.afterRender.call(this);this._updateTimeValue()},destroy:function(){this.purgeListeners();this.hourSlider=null;this.minSlider=null;a.BaseTimePicker.superclass.destroy.call(this)}});Ext.reg("basetimepicker",a.BaseTimePicker)})();Ext.namespace("Ext.ux");(function(){var a=Ext.ux;var c="ux-date-time-picker";a.DateTimePicker=Ext.extend(Ext.BoxComponent,{timeLabel:"Time",timeFormat:"g:i A",changeTimeText:"Change...",doneText:"Done",initComponent:function(){a.DateTimePicker.superclass.initComponent.call(this);this.addEvents("select");this.timePickerButton=new Ext.Button({text:this.changeTimeText,handler:this._showTimePicker,scope:this});this._initDatePicker();this.timeValue=new Date();if(this.value){this.setValue(this.value);delete this.value}},_initTimePicker:function(){if(!this.timeMenu){var d=this.initialConfig.timeMenu;if(d&&d.xtype){this.timeMenu=Ext.create(d)}else{var e=Ext.create(Ext.applyIf(this.initialConfig.timePicker||{},{timeFormat:this.timeFormat}),"basetimepicker");this.timeMenu=new b(e,d||{})}if(!Ext.isFunction(this.timeMenu.getPicker)){throw"Your time menu must provide the getPicker() method"}this.timeMenu.on("timeselect",this.onTimeSelect,this)}},_initDatePicker:function(){var e=this.initialConfig.datePicker||{};e.internalRender=this.initialConfig.internalRender;Ext.applyIf(e,{format:this.dateFormat||Ext.DatePicker.prototype.format});var d=this.datePicker=Ext.create(e,"datepicker");d.update=d.update.createSequence(function(){if(this.el!=null&&this.datePicker.rendered){var f=this.datePicker.el.getWidth();this.el.setWidth(f+this.el.getBorderWidth("lr")+this.el.getPadding("lr"))}},this)},_renderDatePicker:function(g){var e=this.datePicker;e.render(g);var j=e.getEl().child(".x-date-bottom");var f=j.getSize(true);var h=["position: absolute","bottom: 0","left: 0","overflow: hidden","width: "+f.width+"px","height: "+f.height+"px"].join(";");var i=g.createChild({tag:"div",cls:"x-date-bottom",style:h,children:[{tag:"table",cellspacing:0,style:"width: 100%",children:[{tag:"tr",children:[{tag:"td",align:"left"},{tag:"td",align:"right"}]}]}]});if(e.showToday){var d={};Ext.each(["text","tooltip","handler","scope"],function(k){d[k]=e.todayBtn.initialConfig[k]});this.todayBtn=new Ext.Button(d).render(i.child("td:first"))}this.doneBtn=new Ext.Button({text:this.doneText,handler:this.onDone,scope:this}).render(i.child("td:last"))},_getFormattedTimeValue:function(d){return d.format(this.timeFormat)},_renderValueField:function(f){var e=c+"-value-ct";var d=!Ext.isEmpty(this.timeLabel)?'<span class="'+e+'-value-label">'+this.timeLabel+":</span> ":"";var h=f.insertFirst({tag:"div",cls:[e,"x-date-bottom"].join(" ")});var g=h.createChild({tag:"table",cellspacing:0,style:"width: 100%",children:[{tag:"tr",children:[{tag:"td",align:"left",cls:e+"-value-cell",html:'<div class="'+e+'-value-wrap">'+d+'<span class="'+e+'-value">'+this._getFormattedTimeValue(this.timeValue)+"</span></div>"},{tag:"td",align:"right",cls:e+"-btn-cell"}]}]});this.timeValueEl=g.child("."+e+"-value");this.timeValueEl.on("click",this._showTimePicker,this);this.timePickerButton.render(g.child("td:last"))},onRender:function(e,d){this.el=e.createChild({tag:"div",cls:c,children:[{tag:"div",cls:c+"-inner"}]},d);a.DateTimePicker.superclass.onRender.call(this,e,d);var f=this.el.first();this._renderDatePicker(f);this._renderValueField(f)},_updateTimeValue:function(d){this.timeValue=d;if(this.timeValueEl!=null){this.timeValueEl.update(this._getFormattedTimeValue(d))}},setValue:function(d){this._updateTimeValue(d);this.datePicker.setValue(d.clone())},getValue:function(){var d=this.datePicker.getValue();var e=this.timeValue.getElapsed(this.timeValue.clone().clearTime());return new Date(d.getTime()+e)},onTimeSelect:function(f,d,e){this._updateTimeValue(e)},_showTimePicker:function(){this._initTimePicker();this.timeMenu.getPicker().setValue(this.timeValue,false);if(this.timeMenu.isVisible()){this.timeMenu.hide()}else{this.timeMenu.show(this.timePickerButton.el,null,this.parentMenu)}},onDone:function(){this.fireEvent("select",this,this.getValue())},destroy:function(){Ext.destroy(this.timePickerButton);this.timePickerButton=null;if(this.timeValueEl){this.timeValueEl.remove();this.timeValueEl=null}Ext.destroy(this.datePicker);this.datePicker=null;if(this.timeMenu){Ext.destroy(this.timeMenu);this.timeMenu=null}if(this.todayBtn){Ext.destroy(this.todayBtn);this.todayBtn=null}if(this.doneBtn){Ext.destroy(this.doneBtn);this.doneBtn=null}this.parentMenu=null;a.DateTimePicker.superclass.destroy.call(this)}});Ext.reg("datetimepicker",a.DateTimePicker);var b=a.DateTimePicker.Menu=Ext.extend(Ext.menu.Menu,{enableScrolling:false,hideOnClick:false,plain:true,showSeparator:false,constructor:function(e,d){d=d||{};if(d.picker){delete d.picker}this.picker=Ext.create(e);b.superclass.constructor.call(this,Ext.applyIf({items:this.picker},d));this.addEvents("timeselect");this.picker.on("select",this.onTimeSelect,this)},getPicker:function(){return this.picker},onTimeSelect:function(d,e){this.hide();this.fireEvent("timeselect",this,d,e)},destroy:function(){this.purgeListeners();this.picker=null;b.superclass.destroy.call(this)}})})();Ext.namespace("Ext.ux.form");(function(){var c=Ext.ux.form;var a=Ext.isIE7&&Ext.isStrict;var b=Ext.extend(Ext.menu.Menu,{enableScrolling:false,plain:true,showSeparator:false,hideOnClick:true,pickerId:null,cls:"x-date-menu x-date-time-menu",constructor:function(e,d){b.superclass.constructor.call(this,Ext.applyIf({items:e},d||{}));this.primaryPicker=e;e.parentMenu=this;this.on("beforeshow",this.onBeforeShow,this);this.strict=a;if(this.strict){this.on("show",this.onShow,this,{single:true,delay:20})}this.picker=e.datePicker;this.relayEvents(e,["select"]);this.on("show",e.focus,e);this.on("select",this.menuHide,this);if(this.handler){this.on("select",this.handler,this.scope||this)}},menuHide:function(){if(this.hideOnClick){this.hide(true)}},onBeforeShow:function(){if(this.picker){this.picker.hideMonthPicker(true)}},onShow:function(){var d=this.picker.getEl();d.setWidth(d.getWidth())},destroy:function(){this.primaryPicker=null;this.picker=null;b.superclass.destroy.call(this)}});c.DateTimeField=Ext.extend(Ext.form.DateField,{timeFormat:"g:i A",defaultAutoCreate:{tag:"input",type:"text",size:"22",autocomplete:"off"},initComponent:function(){c.DateTimeField.superclass.initComponent.call(this);this.dateFormat=this.dateFormat||this.format;var d=this._createPicker();this.format=this.dateFormat+" "+this.timeFormat;this.menu=new b(d,{hideOnClick:false})},_createPicker:function(){var d=this.initialConfig.picker||{};Ext.apply(d,{ctCls:"x-menu-date-item",internalRender:a||!Ext.isIE});Ext.applyIf(d,{dateFormat:this.dateFormat,timeFormat:this.timeFormat});return Ext.create(d,"datetimepicker")},onTriggerClick:function(){c.DateTimeField.superclass.onTriggerClick.apply(this,arguments);this.menu.primaryPicker.setValue(this.getValue()||new Date())}});Ext.reg("datetimefield",c.DateTimeField)})();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module Netzke
|
|
2
|
+
class BasepackGenerator < ::Rails::Generators::Base
|
|
3
|
+
source_root File.expand_path('../templates', __FILE__)
|
|
4
|
+
|
|
5
|
+
desc 'Copies necessary assets to public/netzke/basepack'
|
|
6
|
+
def execute
|
|
7
|
+
copy_file 'assets/ts-checkbox.gif', "public/netzke/basepack/ts-checkbox.gif"
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
File without changes
|
data/{generators/netzke_basepack → lib/generators/netzke}/templates/create_netzke_field_lists.rb
RENAMED
|
File without changes
|
data/lib/netzke-basepack.rb
CHANGED
|
@@ -2,56 +2,18 @@
|
|
|
2
2
|
require 'netzke-core'
|
|
3
3
|
require 'active_support/dependencies'
|
|
4
4
|
|
|
5
|
-
# path = File.dirname(__FILE__)
|
|
6
|
-
# $LOAD_PATH << path
|
|
7
|
-
|
|
8
5
|
# Make components auto-loadable
|
|
9
6
|
ActiveSupport::Dependencies.autoload_paths << File.dirname(__FILE__)
|
|
10
7
|
|
|
11
8
|
require 'netzke/basepack'
|
|
12
9
|
|
|
13
10
|
module Netzke
|
|
14
|
-
autoload :Ext, 'ext'
|
|
15
|
-
|
|
16
11
|
module Basepack
|
|
17
|
-
class Engine <
|
|
18
|
-
config.before_initialize do
|
|
19
|
-
I18n.load_path << File.dirname(__FILE__) + '/../locales/en.yml'
|
|
20
|
-
end
|
|
12
|
+
class Engine < Rails::Engine
|
|
21
13
|
end
|
|
22
14
|
end
|
|
23
|
-
|
|
24
15
|
end
|
|
25
16
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
# Netzke::Core.javascripts << "#{File.dirname(__FILE__)}/../../javascripts/basepack.js"
|
|
29
|
-
# Netzke::Core.stylesheets << "#{File.dirname(__FILE__)}/../../stylesheets/basepack.css"
|
|
30
|
-
|
|
31
|
-
# Make this plugin auto-reloadable for easier development
|
|
32
|
-
# ActiveSupport::Dependencies.autoload_once_paths.delete(path)
|
|
33
|
-
|
|
34
|
-
# Make gem's models auto-loadable
|
|
35
|
-
# %w{ models }.each do |dir|
|
|
36
|
-
# path = File.join(File.dirname(__FILE__), 'app', dir)
|
|
37
|
-
# $LOAD_PATH << path
|
|
38
|
-
# ActiveSupport::Dependencies.autoload_paths << path
|
|
39
|
-
# ActiveSupport::Dependencies.autoload_once_paths.delete(path)
|
|
40
|
-
# end
|
|
41
|
-
|
|
42
|
-
# Include javascript & styles required by all basepack components.
|
|
43
|
-
# These files will get loaded at the initial load of the framework (along with Ext and Netzke-core).
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
# FIXME: The following stylesheet inclusion doesn't *really* belong here, being component-specific,
|
|
47
|
-
# but I don't see any other solution for now. The problem is that these stylesheets come straight from
|
|
48
|
-
# Ext JS, having *relative* URLs to the images, which doesn't allow us to include them all together as those stylesheets
|
|
49
|
-
# from Netzke.
|
|
50
|
-
|
|
51
|
-
# Used by FormPanel (file upload field)
|
|
52
|
-
# Netzke::Base.config[:external_css] << "/extjs/examples/ux/fileuploadfield/css/fileuploadfield"
|
|
53
|
-
|
|
54
|
-
# Used by GridPanel
|
|
55
|
-
# Netzke::Base.config[:external_css] << "/extjs/examples/ux/gridfilters/css/RangeMenu"
|
|
56
|
-
# Netzke::Base.config[:external_css] << "/extjs/examples/ux/gridfilters/css/GridFilters"
|
|
17
|
+
I18n.load_path << File.dirname(__FILE__) + '/../locales/en.yml'
|
|
57
18
|
|
|
19
|
+
Netzke::Basepack.init
|
|
@@ -138,7 +138,7 @@ module Netzke
|
|
|
138
138
|
res = []
|
|
139
139
|
for a in attributes
|
|
140
140
|
next if a[:included] == false
|
|
141
|
-
res << value_for_attribute(a)
|
|
141
|
+
res << value_for_attribute(a, a[:nested_attribute])
|
|
142
142
|
end
|
|
143
143
|
res
|
|
144
144
|
end
|
|
@@ -148,26 +148,33 @@ module Netzke
|
|
|
148
148
|
res = {}
|
|
149
149
|
for a in (attributes.is_a?(Hash) ? attributes.values : attributes)
|
|
150
150
|
next if a[:included] == false
|
|
151
|
-
res[a[:name].to_sym] = self.value_for_attribute(a)
|
|
151
|
+
res[a[:name].to_sym] = self.value_for_attribute(a, a[:nested_attribute])
|
|
152
152
|
end
|
|
153
153
|
res
|
|
154
154
|
end
|
|
155
155
|
|
|
156
156
|
# Fetches the value specified by an (association) attribute
|
|
157
|
-
|
|
157
|
+
# If +through_association+ is true, get the value of the association by provided method, *not* the associated record's id
|
|
158
|
+
# E.g., author__name with through_association set to true may return "Vladimir Nabokov", while with through_association set to false, it'll return author_id for the current record
|
|
159
|
+
def value_for_attribute(a, through_association = false)
|
|
158
160
|
v = if a[:getter]
|
|
159
161
|
a[:getter].call(self)
|
|
160
162
|
elsif respond_to?("#{a[:name]}")
|
|
161
163
|
send("#{a[:name]}")
|
|
162
164
|
elsif is_association_attr?(a)
|
|
163
165
|
split = a[:name].to_s.split(/\.|__/)
|
|
164
|
-
split.
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
166
|
+
assoc = self.class.reflect_on_association(split.first.to_sym)
|
|
167
|
+
if through_association
|
|
168
|
+
split.inject(self) do |r,m| # TODO: do we really need to descend deeper than 1 level?
|
|
169
|
+
if r.respond_to?(m)
|
|
170
|
+
r.send(m)
|
|
171
|
+
else
|
|
172
|
+
logger.debug "!!! Netzke::Basepack: Wrong attribute name: #{a[:name]}" unless r.nil?
|
|
173
|
+
nil
|
|
174
|
+
end
|
|
170
175
|
end
|
|
176
|
+
else
|
|
177
|
+
self.send("#{assoc.options[:foreign_key] || assoc.name.to_s.foreign_key}")
|
|
171
178
|
end
|
|
172
179
|
end
|
|
173
180
|
|
|
@@ -205,18 +212,7 @@ module Netzke
|
|
|
205
212
|
# what should we do in this case?
|
|
206
213
|
end
|
|
207
214
|
else
|
|
208
|
-
|
|
209
|
-
assoc_instance = assoc.klass.send("find_by_#{assoc_method}", v)
|
|
210
|
-
rescue NoMethodError
|
|
211
|
-
assoc_instance = nil
|
|
212
|
-
logger.debug "!!! Netzke::Basepack: No find_by_#{assoc_method} method for class #{assoc.klass.name}\n"
|
|
213
|
-
end
|
|
214
|
-
if (assoc_instance)
|
|
215
|
-
# we found association instance to assign
|
|
216
|
-
self.send("#{split.first}=", assoc_instance)
|
|
217
|
-
else
|
|
218
|
-
logger.debug "!!! Netzke::Basepack: Couldn't find association #{split.first} by #{assoc_method} '#{v}'"
|
|
219
|
-
end
|
|
215
|
+
self.send("#{assoc.options[:foreign_key] || assoc.name.to_s.foreign_key}=", v)
|
|
220
216
|
end
|
|
221
217
|
else
|
|
222
218
|
logger.debug "!!! Netzke::Basepack: Association #{assoc} is not known for class #{self.class.name}"
|
data/lib/netzke/basepack.rb
CHANGED
|
@@ -8,9 +8,13 @@ module Netzke
|
|
|
8
8
|
mattr_accessor :icons_uri
|
|
9
9
|
|
|
10
10
|
class << self
|
|
11
|
+
|
|
11
12
|
# Called from netzke-basepack.rb
|
|
12
13
|
def init
|
|
14
|
+
Netzke::Core.ext_javascripts << "#{File.dirname(__FILE__)}/../../javascripts/datetimefield.js"
|
|
13
15
|
Netzke::Core.ext_javascripts << "#{File.dirname(__FILE__)}/../../javascripts/basepack.js"
|
|
16
|
+
|
|
17
|
+
Netzke::Core.ext_stylesheets << "#{File.dirname(__FILE__)}/../../stylesheets/datetimefield.css"
|
|
14
18
|
Netzke::Core.ext_stylesheets << "#{File.dirname(__FILE__)}/../../stylesheets/basepack.css"
|
|
15
19
|
|
|
16
20
|
Netzke::Core.external_ext_css << "/extjs/examples/ux/gridfilters/css/RangeMenu"
|
|
@@ -26,5 +30,6 @@ module Netzke
|
|
|
26
30
|
yield self
|
|
27
31
|
end
|
|
28
32
|
end
|
|
33
|
+
|
|
29
34
|
end
|
|
30
|
-
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
require 'netzke/active_record'
|
|
2
|
+
|
|
3
|
+
module Netzke
|
|
4
|
+
module Basepack
|
|
5
|
+
# This module is included into such data-driven components as GridPanel, FormPanel, PagingFormPanel, etc.
|
|
6
|
+
module DataAccessor
|
|
7
|
+
|
|
8
|
+
# Returns options for comboboxes in grids/forms
|
|
9
|
+
def combobox_options_for_column(column, method_options = {})
|
|
10
|
+
query = method_options[:query]
|
|
11
|
+
|
|
12
|
+
# First, check if we have options for this column defined in persistent storage
|
|
13
|
+
options = column[:combobox_options] && column[:combobox_options].split("\n")
|
|
14
|
+
if options
|
|
15
|
+
query ? options.select{ |o| o.index(/^#{query}/) }.map{ |el| [el] } : options
|
|
16
|
+
else
|
|
17
|
+
assoc, assoc_method = assoc_and_assoc_method_for_column(column)
|
|
18
|
+
|
|
19
|
+
if assoc
|
|
20
|
+
# Options for an asssociation attribute
|
|
21
|
+
|
|
22
|
+
relation = assoc.klass.scoped
|
|
23
|
+
|
|
24
|
+
relation = relation.extend_with(method_options[:scope]) if method_options[:scope]
|
|
25
|
+
|
|
26
|
+
if assoc.klass.column_names.include?(assoc_method)
|
|
27
|
+
# apply query
|
|
28
|
+
relation = relation.where(:"#{assoc_method}".like => "#{query}%") if query.present?
|
|
29
|
+
relation.all.map{ |r| [r.id, r.send(assoc_method)] }
|
|
30
|
+
else
|
|
31
|
+
relation.all.map{ |r| [r.id, r.send(assoc_method)] }.select{ |id,value| value =~ /^#{query}/ }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
else
|
|
35
|
+
# Options for a non-association attribute
|
|
36
|
+
res=data_class.netzke_combo_options_for(column[:name], method_options)
|
|
37
|
+
|
|
38
|
+
# ensure it is an array-in-array, as Ext will fail otherwise
|
|
39
|
+
raise RuntimeError, "netzke_combo_options_for should return an Array" unless res.kind_of? Array
|
|
40
|
+
return [[]] if res.empty?
|
|
41
|
+
|
|
42
|
+
unless res.first.kind_of? Array
|
|
43
|
+
res=res.map do |v|
|
|
44
|
+
[v]
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
return res
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Normalize array of attributes
|
|
55
|
+
# [:col1, "col2", {:name => :col3}] =>
|
|
56
|
+
# [{:name => "col1"}, {:name => "col2"}, {:name => "col3"}]
|
|
57
|
+
def normalize_attrs(attrs)
|
|
58
|
+
attrs.map{ |a| normalize_attr(a) }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Normalize an attribute, e.g.:
|
|
62
|
+
# :first_name =>
|
|
63
|
+
# {:name => "first_name"}
|
|
64
|
+
def normalize_attr(a)
|
|
65
|
+
a.is_a?(Symbol) || a.is_a?(String) ? {:name => a.to_s} : a.merge(:name => a[:name].to_s)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Returns association and association method for a column
|
|
69
|
+
def assoc_and_assoc_method_for_column(c)
|
|
70
|
+
assoc_name, assoc_method = c[:name].split('__')
|
|
71
|
+
assoc = data_class.reflect_on_association(assoc_name.to_sym) if assoc_method
|
|
72
|
+
[assoc, assoc_method]
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def association_attr?(name)
|
|
76
|
+
!!name.to_s.index("__")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Model class
|
|
80
|
+
def data_class
|
|
81
|
+
@data_class ||= begin
|
|
82
|
+
klass = constantize_class_name("Netzke::ModelExtensions::#{config[:model]}For#{short_component_class_name}") || original_data_class
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Model class before model extensions are taken into account
|
|
87
|
+
def original_data_class
|
|
88
|
+
@original_data_class ||= begin
|
|
89
|
+
::ActiveSupport::Deprecation.warn("data_class_name option is deprecated. Use model instead", caller) if config[:data_class_name]
|
|
90
|
+
model_name = config[:model] || config[:data_class_name]
|
|
91
|
+
model_name && constantize_class_name(model_name)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# whether a column is bound to the primary_key
|
|
96
|
+
def primary_key_attr?(a)
|
|
97
|
+
data_class && a[:name].to_s == data_class.primary_key
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Mark an attribute as "virtual" by default, when it doesn't reflect a model column, or a model column of an association
|
|
101
|
+
def set_default_virtual(c)
|
|
102
|
+
if c[:virtual].nil? # sometimes at maybe handy to mark a column as non-virtual forcefully
|
|
103
|
+
assoc, assoc_method = assoc_and_assoc_method_for_column(c)
|
|
104
|
+
if assoc
|
|
105
|
+
c[:virtual] = true if !assoc.klass.column_names.map(&:to_sym).include?(assoc_method.to_sym)
|
|
106
|
+
else
|
|
107
|
+
c[:virtual] = true if !data_class.column_names.map(&:to_sym).include?(c[:name].to_sym)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# An ActiveRecord::Relation instance encapsulating all the necessary conditions.
|
|
113
|
+
# Using the meta_where magic.
|
|
114
|
+
def get_relation(params = {})
|
|
115
|
+
# make params coming from Ext grid filters understandable by meta_where
|
|
116
|
+
conditions = params[:filter] && convert_filters(params[:filter]) || {}
|
|
117
|
+
|
|
118
|
+
relation = data_class.where(conditions)
|
|
119
|
+
|
|
120
|
+
if params[:extra_conditions]
|
|
121
|
+
extra_conditions = normalize_extra_conditions(ActiveSupport::JSON.decode(params[:extra_conditions]))
|
|
122
|
+
relation = relation.extend_with_netzke_conditions(extra_conditions) if params[:extra_conditions]
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
if params[:query]
|
|
126
|
+
# array of arrays of conditions that should be joined by OR
|
|
127
|
+
query = ActiveSupport::JSON.decode(params[:query])
|
|
128
|
+
meta_where = query.map do |conditions|
|
|
129
|
+
normalize_and_conditions(conditions)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# join them by OR
|
|
133
|
+
meta_where = meta_where.inject(meta_where.first){ |r,c| r | c } if meta_where.present?
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
relation = relation.where(meta_where)
|
|
137
|
+
|
|
138
|
+
relation = relation.extend_with(config[:scope]) if config[:scope]
|
|
139
|
+
|
|
140
|
+
relation
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
protected
|
|
144
|
+
|
|
145
|
+
def normalize_and_conditions(conditions)
|
|
146
|
+
and_conditions = conditions.map do |q|
|
|
147
|
+
value = q["value"]
|
|
148
|
+
case q["operator"]
|
|
149
|
+
when "contains"
|
|
150
|
+
q["attr"].to_sym.matches % %Q{%#{value}%}
|
|
151
|
+
else
|
|
152
|
+
if value == false || value == true
|
|
153
|
+
q["attr"].to_sym.eq % (value ? 1 : 0)
|
|
154
|
+
else
|
|
155
|
+
q["attr"].to_sym.send(q["operator"]) % value
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# join them by AND
|
|
161
|
+
and_conditions.inject(and_conditions.first){ |r,c| r & c } if and_conditions.present?
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
require "netzke/basepack/form_panel/fields"
|
|
2
2
|
require "netzke/basepack/form_panel/services"
|
|
3
|
-
require "netzke/data_accessor"
|
|
4
3
|
# require "netzke/plugins/configuration_tool"
|
|
5
4
|
|
|
6
5
|
module Netzke
|
|
@@ -20,6 +19,8 @@ module Netzke
|
|
|
20
19
|
# 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). FormPanel will expand fields by looking at their names (unless +no_binding+ set to +true+ is specified for a specific field).
|
|
21
20
|
class FormPanel < Netzke::Base
|
|
22
21
|
|
|
22
|
+
js_base_class "Ext.form.FormPanel"
|
|
23
|
+
|
|
23
24
|
# Class-level configuration
|
|
24
25
|
class_attribute :config_tool_available
|
|
25
26
|
self.config_tool_available = true
|
|
@@ -29,38 +30,67 @@ module Netzke
|
|
|
29
30
|
|
|
30
31
|
include self::Services
|
|
31
32
|
include self::Fields
|
|
32
|
-
include Netzke::DataAccessor
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
include Netzke::Basepack::DataAccessor
|
|
34
|
+
|
|
35
|
+
action :apply do
|
|
36
|
+
{
|
|
37
|
+
:text => I18n.t('netzke.basepack.form_panel.actions.apply'),
|
|
38
|
+
:tooltip => I18n.t('netzke.basepack.form_panel.actions.apply_tooltip'),
|
|
39
|
+
:icon => :tick
|
|
40
|
+
}
|
|
41
|
+
end
|
|
35
42
|
|
|
36
|
-
|
|
37
|
-
|
|
43
|
+
action :edit do
|
|
44
|
+
{
|
|
45
|
+
:text => I18n.t('netzke.basepack.form_panel.actions.edit'),
|
|
46
|
+
:tooltip => I18n.t('netzke.basepack.form_panel.actions.edit_tooltip'),
|
|
47
|
+
:icon => :pencil
|
|
48
|
+
}
|
|
38
49
|
end
|
|
39
50
|
|
|
40
|
-
action :
|
|
41
|
-
|
|
42
|
-
|
|
51
|
+
action :cancel do
|
|
52
|
+
{
|
|
53
|
+
:text => I18n.t('netzke.basepack.form_panel.actions.cancel'),
|
|
54
|
+
:tooltip => I18n.t('netzke.basepack.form_panel.actions.cancel_tooltip'),
|
|
55
|
+
:icon => :cancel
|
|
56
|
+
}
|
|
57
|
+
end
|
|
43
58
|
|
|
44
59
|
def configuration
|
|
45
|
-
|
|
60
|
+
super.tap do |sup|
|
|
61
|
+
configure_locked(sup)
|
|
62
|
+
configure_bbar(sup)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def configure_locked(c)
|
|
67
|
+
c[:locked] = c[:locked].nil? ? (c[:mode] == :lockable) : c[:locked]
|
|
68
|
+
end
|
|
46
69
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
:locked => sup[:locked].nil? ? (sup[:mode] == :lockable) : sup[:locked]
|
|
50
|
-
)
|
|
70
|
+
def configure_bbar(c)
|
|
71
|
+
c[:bbar] = [:apply.action] if c[:bbar].nil?
|
|
51
72
|
end
|
|
52
73
|
|
|
53
74
|
# Extra javascripts
|
|
54
|
-
js_mixin :
|
|
75
|
+
js_mixin :form_panel
|
|
55
76
|
js_include :comma_list_cbg
|
|
56
|
-
js_include :n_radio_group, :display_mode
|
|
77
|
+
js_include :n_radio_group, :display_mode, :readonly_mode
|
|
57
78
|
# Netzke::Base.config[:ext_location] + "/examples/ux/fileuploadfield/FileUploadField.js",
|
|
58
79
|
# "#{File.dirname(__FILE__)}/form_panel/javascripts/netzkefileupload.js"
|
|
59
80
|
|
|
81
|
+
# Extra CSS
|
|
82
|
+
css_include :readonly_mode
|
|
83
|
+
|
|
60
84
|
def js_config
|
|
61
|
-
super.
|
|
62
|
-
:pri
|
|
63
|
-
|
|
85
|
+
super.tap do |res|
|
|
86
|
+
res[:pri] = data_class && data_class.primary_key
|
|
87
|
+
res[:record] = js_record_data if record
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# A hash of record data including the meta field
|
|
92
|
+
def js_record_data
|
|
93
|
+
record.to_hash(fields).merge(:_meta => meta_field).literalize_keys
|
|
64
94
|
end
|
|
65
95
|
|
|
66
96
|
def record
|
|
@@ -101,7 +131,26 @@ module Netzke
|
|
|
101
131
|
private
|
|
102
132
|
|
|
103
133
|
def self.server_side_config_options
|
|
104
|
-
super + [:record]
|
|
134
|
+
super + [:record, :scope]
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def meta_field
|
|
138
|
+
{}.tap do |res|
|
|
139
|
+
assoc_values = get_association_values
|
|
140
|
+
res[:association_values] = assoc_values.literalize_keys if record && !assoc_values.empty?
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def get_association_values
|
|
145
|
+
fields_that_need_associated_values = fields.select{ |k,v| k.to_s.index("__") && !fields[k][:nested_attribute] }
|
|
146
|
+
# Take care of Ruby 1.8.7
|
|
147
|
+
if fields_that_need_associated_values.is_a?(Array)
|
|
148
|
+
fields_that_need_associated_values = fields_that_need_associated_values.inject({}){|r,(k,v)| r.merge(k => v)}
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
fields_that_need_associated_values.each_pair.inject({}) do |r,(k,v)|
|
|
152
|
+
r.merge(k => record.value_for_attribute(fields_that_need_associated_values[k], true))
|
|
153
|
+
end
|
|
105
154
|
end
|
|
106
155
|
|
|
107
156
|
# include ::Netzke::Plugins::ConfigurationTool if config_tool_available # it will load ConfigurationPanel into a modal window
|