toolbox 0.1.0
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/Rakefile +57 -0
- data/lib/dirs.rb +9 -0
- data/lib/toolbox/config.rb +211 -0
- data/lib/toolbox/default_controller.rb +393 -0
- data/lib/toolbox/helpers.rb +11 -0
- data/lib/toolbox/rendering.rb +413 -0
- data/lib/toolbox/searching.rb +85 -0
- data/lib/toolbox/session_params.rb +63 -0
- data/lib/toolbox/sorting.rb +74 -0
- data/lib/toolbox/version.rb +4 -0
- data/lib/toolbox.rb +29 -0
- data/locale/de/LC_MESSAGES/toolbox.mo +0 -0
- data/public/images/add.png +0 -0
- data/public/images/arrow_down.gif +0 -0
- data/public/images/arrow_up.gif +0 -0
- data/public/images/close.png +0 -0
- data/public/images/edit.gif +0 -0
- data/public/images/email.png +0 -0
- data/public/images/page.png +0 -0
- data/public/images/page_acrobat.png +0 -0
- data/public/images/page_add.png +0 -0
- data/public/images/page_copy.png +0 -0
- data/public/images/page_delete.png +0 -0
- data/public/images/page_edit.png +0 -0
- data/public/images/page_excel.png +0 -0
- data/public/images/page_list.png +0 -0
- data/public/images/page_save.png +0 -0
- data/public/images/page_word.png +0 -0
- data/public/images/remove.png +0 -0
- data/public/images/show.gif +0 -0
- data/public/images/spinner.gif +0 -0
- data/public/javascripts/popup.js +498 -0
- data/public/javascripts/toolbox.js +18 -0
- data/public/stylesheets/context_menu.css +168 -0
- data/public/stylesheets/popup.css +30 -0
- data/public/stylesheets/toolbox.css +107 -0
- data/view/toolbox/_collection.html.erb +24 -0
- data/view/toolbox/_collection_header.html.erb +7 -0
- data/view/toolbox/_context_menu.html.erb +17 -0
- data/view/toolbox/_dialogs.html.erb +6 -0
- data/view/toolbox/_form.html.erb +30 -0
- data/view/toolbox/_form_collection_row.html.erb +18 -0
- data/view/toolbox/_form_fieldset.html.erb +30 -0
- data/view/toolbox/_form_fieldset_row.html.erb +19 -0
- data/view/toolbox/_list.html.erb +25 -0
- data/view/toolbox/_list_row.html.erb +10 -0
- data/view/toolbox/_menu.html.erb +7 -0
- data/view/toolbox/_search_field.html.erb +8 -0
- data/view/toolbox/_show.html.erb +12 -0
- data/view/toolbox/_show_collection_row.html.erb +6 -0
- data/view/toolbox/_show_fieldset.html.erb +21 -0
- data/view/toolbox/edit.html.erb +5 -0
- data/view/toolbox/index.html.erb +3 -0
- data/view/toolbox/new.html.erb +9 -0
- data/view/toolbox/show.html.erb +39 -0
- metadata +178 -0
data/Rakefile
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require File.dirname(__FILE__) << "/lib/toolbox/version"
|
3
|
+
|
4
|
+
spec = Gem::Specification.new do |s|
|
5
|
+
s.name = %q{toolbox}
|
6
|
+
s.version = Toolbox::VERSION
|
7
|
+
s.date = %q{2009-01-29}
|
8
|
+
s.authors = ["David Nyffenegger"]
|
9
|
+
s.email = %q{vidl@sunrise.ch}
|
10
|
+
s.summary = %q{Davids toolbox to speedup development with rails (mainly view).}
|
11
|
+
s.homepage = %q{http://github.com/vidl/toolbox}
|
12
|
+
s.description = %q{This toolbox goes in the direction of the django admin interface.}
|
13
|
+
s.rubyforge_project = %q{toolbox}
|
14
|
+
s.files = FileList["[A-Z]*", "{lib,locale,view,public}/**/*"]
|
15
|
+
|
16
|
+
# rdoc
|
17
|
+
s.has_rdoc = true
|
18
|
+
#s.extra_rdoc_files = %w(README.rdoc MIT-LICENSE.txt)
|
19
|
+
|
20
|
+
s.add_dependency "activesupport", ">= 2.1.1"
|
21
|
+
s.add_dependency "actionpack", ">= 2.1.1"
|
22
|
+
s.add_dependency "actionpack", ">= 2.1.1"
|
23
|
+
s.add_dependency "gettext", ">= 2.1.0"
|
24
|
+
s.add_dependency "mislav-will_paginate", ">= 2.3.3"
|
25
|
+
s.add_dependency "calendar_date_select", ">= 1.15.0"
|
26
|
+
s.add_dependency "fastercsv", ">= 1.4.0"
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
desc "Generate a gemspec file for GitHup"
|
31
|
+
task :gemspec do
|
32
|
+
File.open("#{spec.name}.gemspec", 'w') do |f|
|
33
|
+
f.write spec.to_ruby
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
desc "Create the gem"
|
38
|
+
task :gem => [:makemo, :gemspec] do
|
39
|
+
`gem build #{spec.name}.gemspec`
|
40
|
+
end
|
41
|
+
|
42
|
+
desc "Generate RDoc"
|
43
|
+
task :doc do
|
44
|
+
system "hanna -x Rakefile -x spec --title 'Toolbox #{Toolbox::VERSION} API Documentation'"
|
45
|
+
end
|
46
|
+
|
47
|
+
desc "Update pot/po files."
|
48
|
+
task :updatepo do
|
49
|
+
require 'gettext/tools' #HERE!
|
50
|
+
GetText.update_pofiles("#{spec.name}", Dir.glob("{lib}/**/*.{rb,erb,rjs}"), "#{spec.name} #{Toolbox::VERSION}")
|
51
|
+
end
|
52
|
+
|
53
|
+
desc "Create mo-files"
|
54
|
+
task :makemo do
|
55
|
+
require 'gettext/tools' #HERE!
|
56
|
+
GetText.create_mofiles
|
57
|
+
end
|
data/lib/dirs.rb
ADDED
@@ -0,0 +1,211 @@
|
|
1
|
+
|
2
|
+
module Toolbox
|
3
|
+
|
4
|
+
class WidgetList
|
5
|
+
attr_reader :widgets
|
6
|
+
|
7
|
+
def initialize model_name, widgets, type
|
8
|
+
@widgets = []
|
9
|
+
last = nil
|
10
|
+
h = nil
|
11
|
+
widgets.each do |widget|
|
12
|
+
h = { :name => last, :model_name => model_name}
|
13
|
+
if widget.is_a? Hash
|
14
|
+
raise 'Before an option hash there must be a widget name (string or symbol)' unless last
|
15
|
+
raise 'The item before a option hash must be a string or a symbol' unless last.is_a?(String) || last.is_a?(Symbol)
|
16
|
+
h.merge! widget
|
17
|
+
end
|
18
|
+
@widgets << type.new(h) if last && !last.is_a?(Hash)
|
19
|
+
last = widget
|
20
|
+
end
|
21
|
+
# add the last field, if it's a symbol
|
22
|
+
if last && !last.is_a?(Hash)
|
23
|
+
h = { :name => last, :model_name => model_name}
|
24
|
+
@widgets << type.new(h)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
class EditConfig
|
31
|
+
attr_reader :controlsets
|
32
|
+
|
33
|
+
def initialize model_name, config
|
34
|
+
@controlsets = []
|
35
|
+
config.each do |controlset|
|
36
|
+
raise 'The options aggregation_config and collection_config must not be defined toghether' if controlset[:collection_config] and controlset[:aggregation_config]
|
37
|
+
h = {}
|
38
|
+
h.merge! controlset
|
39
|
+
h[:widget_list] = WidgetList.new(model_name, controlset[:widget_list], controlset[:widget_type] || ControlConfig)
|
40
|
+
h[:collection_config] = CollectionConfig.new controlset[:collection_config] if controlset[:collection_config]
|
41
|
+
h[:aggregation_config] = AggregationConfig.new controlset[:aggregation_config] if controlset[:aggregation_config]
|
42
|
+
@controlsets << WidgetSetConfig.new(h)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class ShowConfig
|
48
|
+
attr_reader :fieldsets
|
49
|
+
attr_reader :embedded_lists
|
50
|
+
attr_reader :pdf_options
|
51
|
+
|
52
|
+
def initialize model_name, config
|
53
|
+
@fieldsets = []
|
54
|
+
config[:fieldsets].each do |fieldset|
|
55
|
+
h = {}
|
56
|
+
h.merge! fieldset
|
57
|
+
h[:widget_list] = WidgetList.new(model_name, fieldset[:widget_list], fieldset[:widget_type] || FieldConfig)
|
58
|
+
h[:collection_config] = CollectionConfig.new fieldset[:collection_config] if fieldset[:collection_config]
|
59
|
+
@fieldsets << WidgetSetConfig.new(h)
|
60
|
+
end
|
61
|
+
@embedded_lists = []
|
62
|
+
prefixes = {}
|
63
|
+
config[:embedded_lists] = config[:embedded_lists] || []
|
64
|
+
config[:embedded_lists].each do |list|
|
65
|
+
h = {}
|
66
|
+
h.merge! list
|
67
|
+
h[:collection_config] = CollectionConfig.new list[:collection_config]
|
68
|
+
h[:widget_list] = WidgetList.new(h[:collection_config].type.name, list[:widget_list], FieldConfig)
|
69
|
+
c = EmbeddedListConfig.new(h)
|
70
|
+
raise "Prefix #{c.prefix} is already used by #{prefixes[c.prefix].label}" if prefixes[c.prefix]
|
71
|
+
prefixes[c.prefix] = c
|
72
|
+
@embedded_lists << c
|
73
|
+
end
|
74
|
+
@pdf_options = config[:pdf_options] || {}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# The base class of the configuration classes.
|
79
|
+
|
80
|
+
class Config
|
81
|
+
protected
|
82
|
+
# Use this methods to easily define which options the configuration class supports.
|
83
|
+
# This is done by hash, where each key defines a single option by its name.
|
84
|
+
# The value is a hash that may contain the following keys:
|
85
|
+
# - :required boolean, if true, this option is mandatory, default is false
|
86
|
+
# - :type array of class-names. If set, an exception is raised if the given value is not of the specified type
|
87
|
+
# - :values array of allowed values. If set, an exception is raised if the given value is not withing the specified values.
|
88
|
+
# - :default a default value, that is set, if the option is not given. Otherwise, nil is assgined
|
89
|
+
def self.define_options options
|
90
|
+
# attr_reader for each option
|
91
|
+
options.each_key { |k| attr_reader k }
|
92
|
+
write_inheritable_attribute 'options', options || {}
|
93
|
+
end
|
94
|
+
|
95
|
+
# create the constructor out of the options definition
|
96
|
+
def initialize vars
|
97
|
+
vars.each_pair do |k,v|
|
98
|
+
if respond_to? k.to_sym # set only, if there is an accessor
|
99
|
+
k = '@' + k.to_s
|
100
|
+
instance_variable_set(k.to_sym,v)
|
101
|
+
else
|
102
|
+
raise "Unkown option #{k.to_s} (#{self.class.name.to_s})"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
# check against the definition
|
106
|
+
self.class.read_inheritable_attribute('options').each_pair do |name, opt|
|
107
|
+
val = vars[name]
|
108
|
+
class_name = "(#{self.class.name.to_s}: #{val.inspect})"
|
109
|
+
raise "#{name} is required #{class_name}" if opt[:required] && !val
|
110
|
+
raise "#{name} is #{val.class.to_s} and not of #{opt[:type].join(' or ')} #{class_name}" if opt[:type] && val && !opt[:type].include?(val.class)
|
111
|
+
raise "#{name} is not in #{opt[:values].join(', ')} #{class_name}" if opt[:values] && val && !opt[:values].include?(val)
|
112
|
+
instance_variable_set("@#{name.to_s}".to_sym, opt[:default] || nil) unless val
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Holds the configuration for a collection. This defines how to render a widget if it's not a simple
|
118
|
+
# type but a collection.
|
119
|
+
class CollectionConfig < Config
|
120
|
+
define_options :type => { :type => [Class], :required => true},
|
121
|
+
:model_method => { :type => [Symbol], :required => true},
|
122
|
+
:suppress_table_header => { :default => false},
|
123
|
+
:hide_empty => { :default => false}
|
124
|
+
end
|
125
|
+
|
126
|
+
# Holds the configuration for other aggregations than collections.
|
127
|
+
class AggregationConfig < Config
|
128
|
+
define_options :type => { :type => [Class], :required => true},
|
129
|
+
:model_method => { :type => [Symbol], :required => true}
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
# Holds the configuration for a from control
|
134
|
+
class ControlConfig < Config
|
135
|
+
define_options :model_method => { :type => [Symbol, Proc]},
|
136
|
+
:text_method => { :type => [Symbol]},
|
137
|
+
:suppress_label => { :default => false },
|
138
|
+
:label => { :type => [String] },
|
139
|
+
:name => { :required => true, :type => [Symbol] },
|
140
|
+
:model_name => { :required => true, :type => [String] },
|
141
|
+
:type => { :type => [Symbol], :default => :textfield,
|
142
|
+
:values => [:select, :collection_select, :auto_complete, :textfield,
|
143
|
+
:textarea, :radio, :date, :check_box, :filefield, :hidden]},
|
144
|
+
:url => { :type => [Symbol]},
|
145
|
+
:size => { :type => [Fixnum, String]},
|
146
|
+
:values => { :type => [Array]},
|
147
|
+
:info => { :type => [String]},
|
148
|
+
:css_class => { :type => [String]},
|
149
|
+
:disabled => { :default => false, :type => [FalseClass, TrueClass, Proc]}
|
150
|
+
end
|
151
|
+
|
152
|
+
# Holds the configuration for a non-editable field
|
153
|
+
class FieldConfig < Config
|
154
|
+
define_options :model_method => { :type => [Symbol, Proc]},
|
155
|
+
:render_method => { :type => [Symbol, Proc]},
|
156
|
+
:hide_empty => { :default => false },
|
157
|
+
:suppress_label => { :default => false },
|
158
|
+
:suppress_link => { :default => false },
|
159
|
+
:suppress_sorting => { :default => false },
|
160
|
+
:suppress_context_menu => { :default => false},
|
161
|
+
:order_by => { :type => [String, Array] },
|
162
|
+
:label => { :type => [String] },
|
163
|
+
:join => {},
|
164
|
+
:suffix => {},
|
165
|
+
:name => { :required => true, :type => [String, Symbol] },
|
166
|
+
:model_name => { :required => true, :type => [String] },
|
167
|
+
:trunc => { :type => [Fixnum]}
|
168
|
+
|
169
|
+
|
170
|
+
def ==(s)
|
171
|
+
(s.is_a?(Hash) && s[:name] && @name.to_s == s[:name].to_s) ||
|
172
|
+
(s.is_a?(Symbol) && @name.to_s == s.to_s) ||
|
173
|
+
(s.is_a?(String) && @name.to_s == s) ||
|
174
|
+
(s.is_a?(FieldConfig) && s.name.to_s == @name.to_s)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# Holds the configuration for a set of widgets. Widgets are either controls of a form
|
179
|
+
# or un-editable fields.
|
180
|
+
class WidgetSetConfig < Config
|
181
|
+
define_options :widget_list => { :type => [WidgetList], :required => true},
|
182
|
+
:label => { :type => [String], :required => true},
|
183
|
+
:collection_config => { :type => [CollectionConfig] },
|
184
|
+
:aggregation_config => { :type => [AggregationConfig] },
|
185
|
+
:partial => { :type => [String]},
|
186
|
+
:widget_type => { :type => [Class], :values => [ControlConfig, FieldConfig]},
|
187
|
+
:condition => { :type => [Proc] } # if set, the fieldset is only rendered, if the proc returns true
|
188
|
+
end
|
189
|
+
|
190
|
+
# Holds the configuration for an embedded list
|
191
|
+
class EmbeddedListConfig < Config
|
192
|
+
define_options :widget_list => { :type => [WidgetList], :required => true},
|
193
|
+
:label => { :type => [String], :required => true},
|
194
|
+
:collection_config => { :type => [CollectionConfig], :required => true },
|
195
|
+
:prefix => { :type => [String], :required => true},
|
196
|
+
:include_assoc => { :type => [Array]}
|
197
|
+
end
|
198
|
+
|
199
|
+
class ActionConfig < Config
|
200
|
+
define_options :label => { :type => [String] },
|
201
|
+
:name => { :required => true, :type => [Symbol] },
|
202
|
+
:model_name => { :required => true, :type => [String] },
|
203
|
+
:url => {:type => [String]},
|
204
|
+
:direct_link => { :type => [TrueClass, FalseClass], :default => false},
|
205
|
+
:active => { :type => [Fixnum], :default => 1},
|
206
|
+
:css_class => { :type => [String]}
|
207
|
+
|
208
|
+
end
|
209
|
+
|
210
|
+
|
211
|
+
end
|
@@ -0,0 +1,393 @@
|
|
1
|
+
require File.expand_path("../dirs", File.dirname(__FILE__))
|
2
|
+
require 'fastercsv'
|
3
|
+
|
4
|
+
module Toolbox
|
5
|
+
|
6
|
+
#
|
7
|
+
# Defines the default action.
|
8
|
+
#
|
9
|
+
module DefaultController
|
10
|
+
|
11
|
+
def self.setup #:nodoc:
|
12
|
+
ActionController::Base.send(:extend, ClassMethods)
|
13
|
+
#ActionController::Base.send(:include, InstanceMethods)
|
14
|
+
ActionView::Base.send(:include, HelperMethods)
|
15
|
+
end
|
16
|
+
|
17
|
+
module HelperMethods
|
18
|
+
def get_record
|
19
|
+
instance_variable_get ('@' + controller.model_name).to_sym
|
20
|
+
end
|
21
|
+
|
22
|
+
def context_menu_link(rec, context = nil)
|
23
|
+
if controller.respond_to? :menu_config
|
24
|
+
context_records = []
|
25
|
+
# get the record in show-mode
|
26
|
+
context_records << get_record if get_record.is_a? ActiveRecord::Base
|
27
|
+
context_records << context if context
|
28
|
+
p = {}
|
29
|
+
context_records.each { |r| p["#{r.class.name.to_s}_id".to_sym] = r }
|
30
|
+
ctx_url =send("context_menu_#{rec.class.to_s.underscore}_path".to_sym, rec, p)
|
31
|
+
link_to_function('▼', "show_context_menu(event, '#{ctx_url}')")
|
32
|
+
else
|
33
|
+
''
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# returns html necessary to load javascript and css to make the toolbox work
|
38
|
+
def toolbox_includes()
|
39
|
+
return "" if @toolbox_already_included
|
40
|
+
@toolbox_already_included = true
|
41
|
+
|
42
|
+
|
43
|
+
js = javascript_include_tag "toolbox-#{Toolbox::VERSION}/toolbox", "toolbox-#{Toolbox::VERSION}/popup"
|
44
|
+
cs = stylesheet_link_tag 'toolbox/context_menu', 'toolbox/popup', 'toolbox/toolbox'
|
45
|
+
js + "\n" + cs + "\n"
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
module ClassMethods
|
52
|
+
|
53
|
+
# The listed associations are included in when using find together with
|
54
|
+
# search fields. Thus if a search field defintion contains "foreign" fields
|
55
|
+
# one may include the corresponding association.
|
56
|
+
def include_assoc *assoc
|
57
|
+
write_inheritable_attribute('include_assoc', assoc)
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
module InstanceMethods #:nodoc:all
|
63
|
+
|
64
|
+
def index
|
65
|
+
obj = paging
|
66
|
+
instance_variable_set :@recs, obj
|
67
|
+
|
68
|
+
respond_to do |format|
|
69
|
+
format.html do
|
70
|
+
begin
|
71
|
+
render # allow to overwrite the view of an action
|
72
|
+
rescue ActionView::MissingTemplate
|
73
|
+
render :template => 'toolbox/index'
|
74
|
+
end
|
75
|
+
end
|
76
|
+
format.xml { render :xml => obj }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def show
|
81
|
+
rec = model_class.find(params[:id])
|
82
|
+
set_record rec
|
83
|
+
|
84
|
+
@show_config = show_config rec
|
85
|
+
@embedded_list = nil
|
86
|
+
if @show_config.embedded_lists.length > 0
|
87
|
+
prefix = params[:list] || @show_config.embedded_lists.first.prefix
|
88
|
+
@show_config.embedded_lists.each do |embedded_list|
|
89
|
+
@embedded_list = embedded_list if embedded_list.prefix == prefix
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
respond_to do |format|
|
94
|
+
format.html do
|
95
|
+
begin
|
96
|
+
render # allow to overwrite the view of an action
|
97
|
+
rescue ActionView::MissingTemplate
|
98
|
+
render :template => 'toolbox/show'
|
99
|
+
end
|
100
|
+
end
|
101
|
+
format.xml { render :xml => rec }
|
102
|
+
format.pdf { render @show_config.pdf_options }
|
103
|
+
format.csv { stream_csv show_csv(rec, params) }
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
def new
|
109
|
+
# Handle dublicate command
|
110
|
+
param_name = "#{model_name}_id".to_sym
|
111
|
+
attr = {}
|
112
|
+
attr = model_class.find(params[param_name]).attributes if params[param_name]
|
113
|
+
obj = model_class.new attr
|
114
|
+
|
115
|
+
set_record obj
|
116
|
+
|
117
|
+
# context support
|
118
|
+
# set all _id fields, if available from params
|
119
|
+
params.each_pair do | key,value |
|
120
|
+
if key.to_s.ends_with? '_id' and obj.respond_to? key + '='
|
121
|
+
obj.send(key + '=', value)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
respond_to do |format|
|
126
|
+
format.html { render :template => 'toolbox/new' }
|
127
|
+
format.xml { render :xml => obj }
|
128
|
+
format.js do
|
129
|
+
render :update do |page|
|
130
|
+
locals = {:widgetsets => controller.edit_config(obj).controlsets, :dialog => true }
|
131
|
+
page.replace_html 'dialog_id', :partial => "toolbox/form", :object => obj, :locals => locals
|
132
|
+
page << "$('dialog_id').popup.show();"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def edit
|
139
|
+
obj = model_class.find(params[:id])
|
140
|
+
set_record obj
|
141
|
+
|
142
|
+
respond_to do |format|
|
143
|
+
format.html { render :template => 'toolbox/edit' }
|
144
|
+
format.js do
|
145
|
+
render :update do |page|
|
146
|
+
locals = {:widgetsets => controller.edit_config(obj).controlsets, :dialog => true }
|
147
|
+
page.replace_html 'dialog_id', :partial => "toolbox/form", :object => obj, :locals => locals
|
148
|
+
page << "$('dialog_id').popup.show();"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def create
|
155
|
+
if params[:save] # save or ok clicked
|
156
|
+
p = params[model_name.to_sym]
|
157
|
+
preprocess_params nil, p
|
158
|
+
obj = model_class.new p
|
159
|
+
set_record obj
|
160
|
+
|
161
|
+
respond_to do |format|
|
162
|
+
if obj.save
|
163
|
+
obj = redirect_create obj if respond_to? :redirect_create
|
164
|
+
flash[:notice] = Messages.created % { :object => s_(controller_name.singularize.humanize.downcase)}
|
165
|
+
format.html { redirect_to(obj) }
|
166
|
+
format.js do
|
167
|
+
render :update do
|
168
|
+
|page| page << 'window.location.reload();' # TODO improvment?
|
169
|
+
end
|
170
|
+
end
|
171
|
+
format.xml { render :xml => obj, :status => :created, :location => obj }
|
172
|
+
else
|
173
|
+
format.html { render :template => "toolbox/new" }
|
174
|
+
format.xml { render :xml => obj.errors, :status => :unprocessable_entity }
|
175
|
+
format.js do
|
176
|
+
render :update do |page|
|
177
|
+
locals = {:widgetsets => controller.edit_config(obj).controlsets, :dialog => true }
|
178
|
+
page.replace_html 'dialog_id', :partial => "toolbox/form", :object => obj, :locals => locals
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
else # cancel clicked
|
184
|
+
redirect_to eval("#{controller_name}_path")
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
def update
|
190
|
+
obj = model_class.find(params[:id])
|
191
|
+
set_record obj
|
192
|
+
|
193
|
+
if params[:save]
|
194
|
+
par = params[model_name.to_sym]
|
195
|
+
preprocess_params obj, par
|
196
|
+
# Ensure that the the collection attributes exist - create them if not
|
197
|
+
# This is needed, if all collection items have been deleted.
|
198
|
+
edit_config(obj).controlsets.each do |controlset|
|
199
|
+
if controlset.collection_config
|
200
|
+
attr = Toolbox::Helpers.collection_attribute_name(controlset.collection_config, obj)
|
201
|
+
par[attr] ||= {}
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
respond_to do |format|
|
206
|
+
if obj.update_attributes(par)
|
207
|
+
flash[:notice] = Messages.updated % { :object => s_(controller_name.singularize.humanize.downcase)}
|
208
|
+
format.html { redirect_to(obj) }
|
209
|
+
format.xml { head :ok }
|
210
|
+
format.js do
|
211
|
+
render :update do |page|
|
212
|
+
page << 'window.location.reload();' # TODO improve ?
|
213
|
+
end
|
214
|
+
end
|
215
|
+
else
|
216
|
+
format.html { render :template => "toolbox/edit" }
|
217
|
+
format.xml { render :xml => obj.errors, :status => :unprocessable_entity }
|
218
|
+
format.js do
|
219
|
+
render :update do |page|
|
220
|
+
locals = {:widgetsets => controller.edit_config(obj).controlsets, :dialog => true }
|
221
|
+
page.replace_html 'dialog_id', :partial => "toolbox/form", :object => obj, :locals => locals
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
else # cancel clicked
|
227
|
+
respond_to do |format|
|
228
|
+
format.html { redirect_to(obj) }
|
229
|
+
format.xml { head :ok }
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def destroy
|
235
|
+
obj = model_class.find(params[:id])
|
236
|
+
set_record obj
|
237
|
+
obj.destroy
|
238
|
+
|
239
|
+
respond_to do |format|
|
240
|
+
format.html { redirect_to(send(controller_name + '_url')) }
|
241
|
+
format.xml { head :ok }
|
242
|
+
format.js do
|
243
|
+
render :update do |page|
|
244
|
+
page << 'window.location.reload();' # TODO: improve?
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def context_menu
|
251
|
+
rec = model_class.find(params[:id])
|
252
|
+
set_record rec
|
253
|
+
context = {}
|
254
|
+
params.select { |k,v| k.ends_with? '_id' }.each {|a| context[a[0]] = a[1] }
|
255
|
+
locals = { :context => context, :widget_list => menu_config(rec, context)}
|
256
|
+
respond_to do |format|
|
257
|
+
format.html { render :partial => "toolbox/context_menu", :object => rec, :locals => locals }
|
258
|
+
format.js do
|
259
|
+
render :update do |page|
|
260
|
+
page.replace_html 'context_menu_id', :partial => "toolbox/context_menu", :object => rec, :locals => locals
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def model_name
|
267
|
+
controller_name.singularize
|
268
|
+
end
|
269
|
+
|
270
|
+
def model_class
|
271
|
+
controller_name.classify.constantize
|
272
|
+
end
|
273
|
+
|
274
|
+
def embedded_list_collection rec
|
275
|
+
options = {}
|
276
|
+
options[:order] = Toolbox::Sorting.order_by params, @embedded_list.widget_list.widgets, @embedded_list.prefix
|
277
|
+
options[:include] = @embedded_list.include_assoc if @embedded_list.include_assoc
|
278
|
+
rec.send(@embedded_list.collection_config.model_method).find(:all, options)
|
279
|
+
end
|
280
|
+
|
281
|
+
protected
|
282
|
+
|
283
|
+
def stream_csv options
|
284
|
+
field_renderers = options[:widget_list].widgets.map { |f| Toolbox::FieldRenderer.new self, f }
|
285
|
+
|
286
|
+
csv_string = FasterCSV.generate do |csv|
|
287
|
+
# Title
|
288
|
+
csv << field_renderers.map { |fr| fr.label(false, false) }
|
289
|
+
options[:collection].each do |rec|
|
290
|
+
csv << field_renderers.map { |fr| fr.render_value(rec, false) }
|
291
|
+
end
|
292
|
+
end
|
293
|
+
send_data csv_string, :type => "text/csv",
|
294
|
+
:filename=> options[:filename],
|
295
|
+
:disposition => 'attachment'
|
296
|
+
end
|
297
|
+
|
298
|
+
|
299
|
+
# check the consitency between the visible input-field
|
300
|
+
# and the invisible id field of autocomplete fields and
|
301
|
+
# parses date-fields
|
302
|
+
def preprocess_params rec, params
|
303
|
+
edit_config(rec).controlsets.each do |controlset|
|
304
|
+
controlset.widget_list.widgets.each do |widget|
|
305
|
+
case widget.type
|
306
|
+
when :auto_complete
|
307
|
+
if controlset.collection_config
|
308
|
+
# remove autocomplete text field for new or existing entries
|
309
|
+
a = Toolbox::Helpers.collection_attribute_name(controlset.collection_config, rec)
|
310
|
+
coll = params[a]
|
311
|
+
if coll
|
312
|
+
coll.each { |p| remove_autocomplete_text p, widget} if coll.is_a? Array
|
313
|
+
coll.each_value { |p| remove_autocomplete_text p, widget} if coll.is_a? Hash
|
314
|
+
end
|
315
|
+
if rec # may also have new entries...
|
316
|
+
a = Toolbox::Helpers.collection_attribute_name(controlset.collection_config, nil)
|
317
|
+
coll = params[a]
|
318
|
+
coll.each { |p| remove_autocomplete_text p, widget} if coll
|
319
|
+
end
|
320
|
+
else
|
321
|
+
remove_autocomplete_text params, widget
|
322
|
+
end
|
323
|
+
when :date
|
324
|
+
#params[widget.name] = Date.strptime(params[widget.name], '%d.%m.%y')
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
params
|
329
|
+
end
|
330
|
+
|
331
|
+
# do the paging for the index
|
332
|
+
def paging
|
333
|
+
filter = (params[:query] || params[:filter] || '')
|
334
|
+
cond = Toolbox::Searching.create_search_condition filter, search_fields
|
335
|
+
order = Toolbox::Sorting.order_by params, index_config.widgets
|
336
|
+
options = { :page => params[:page], :conditions => cond }
|
337
|
+
options[:order] = order if order
|
338
|
+
options[:include] = include_assoc if include_assoc
|
339
|
+
if respond_to? :index_data
|
340
|
+
index_data.paginate options
|
341
|
+
else
|
342
|
+
model_class.paginate options
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
private
|
347
|
+
|
348
|
+
def remove_autocomplete_text param, widget
|
349
|
+
value = param.delete widget.name
|
350
|
+
param[widget.name.to_s.foreign_key] = nil if value == '' || value == nil
|
351
|
+
end
|
352
|
+
|
353
|
+
def set_record obj
|
354
|
+
instance_variable_set "@#{model_name}".to_sym, obj
|
355
|
+
end
|
356
|
+
|
357
|
+
def include_assoc
|
358
|
+
self.class.read_inheritable_attribute('include_assoc')
|
359
|
+
end
|
360
|
+
|
361
|
+
end
|
362
|
+
|
363
|
+
end
|
364
|
+
|
365
|
+
# needed for not to conflict with the textdomain from the application controller
|
366
|
+
module Messages #:nodoc:all
|
367
|
+
include GetText
|
368
|
+
bindtextdomain('toolbox', :path => Toolbox::Dirs::LOCALE)
|
369
|
+
def self.created
|
370
|
+
_('%{object} was successfully created.')
|
371
|
+
end
|
372
|
+
def self.updated
|
373
|
+
_('%{object} was successfully updated.')
|
374
|
+
end
|
375
|
+
|
376
|
+
def self.create
|
377
|
+
_('Create')
|
378
|
+
end
|
379
|
+
|
380
|
+
def self.save
|
381
|
+
_('Save')
|
382
|
+
end
|
383
|
+
|
384
|
+
def self.cancel
|
385
|
+
_('Cancel')
|
386
|
+
end
|
387
|
+
|
388
|
+
def self.nothing_found
|
389
|
+
_('Nothing found')
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
|
2
|
+
module Toolbox
|
3
|
+
module Helpers
|
4
|
+
|
5
|
+
def self.collection_attribute_name collection_config, rec
|
6
|
+
new_or_existing = rec && !rec.new_record? ? 'existing' : 'new'
|
7
|
+
"#{new_or_existing}_#{collection_config.type.name.to_s.underscore}_attributes"
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
end
|