engine2 1.0.4 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/Gemfile +0 -0
- data/Rakefile +4 -4
- data/app/{engine2actions.coffee → actions.coffee} +341 -215
- data/app/app.coffee +0 -0
- data/app/app.css +17 -0
- data/app/engine2.coffee +158 -208
- data/app/modal.coffee +138 -0
- data/bower.json +4 -2
- data/conf/message.yaml +5 -0
- data/conf/message_pl.yaml +7 -2
- data/config.coffee +24 -12
- data/engine2.gemspec +8 -8
- data/lib/engine2.rb +12 -10
- data/lib/engine2/action.rb +1338 -133
- data/lib/engine2/action/array.rb +189 -0
- data/lib/engine2/{meta/decode_meta.rb → action/decode.rb} +52 -21
- data/lib/engine2/action/delete.rb +64 -0
- data/lib/engine2/action/form.rb +16 -0
- data/lib/engine2/{meta/infra_meta.rb → action/infra.rb} +123 -89
- data/lib/engine2/action/link.rb +117 -0
- data/lib/engine2/action/list.rb +333 -0
- data/lib/engine2/action/save.rb +28 -0
- data/lib/engine2/action/view.rb +8 -0
- data/lib/engine2/action_node.rb +221 -0
- data/lib/engine2/core.rb +175 -87
- data/lib/engine2/handler.rb +14 -13
- data/lib/engine2/model.rb +85 -43
- data/lib/engine2/models/Files.rb +4 -1
- data/lib/engine2/models/UserInfo.rb +6 -3
- data/lib/engine2/post_bootstrap.rb +4 -3
- data/lib/engine2/pre_bootstrap.rb +10 -6
- data/lib/engine2/scheme.rb +107 -65
- data/lib/engine2/templates.rb +41 -6
- data/lib/engine2/type_info.rb +51 -23
- data/lib/engine2/version.rb +2 -1
- data/package.json +22 -16
- data/public/favicon.ico +0 -0
- data/public/img/ajax-loader-dark.gif +0 -0
- data/public/img/ajax-loader.gif +0 -0
- data/views/fields/blob.slim +1 -1
- data/views/fields/bs_select.slim +2 -2
- data/views/fields/bsselect_picker.slim +4 -4
- data/views/fields/bsselect_picker_opt.slim +5 -5
- data/views/fields/checkbox.slim +4 -4
- data/views/fields/checkbox_button.slim +6 -0
- data/views/fields/checkbox_buttons.slim +3 -3
- data/views/fields/checkbox_buttons_opt.slim +3 -3
- data/views/fields/currency.slim +2 -2
- data/views/fields/date.slim +4 -4
- data/views/fields/date_range.slim +9 -9
- data/views/fields/date_time.slim +9 -9
- data/views/fields/datetime.slim +8 -8
- data/views/fields/decimal.slim +1 -1
- data/views/fields/decimal_date.slim +3 -3
- data/views/fields/decimal_time.slim +3 -3
- data/views/fields/email.slim +3 -3
- data/views/fields/file_store.slim +11 -11
- data/views/fields/input_text.slim +4 -4
- data/views/fields/integer.slim +1 -1
- data/views/fields/list_bsmselect.slim +20 -0
- data/views/fields/list_bsselect.slim +5 -5
- data/views/fields/list_bsselect_opt.slim +6 -6
- data/views/fields/list_buttons.slim +1 -1
- data/views/fields/list_buttons_opt.slim +2 -2
- data/views/fields/list_mbuttons.slim +9 -0
- data/views/fields/list_mbuttons_opt.slim +11 -0
- data/views/fields/list_mselect.slim +12 -0
- data/views/fields/list_select.slim +4 -4
- data/views/fields/list_select_opt.slim +5 -5
- data/views/fields/password.slim +4 -4
- data/views/fields/radio_checkbox.slim +3 -3
- data/views/fields/scaffold.slim +2 -2
- data/views/fields/scaffold_picker.slim +5 -5
- data/views/fields/select_picker.slim +3 -3
- data/views/fields/select_picker_opt.slim +4 -4
- data/views/fields/text_area.slim +4 -3
- data/views/fields/time.slim +5 -4
- data/views/fields/typeahead_picker.slim +12 -9
- data/views/index.slim +3 -3
- data/views/infra/index.slim +0 -0
- data/views/infra/inspect.slim +41 -10
- data/views/modals/close_m.slim +0 -0
- data/views/modals/confirm_m.slim +0 -0
- data/views/modals/empty_m.slim +0 -0
- data/views/modals/menu_m.slim +1 -1
- data/views/modals/yes_no_m.slim +0 -0
- data/views/panels/menu_m.slim +1 -1
- data/views/scaffold/confirm.slim +0 -0
- data/views/scaffold/fields.slim +6 -4
- data/views/scaffold/form.slim +1 -1
- data/views/scaffold/form_collapse.slim +4 -3
- data/views/scaffold/form_tabs.slim +3 -2
- data/views/scaffold/list.slim +0 -0
- data/views/scaffold/message.slim +0 -0
- data/views/scaffold/search.slim +4 -4
- data/views/scaffold/search_collapse.slim +8 -7
- data/views/scaffold/search_tabs.slim +6 -5
- data/views/scaffold/view.slim +2 -2
- data/views/scaffold/view_collapse.slim +5 -4
- data/views/scaffold/view_tabs.slim +4 -3
- data/views/search_fields/bsmselect_picker.slim +4 -4
- data/views/search_fields/bsselect_picker.slim +4 -4
- data/views/search_fields/checkbox.slim +3 -3
- data/views/search_fields/checkbox2.slim +5 -5
- data/views/search_fields/checkbox_buttons.slim +3 -3
- data/views/search_fields/date.slim +20 -0
- data/views/search_fields/date_range.slim +8 -8
- data/views/search_fields/decimal_date_range.slim +5 -5
- data/views/search_fields/input_text.slim +2 -2
- data/views/search_fields/integer.slim +1 -1
- data/views/search_fields/integer_range.slim +2 -2
- data/views/search_fields/list_bsmselect.slim +4 -4
- data/views/search_fields/list_bsselect.slim +4 -4
- data/views/search_fields/list_buttons.slim +2 -2
- data/views/search_fields/list_mbuttons.slim +12 -0
- data/views/search_fields/list_select.slim +3 -3
- data/views/search_fields/scaffold_picker.slim +2 -2
- data/views/search_fields/select_picker.slim +3 -3
- data/views/search_fields/typeahead_picker.slim +8 -7
- metadata +53 -48
- data/lib/engine2/meta.rb +0 -1216
- data/lib/engine2/meta/array_meta.rb +0 -82
- data/lib/engine2/meta/delete_meta.rb +0 -60
- data/lib/engine2/meta/form_meta.rb +0 -15
- data/lib/engine2/meta/link_meta.rb +0 -134
- data/lib/engine2/meta/list_meta.rb +0 -281
- data/lib/engine2/meta/save_meta.rb +0 -50
- data/lib/engine2/meta/view_meta.rb +0 -7
- data/public/__sinatra__/404.png +0 -0
- data/public/__sinatra__/500.png +0 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Engine2
|
|
5
|
+
class StarToManyLinkAction < Action
|
|
6
|
+
include ActionModelSupport
|
|
7
|
+
http_method :post
|
|
8
|
+
action_type :star_to_many_link
|
|
9
|
+
|
|
10
|
+
def pre_run
|
|
11
|
+
super
|
|
12
|
+
execute "action.errors || [action.parent().invoke(), action.panel_close()]"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def invoke handler
|
|
16
|
+
json = handler.post_to_json
|
|
17
|
+
parent = json[:parent_id]
|
|
18
|
+
ids = json[:ids]
|
|
19
|
+
handler.permit parent && ids
|
|
20
|
+
invoke_link_db handler, split_keys(parent), ids
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def invoke_link_db handler, parent, ids
|
|
24
|
+
model = assets[:model]
|
|
25
|
+
assoc = assets[:assoc]
|
|
26
|
+
|
|
27
|
+
case assoc[:type]
|
|
28
|
+
when :one_to_many
|
|
29
|
+
model.db.transaction do
|
|
30
|
+
pk = Hash[assoc[:keys].zip(parent)]
|
|
31
|
+
ids.each do |id|
|
|
32
|
+
model.where(model.primary_keys_hash(split_keys(id))).update(pk)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
when :many_to_many
|
|
36
|
+
p_pk = Hash[assoc[:left_keys].zip(parent)]
|
|
37
|
+
values = ids.map do |id|
|
|
38
|
+
p_pk.merge Hash[assoc[:right_keys].zip(split_keys(id))]
|
|
39
|
+
end
|
|
40
|
+
model.db[assoc[:join_table]].multi_insert values
|
|
41
|
+
else unsupported_association
|
|
42
|
+
end
|
|
43
|
+
{}
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
class StarToManyUnlinkActionBase < Action
|
|
48
|
+
include ActionModelSupport
|
|
49
|
+
http_method :delete
|
|
50
|
+
|
|
51
|
+
def pre_run
|
|
52
|
+
super
|
|
53
|
+
execute "[action.parent().invoke(), action.panel_close()]"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def invoke_unlink_db handler, parent, ids
|
|
57
|
+
model = assets[:model]
|
|
58
|
+
assoc = assets[:assoc]
|
|
59
|
+
|
|
60
|
+
case assoc[:type]
|
|
61
|
+
when :one_to_many
|
|
62
|
+
keys = assoc[:keys]
|
|
63
|
+
if keys.all?{|k|model.db_schema[k][:allow_null] == true}
|
|
64
|
+
model.db.transaction do
|
|
65
|
+
ids.each do |id|
|
|
66
|
+
model.where(model.primary_keys_hash(split_keys(id))).update(Hash[keys.zip([nil])])
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
else
|
|
70
|
+
handler.halt_method_not_allowed LOCS[:"non_nullable"]
|
|
71
|
+
end
|
|
72
|
+
when :many_to_many
|
|
73
|
+
model.db.transaction do
|
|
74
|
+
p_pk = Hash[assoc[:left_keys].zip(parent)]
|
|
75
|
+
ds = model.db[assoc[:join_table]]
|
|
76
|
+
ids.each do |id|
|
|
77
|
+
ds.where(p_pk & Hash[assoc[:right_keys].zip(split_keys(id))]).delete
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
else unsupported_association
|
|
81
|
+
end
|
|
82
|
+
{}
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
class StarToManyUnlinkAction < StarToManyUnlinkActionBase
|
|
87
|
+
action_type :star_to_many_unlink
|
|
88
|
+
|
|
89
|
+
def pre_run
|
|
90
|
+
super
|
|
91
|
+
node.parent.parent.*.menu(:item_menu).option :confirm_unlink, icon: "minus", show: "action.selected_size() == 0", button_loc: false
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def invoke handler
|
|
95
|
+
handler.permit id = handler.params[:id]
|
|
96
|
+
handler.permit parent = handler.params[:parent_id]
|
|
97
|
+
invoke_unlink_db handler, split_keys(parent), [id]
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
class StarToManyBulkUnlinkAction < StarToManyUnlinkActionBase
|
|
102
|
+
action_type :star_to_many_bulk_unlink
|
|
103
|
+
|
|
104
|
+
def pre_run
|
|
105
|
+
super
|
|
106
|
+
node.parent.parent.*.select_toggle_menu
|
|
107
|
+
node.parent.parent.*.menu(:menu).option_after :default_order, :confirm_bulk_unlink, icon: "minus", show: "action.selected_size() > 0", button_loc: false
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def invoke handler
|
|
111
|
+
ids = handler.param_to_json(:ids)
|
|
112
|
+
handler.permit parent = handler.params[:parent_id]
|
|
113
|
+
handler.permit ids.is_a?(Array)
|
|
114
|
+
invoke_unlink_db handler, split_keys(parent), ids
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Engine2
|
|
5
|
+
class ListAction < Action
|
|
6
|
+
action_type :list
|
|
7
|
+
include ActionListSupport, ActionQuerySupport
|
|
8
|
+
|
|
9
|
+
(DefaultFilters ||= {}).merge!(
|
|
10
|
+
string: lambda{|query, name, value, type_info, hash|
|
|
11
|
+
case type_info[:type]
|
|
12
|
+
when :list_select
|
|
13
|
+
raise E2Error.new("Filter unimplemented for string multi list_select, field: '#{name.to_sym}'") if type_info[:multiselect] # todo
|
|
14
|
+
query.where(name => value)
|
|
15
|
+
when :many_to_one
|
|
16
|
+
query.where(name => value)
|
|
17
|
+
else
|
|
18
|
+
query.where(name.like("%#{value}%"))
|
|
19
|
+
end
|
|
20
|
+
},
|
|
21
|
+
date: lambda{|query, name, value, type_info, hash|
|
|
22
|
+
if value.is_a? Hash
|
|
23
|
+
from, to = value[:from], value[:to]
|
|
24
|
+
if from && to
|
|
25
|
+
query.where(name => from .. to)
|
|
26
|
+
elsif from
|
|
27
|
+
query.where(name >= Sequel.string_to_date(from))
|
|
28
|
+
elsif to
|
|
29
|
+
query.where(name <= Sequel.string_to_date(to))
|
|
30
|
+
else
|
|
31
|
+
query # ?
|
|
32
|
+
end
|
|
33
|
+
else
|
|
34
|
+
query.where(name => Sequel.string_to_date(value))
|
|
35
|
+
end
|
|
36
|
+
},
|
|
37
|
+
datetime: lambda{|query, name, value, type_info, hash|
|
|
38
|
+
if value.is_a? Hash
|
|
39
|
+
DefaultFilters[:date].(query, name, value, type_info, hash)
|
|
40
|
+
else
|
|
41
|
+
query.where(:date.sql_function(name) => Sequel.string_to_date(value))
|
|
42
|
+
end
|
|
43
|
+
},
|
|
44
|
+
integer: lambda{|query, name, value, type_info, hash|
|
|
45
|
+
if value.is_a? Hash
|
|
46
|
+
from, to = value[:from], value[:to]
|
|
47
|
+
if from && to
|
|
48
|
+
query.where(name => from .. to)
|
|
49
|
+
else
|
|
50
|
+
query.where(from ? name >= from.to_i : name <= to.to_i)
|
|
51
|
+
end
|
|
52
|
+
elsif value.is_a?(Integer) || value.is_a?(String)
|
|
53
|
+
query.where(name => value.to_i)
|
|
54
|
+
elsif value.is_a? Array
|
|
55
|
+
if !value.empty?
|
|
56
|
+
case type_info[:type]
|
|
57
|
+
when :many_to_one
|
|
58
|
+
keys = type_info[:keys]
|
|
59
|
+
if keys.length == 1
|
|
60
|
+
query.where(name => value)
|
|
61
|
+
else
|
|
62
|
+
query.where(keys.map{|k| hash[k]}.transpose.map{|vals| Hash[keys.zip(vals)]}.reduce{|q, c| q | c})
|
|
63
|
+
end
|
|
64
|
+
when :list_select
|
|
65
|
+
if type_info[:multiselect]
|
|
66
|
+
query.where(~{(name.sql_number & value.reduce(0, :|)) => 0})
|
|
67
|
+
else
|
|
68
|
+
query.where(name => value) # decode in sql query ?
|
|
69
|
+
end
|
|
70
|
+
when :integer
|
|
71
|
+
query
|
|
72
|
+
else
|
|
73
|
+
nil
|
|
74
|
+
end
|
|
75
|
+
else
|
|
76
|
+
nil
|
|
77
|
+
end
|
|
78
|
+
else
|
|
79
|
+
nil
|
|
80
|
+
end
|
|
81
|
+
},
|
|
82
|
+
boolean: lambda{|query, name, value, type_info, hash|
|
|
83
|
+
query.where(name => value)
|
|
84
|
+
}
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
def post_run
|
|
88
|
+
query select(*assets[:model].columns.reject{|col| assets[:model].type_info[col][:length].to_i > 20}.take(10)) unless @query
|
|
89
|
+
super
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def page_frame handler, entries
|
|
93
|
+
entries
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def invoke handler
|
|
97
|
+
params = handler.params
|
|
98
|
+
model = assets[:model]
|
|
99
|
+
query = list_context(get_query, handler)
|
|
100
|
+
|
|
101
|
+
if search = params[:search]
|
|
102
|
+
query = list_search(query, handler, search)
|
|
103
|
+
elsif @filters || static.filters
|
|
104
|
+
static.filters.to_h.merge(@filters.to_h).each do |name, filter|
|
|
105
|
+
query = filter.(handler, query, {})
|
|
106
|
+
handler.permit query
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
count = query.count if lookup(:config, :use_count)
|
|
111
|
+
|
|
112
|
+
if order = params[:order]
|
|
113
|
+
order = order.to_sym
|
|
114
|
+
handler.permit lookup(:fields, order, :sort)
|
|
115
|
+
query = list_order(query, handler, order, params[:asc] == "true")
|
|
116
|
+
elsif order = @default_order_field || static.default_order_field
|
|
117
|
+
query = list_order(query, handler, order, true)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
per_page = lookup(:config, :per_page)
|
|
121
|
+
page = params[:page].to_i
|
|
122
|
+
handler.permit page >= 0 && page < 1000000
|
|
123
|
+
|
|
124
|
+
query = query.limit(per_page, page)
|
|
125
|
+
|
|
126
|
+
res = {entries: page_frame(handler, query.load_all)}
|
|
127
|
+
res[:count] = count if count
|
|
128
|
+
res
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def list_search query, handler, search
|
|
132
|
+
hash = JSON.parse(search, symbolize_names: true) rescue handler.halt_forbidden
|
|
133
|
+
model = assets[:model]
|
|
134
|
+
sfields = lookup(:search_field_list)
|
|
135
|
+
handler.permit sfields
|
|
136
|
+
|
|
137
|
+
filters = @filters || static.filters ? static.filters.to_h.merge(@filters.to_h) : {}
|
|
138
|
+
|
|
139
|
+
sfields.each do |name|
|
|
140
|
+
type_info = model.find_type_info(name)
|
|
141
|
+
name = name.to_sym
|
|
142
|
+
filter = filters[name]
|
|
143
|
+
if !(value = hash[name]).nil? # use key?
|
|
144
|
+
query = if filter
|
|
145
|
+
filter.(handler, query, hash)
|
|
146
|
+
elsif filter = DefaultFilters[type_info[:otype]]
|
|
147
|
+
name = model.type_info[name] ? model.table_name.q(name) : Sequel.expr(name)
|
|
148
|
+
filter.(query, name, value, type_info, hash)
|
|
149
|
+
else
|
|
150
|
+
raise E2Error.new("Filter not found for field '#{name}' in model '#{model}'") unless filter
|
|
151
|
+
end
|
|
152
|
+
handler.permit query
|
|
153
|
+
else
|
|
154
|
+
if filter
|
|
155
|
+
query = filter.(handler, query, hash)
|
|
156
|
+
handler.permit query
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# hash.each_pair do |name, value|
|
|
162
|
+
# handler.permit name = sfields.find{|sf|sf.to_sym == name}
|
|
163
|
+
# type_info = model.find_type_info(name)
|
|
164
|
+
# query = if filter = (@filters && @filters[name]) || (dynamic? && (static.filters && static.filters[name]))
|
|
165
|
+
# filter.(handler, query, hash)
|
|
166
|
+
# elsif filter = DefaultFilters[type_info[:otype]]
|
|
167
|
+
# name = model.type_info[name] ? model.table_name.q(name) : Sequel.expr(name)
|
|
168
|
+
# filter.(query, name, value, type_info, hash)
|
|
169
|
+
# else
|
|
170
|
+
# raise E2Error.new("Filter not found for field '#{name}' in model '#{model}'") unless filter
|
|
171
|
+
# end
|
|
172
|
+
# handler.permit query
|
|
173
|
+
# end
|
|
174
|
+
|
|
175
|
+
query
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def list_order query, handler, order, asc
|
|
179
|
+
model = assets[:model]
|
|
180
|
+
query = if order_blk = (@orders && @orders[order]) || (dynamic? && (static.orders && static.orders[order]))
|
|
181
|
+
order_blk.(handler, query)
|
|
182
|
+
else
|
|
183
|
+
order = model.table_name.q(order) if model.type_info[order]
|
|
184
|
+
query.order(order)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
asc ? query : query.reverse
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def list_context query, handler
|
|
191
|
+
query
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
#
|
|
196
|
+
# Many to One
|
|
197
|
+
#
|
|
198
|
+
class ManyToOneListAction < ListAction
|
|
199
|
+
action_type :many_to_one_list
|
|
200
|
+
|
|
201
|
+
def pre_run
|
|
202
|
+
super
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
#
|
|
207
|
+
# * to Many
|
|
208
|
+
#
|
|
209
|
+
class StarToManyListAction < ListAction
|
|
210
|
+
action_type :star_to_many_list
|
|
211
|
+
def pre_run
|
|
212
|
+
super
|
|
213
|
+
menu(:panel_menu).option_at 0, :close, icon: "remove"
|
|
214
|
+
panel_title "#{assets[:model].model_icon.icon} #{LOCS[assets[:assoc][:name]]}"
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def list_context query, handler
|
|
218
|
+
handler.permit parent = handler.params[:parent_id]
|
|
219
|
+
model = assets[:model]
|
|
220
|
+
assoc = assets[:assoc]
|
|
221
|
+
parent_keys = split_keys(parent)
|
|
222
|
+
case assoc[:type]
|
|
223
|
+
when :one_to_many
|
|
224
|
+
keys = assoc[:keys]
|
|
225
|
+
condition = parent_keys.all?(&:empty?) ? false : Hash[keys.map{|k| model.table_name.q(k)}.zip(parent_keys)]
|
|
226
|
+
if handler.params[:negate]
|
|
227
|
+
query = query.exclude(condition)
|
|
228
|
+
query = query.or(Hash[keys.zip([nil])]) if keys.all?{|k|model.db_schema[k][:allow_null] == true} # type_info[:required] ?
|
|
229
|
+
query
|
|
230
|
+
else
|
|
231
|
+
query.where(condition)
|
|
232
|
+
end
|
|
233
|
+
when :many_to_many
|
|
234
|
+
q_pk = model.primary_keys_qualified
|
|
235
|
+
j_table = assoc[:join_table]
|
|
236
|
+
l_keys = assoc[:left_keys].map{|k| j_table.q(k)}
|
|
237
|
+
r_keys = assoc[:right_keys].map{|k| j_table.q(k)}
|
|
238
|
+
r_keys_vals = Hash[r_keys.zip(q_pk)]
|
|
239
|
+
l_keys_vals = parent_keys.all?(&:empty?) ? false : Hash[l_keys.zip(parent_keys)]
|
|
240
|
+
|
|
241
|
+
if handler.params[:negate]
|
|
242
|
+
query.exclude(model.db[j_table].select(nil).where(r_keys_vals & l_keys_vals).exists)
|
|
243
|
+
else
|
|
244
|
+
# query.qualify.join(j_table, [r_keys_vals, l_keys_vals])
|
|
245
|
+
if joins = query.opts[:join] and joins.find{|j|j.table == j_table}
|
|
246
|
+
query
|
|
247
|
+
else
|
|
248
|
+
query.qualify.left_join(j_table, r_keys_vals)
|
|
249
|
+
end.filter(l_keys_vals)
|
|
250
|
+
end
|
|
251
|
+
else unsupported_association
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def post_run
|
|
256
|
+
super
|
|
257
|
+
nd = node.parent.nodes[:decode_entry]
|
|
258
|
+
request do |h|
|
|
259
|
+
if h.initial?
|
|
260
|
+
action = nd.*
|
|
261
|
+
rec = action.invoke_decode(h, [split_keys(h.params[:parent_id])]).first
|
|
262
|
+
panel_title "#{static.panel[:title]} - #{action.meta[:decode_fields].map{|f|rec[f]}.join(action.meta[:separator])}"
|
|
263
|
+
end
|
|
264
|
+
end if nd
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
class StarToManyLinkListAction < StarToManyListAction
|
|
269
|
+
action_type :star_to_many_link_list
|
|
270
|
+
def pre_run
|
|
271
|
+
super
|
|
272
|
+
panel_title LOCS[:link_title]
|
|
273
|
+
menu(:panel_menu).option_at 0, :link, icon: "ok", enabled: "action.selected_size() > 0"
|
|
274
|
+
node.parent.*.menu(:menu).option_at 0, :link_list, icon: "paperclip", button_loc: false
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# *_to_many_field
|
|
279
|
+
class StarToManyFieldAction < StarToManyListAction
|
|
280
|
+
action_type :star_to_many_field
|
|
281
|
+
|
|
282
|
+
def pre_run
|
|
283
|
+
super
|
|
284
|
+
modal_action false
|
|
285
|
+
panel_panel_template false
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def list_context query, handler
|
|
289
|
+
changes = handler.param_to_json(:changes)
|
|
290
|
+
model = assets[:model]
|
|
291
|
+
unlinked = changes[:unlink].to_a + changes[:delete].to_a + changes[:modify].to_a.map{|m|join_keys(model.primary_keys.map{|k|m[k]})}
|
|
292
|
+
linked = changes[:link]
|
|
293
|
+
query = super(query, handler)
|
|
294
|
+
|
|
295
|
+
pks = model.primary_keys_qualified
|
|
296
|
+
|
|
297
|
+
if handler.params[:negate]
|
|
298
|
+
query = unlinked.reduce(query){|q, unl|q.or pks.zip(split_keys(unl))}
|
|
299
|
+
query = linked.reduce(query){|q, ln|q.where(pks.zip(split_keys(ln)).sql_negate)}
|
|
300
|
+
else
|
|
301
|
+
query = unlinked.reduce(query){|q, unl|q.where(pks.zip(split_keys(unl)).sql_negate)}
|
|
302
|
+
query = case assets[:assoc][:type]
|
|
303
|
+
when :one_to_many
|
|
304
|
+
linked.reduce(query){|q, ln|q.or pks.zip(split_keys(ln))}
|
|
305
|
+
when :many_to_many
|
|
306
|
+
linked.reduce(query){|q, ln|q.or pks.zip(split_keys(ln))}.distinct
|
|
307
|
+
else unsupported_association
|
|
308
|
+
end unless linked.empty?
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
added = changes[:create].to_a + changes[:modify].to_a
|
|
312
|
+
cols = get_query.columns
|
|
313
|
+
query = added.reduce query do |q, a|
|
|
314
|
+
q.union(model.db.select(*cols.map{|c|a[c]}), all: true, alias: model.table_name)
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
query
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
class StarToManyFieldLinkListAction < StarToManyFieldAction
|
|
322
|
+
action_type :star_to_many_field_link_list
|
|
323
|
+
|
|
324
|
+
def pre_run
|
|
325
|
+
super
|
|
326
|
+
modal_action true
|
|
327
|
+
panel_panel_template nil
|
|
328
|
+
panel_title LOCS[:link_title]
|
|
329
|
+
menu(:panel_menu).option_at 0, :link, icon: "ok", enabled: "action.selected_size() > 0"
|
|
330
|
+
node.parent.*.menu(:menu).option_at 0, :link_list, icon: "paperclip", button_loc: false
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Engine2
|
|
5
|
+
class SaveAction < Action
|
|
6
|
+
include ActionSaveSupport
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
class InsertAction < SaveAction
|
|
10
|
+
include ActionInsertSupport
|
|
11
|
+
action_type :approve
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class UpdateAction < SaveAction
|
|
15
|
+
include ActionUpdateSupport
|
|
16
|
+
action_type :approve
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class StarToManyFieldInsertAction < InsertAction
|
|
20
|
+
self.validate_only = true
|
|
21
|
+
action_type :star_to_many_field_approve
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class StarToManyFieldUpdateAction < UpdateAction
|
|
25
|
+
self.validate_only = true
|
|
26
|
+
action_type :star_to_many_field_approve
|
|
27
|
+
end
|
|
28
|
+
end
|