engine2 1.0.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.
Files changed (145) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +2 -0
  3. data/Rakefile +138 -0
  4. data/conf/message.yaml +93 -0
  5. data/conf/message_pl.yaml +93 -0
  6. data/engine2.gemspec +34 -0
  7. data/lib/engine2.rb +34 -0
  8. data/lib/engine2/action.rb +217 -0
  9. data/lib/engine2/core.rb +572 -0
  10. data/lib/engine2/handler.rb +134 -0
  11. data/lib/engine2/meta.rb +969 -0
  12. data/lib/engine2/meta/decode_meta.rb +110 -0
  13. data/lib/engine2/meta/delete_meta.rb +73 -0
  14. data/lib/engine2/meta/form_meta.rb +144 -0
  15. data/lib/engine2/meta/infra_meta.rb +292 -0
  16. data/lib/engine2/meta/link_meta.rb +133 -0
  17. data/lib/engine2/meta/list_meta.rb +284 -0
  18. data/lib/engine2/meta/save_meta.rb +63 -0
  19. data/lib/engine2/meta/view_meta.rb +22 -0
  20. data/lib/engine2/model.rb +390 -0
  21. data/lib/engine2/models/Files.rb +38 -0
  22. data/lib/engine2/models/UserInfo.rb +24 -0
  23. data/lib/engine2/post_bootstrap.rb +83 -0
  24. data/lib/engine2/pre_bootstrap.rb +27 -0
  25. data/lib/engine2/scheme.rb +202 -0
  26. data/lib/engine2/templates.rb +229 -0
  27. data/lib/engine2/type_info.rb +342 -0
  28. data/lib/engine2/version.rb +9 -0
  29. data/public/assets/javascripts.js +13 -0
  30. data/public/assets/styles.css +4 -0
  31. data/public/css/angular-motion.css +1022 -0
  32. data/public/css/angular-ui-tree.min.css +1 -0
  33. data/public/css/app.css +196 -0
  34. data/public/css/bootstrap-additions.css +1560 -0
  35. data/public/css/bootstrap.min.css +11 -0
  36. data/public/css/font-awesome.min.css +4 -0
  37. data/public/favicon.ico +0 -0
  38. data/public/fonts/FontAwesome.otf +0 -0
  39. data/public/fonts/fontawesome-webfont.eot +0 -0
  40. data/public/fonts/fontawesome-webfont.svg +655 -0
  41. data/public/fonts/fontawesome-webfont.ttf +0 -0
  42. data/public/fonts/fontawesome-webfont.woff +0 -0
  43. data/public/fonts/fontawesome-webfont.woff2 +0 -0
  44. data/public/fonts/glyphicons-halflings-regular.eot +0 -0
  45. data/public/fonts/glyphicons-halflings-regular.svg +288 -0
  46. data/public/fonts/glyphicons-halflings-regular.ttf +0 -0
  47. data/public/fonts/glyphicons-halflings-regular.woff +0 -0
  48. data/public/fonts/glyphicons-halflings-regular.woff2 +0 -0
  49. data/public/images/file.png +0 -0
  50. data/public/images/folder-closed.png +0 -0
  51. data/public/images/folder.png +0 -0
  52. data/public/images/node-closed-2.png +0 -0
  53. data/public/images/node-closed-light.png +0 -0
  54. data/public/images/node-closed.png +0 -0
  55. data/public/images/node-opened-2.png +0 -0
  56. data/public/images/node-opened-light.png +0 -0
  57. data/public/images/node-opened.png +0 -0
  58. data/public/img/ajax-loader-dark.gif +0 -0
  59. data/public/img/ajax-loader-light.gif +0 -0
  60. data/public/img/ajax-loader.gif +0 -0
  61. data/public/js/angular-animate.js +4115 -0
  62. data/public/js/angular-cookies.js +322 -0
  63. data/public/js/angular-local-storage.js +455 -0
  64. data/public/js/angular-route.js +1022 -0
  65. data/public/js/angular-sanitize.js +717 -0
  66. data/public/js/angular-strap.js +4339 -0
  67. data/public/js/angular-strap.tpl.js +43 -0
  68. data/public/js/angular-ui-tree.js +1569 -0
  69. data/public/js/angular.js +30714 -0
  70. data/public/js/i18n/angular-locale_pl.js +115 -0
  71. data/public/js/lodash.custom.min.js +97 -0
  72. data/public/js/ng-file-upload-shim.min.js +2 -0
  73. data/public/js/ng-file-upload.min.js +3 -0
  74. data/views/app.coffee +3 -0
  75. data/views/engine2.coffee +557 -0
  76. data/views/engine2actions.coffee +849 -0
  77. data/views/engine2templates.coffee +0 -0
  78. data/views/fields/blob.slim +22 -0
  79. data/views/fields/bs_select.slim +10 -0
  80. data/views/fields/bsselect_picker.slim +18 -0
  81. data/views/fields/bsselect_picker_opt.slim +22 -0
  82. data/views/fields/checkbox.slim +11 -0
  83. data/views/fields/checkbox_buttons.slim +6 -0
  84. data/views/fields/checkbox_buttons_opt.slim +8 -0
  85. data/views/fields/currency.slim +10 -0
  86. data/views/fields/date.slim +21 -0
  87. data/views/fields/date_range.slim +44 -0
  88. data/views/fields/date_time.slim +42 -0
  89. data/views/fields/datetime.slim +42 -0
  90. data/views/fields/decimal.slim +11 -0
  91. data/views/fields/decimal_date.slim +22 -0
  92. data/views/fields/decimal_time.slim +26 -0
  93. data/views/fields/email.slim +13 -0
  94. data/views/fields/file_store.slim +61 -0
  95. data/views/fields/input_text.slim +14 -0
  96. data/views/fields/integer.slim +11 -0
  97. data/views/fields/list_bsselect.slim +18 -0
  98. data/views/fields/list_bsselect_opt.slim +21 -0
  99. data/views/fields/list_buttons.slim +3 -0
  100. data/views/fields/list_buttons_opt.slim +5 -0
  101. data/views/fields/list_select.slim +11 -0
  102. data/views/fields/list_select_opt.slim +15 -0
  103. data/views/fields/password.slim +14 -0
  104. data/views/fields/radio_checkbox.slim +10 -0
  105. data/views/fields/scaffold.slim +2 -0
  106. data/views/fields/scaffold_picker.slim +20 -0
  107. data/views/fields/select_picker.slim +12 -0
  108. data/views/fields/select_picker_opt.slim +16 -0
  109. data/views/fields/text_area.slim +10 -0
  110. data/views/fields/time.slim +22 -0
  111. data/views/fields/typeahead_picker.slim +25 -0
  112. data/views/index.slim +44 -0
  113. data/views/infra/index.slim +5 -0
  114. data/views/infra/inspect.slim +81 -0
  115. data/views/modals/close_m.slim +15 -0
  116. data/views/modals/confirm_m.slim +19 -0
  117. data/views/modals/empty_m.slim +12 -0
  118. data/views/modals/menu_m.slim +13 -0
  119. data/views/modals/yes_no_m.slim +19 -0
  120. data/views/panels/menu_m.slim +9 -0
  121. data/views/scaffold/confirm.slim +3 -0
  122. data/views/scaffold/fields.slim +10 -0
  123. data/views/scaffold/form.slim +11 -0
  124. data/views/scaffold/list.slim +42 -0
  125. data/views/scaffold/message.slim +3 -0
  126. data/views/scaffold/search.slim +20 -0
  127. data/views/scaffold/view.slim +18 -0
  128. data/views/search_fields/bsmselect_picker.slim +25 -0
  129. data/views/search_fields/bsselect_picker.slim +24 -0
  130. data/views/search_fields/checkbox.slim +11 -0
  131. data/views/search_fields/checkbox2.slim +14 -0
  132. data/views/search_fields/checkbox_buttons.slim +10 -0
  133. data/views/search_fields/date_range.slim +46 -0
  134. data/views/search_fields/decimal_date_range.slim +47 -0
  135. data/views/search_fields/input_text.slim +18 -0
  136. data/views/search_fields/integer.slim +18 -0
  137. data/views/search_fields/integer_range.slim +27 -0
  138. data/views/search_fields/list_bsmselect.slim +24 -0
  139. data/views/search_fields/list_bsselect.slim +22 -0
  140. data/views/search_fields/list_buttons.slim +8 -0
  141. data/views/search_fields/list_select.slim +17 -0
  142. data/views/search_fields/scaffold_picker.slim +19 -0
  143. data/views/search_fields/select_picker.slim +17 -0
  144. data/views/search_fields/typeahead_picker.slim +25 -0
  145. metadata +327 -0
@@ -0,0 +1,110 @@
1
+ # coding: utf-8
2
+
3
+ module Engine2
4
+ class DecodeMeta < Meta
5
+ include MetaAPISupport, MetaModelSupport, MetaQuerySupport
6
+
7
+ def decode *fields, &blk
8
+ query select(*fields), &blk
9
+ @meta[:decode_fields] = fields
10
+ end
11
+
12
+ def separator sep
13
+ @meta[:separator] = sep
14
+ end
15
+
16
+ def show_max_selected max
17
+ @meta[:show_max_selected] = max
18
+ end
19
+
20
+ def post_process
21
+ if fields = @meta[:fields]
22
+ fields = fields - static.get[:fields] if dynamic?
23
+ # no decorate here
24
+ fields.each do |name|
25
+ type_info = assets[:model].type_info[name] # foreign keys ?
26
+ proc = ListRendererPostProcessors[type_info[:type]] # like... checkboxes, list_selects
27
+ proc.(self, name, type_info) if proc
28
+ end
29
+ end
30
+ # no super
31
+ end
32
+
33
+ def pre_run
34
+ super
35
+ if assoc = assets[:assoc]
36
+ decode = assoc[:model].type_info[assoc[:keys].first][:decode]
37
+ if decode[:search][:multiple]
38
+ show_max_selected 3
39
+ @meta[:decode_selected] = LOCS[:decode_selected]
40
+ end
41
+ end
42
+ end
43
+
44
+ def post_run
45
+ query select(*assets[:model].primary_keys) unless @query
46
+ @meta[:separator] = '/' unless @meta[:separator]
47
+ super
48
+ @meta[:primary_fields] = assets[:model].primary_keys
49
+ end
50
+
51
+ end
52
+
53
+ class DecodeListMeta < DecodeMeta
54
+ meta_type :decode_list
55
+
56
+ def invoke handler
57
+ {entries: get_query.limit(200).all}
58
+ end
59
+ end
60
+
61
+ class TypeAheadMeta < DecodeMeta
62
+ meta_type :typeahead
63
+
64
+ def pre_run
65
+ super
66
+ limit 10
67
+ end
68
+
69
+ def limit lmt
70
+ @meta[:limit] = lmt
71
+ end
72
+
73
+ def invoke handler
74
+ if query = handler.params[:query]
75
+ condition = (@meta[:decode_fields] || @meta[:fields]).map{|f|f.like("%#{query}%")}.inject{|q, f| q | f}
76
+ {entries: get_query.where(condition).limit(@meta[:limit]).all}
77
+ else
78
+ handler.permit id = handler.params[:id]
79
+ record = get_query[Hash[assets[:model].primary_keys.zip(split_keys(id))]]
80
+ # handler.halt_not_found(LOCS[:no_entry]) unless record
81
+ {entry: record}
82
+ end
83
+ end
84
+ end
85
+
86
+ class DecodeEntryMeta < DecodeMeta
87
+ meta_type :decode_entry
88
+
89
+ def invoke handler
90
+ {entries: invoke_decode(handler, handler.param_to_json(:ids))}
91
+ end
92
+
93
+ def invoke_decode handler, ids
94
+ records = get_query.where(ids.map{|keys| Hash[assets[:model].primary_keys.zip(keys)]}.inject{|q, c| q | c}).all
95
+ # handler.halt_not_found(LOCS[:no_entry]) if records.empty?
96
+ records
97
+ end
98
+
99
+ def post_run
100
+ super
101
+ if assoc = assets[:assoc]
102
+ decode = assoc[:model].type_info[assoc[:keys].first][:decode]
103
+
104
+ if decode[:search][:multiple] && action.parent.parent.*.is_a?(ListMeta)
105
+ action.list.*.menu(:panel_menu).option_at 0, :choose, icon: :ok # , disabled: "action.selected_size() == 0"
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,73 @@
1
+ # coding: utf-8
2
+
3
+ module Engine2
4
+ class DeleteMetaBase < Meta
5
+ include MetaModelSupport
6
+
7
+ def invoke_delete_db handler, ids
8
+ begin
9
+ model = assets[:model]
10
+ model.db.transaction do
11
+ ids.each do |id|
12
+ keys = split_keys(id)
13
+ restrict = model.association_reflections.select do |name, rel|
14
+ case rel[:type]
15
+ when :one_to_many
16
+ !model.db[name].where(Hash[rel[:keys].zip(keys)]).empty?
17
+ when :many_to_many
18
+ !model.db[rel[:join_table]].where(Hash[rel[:left_keys].zip(keys)]).empty?
19
+ when :many_to_one
20
+ when :one_to_one
21
+ else
22
+ unsupported_association rel[:type]
23
+ end
24
+ end
25
+ raise Sequel::DestroyFailed.new("Blokujące relacje: #{restrict.map{|name, rel| name}.join(', ')}" ) unless restrict.empty?
26
+
27
+ rec = model.call(Hash[model.primary_keys.zip(keys)])
28
+ rec.destroy(transaction: false)
29
+ # model.where(model.primary_keys_hash(keys)).delete # model.dataset[model.primary_key => id].delete
30
+ end
31
+ end
32
+ {}
33
+ rescue Sequel::NoExistingObject
34
+ handler.halt_not_found LOCS[:no_entry]
35
+ rescue Sequel::DestroyFailed => failure
36
+ handler.halt_forbidden failure.error.to_s
37
+ end
38
+ end
39
+
40
+ end
41
+
42
+ class DeleteMeta < DeleteMetaBase
43
+ http_method :delete
44
+ meta_type :delete
45
+
46
+ def pre_run
47
+ super
48
+ action.parent.parent.*.menu(:item_menu).option :confirm_delete, icon: "trash", show: "action.selected_size() == 0", button_loc: false
49
+ end
50
+
51
+ def invoke handler
52
+ handler.permit id = handler.params[:id]
53
+ invoke_delete_db(handler, [id])
54
+ end
55
+ end
56
+
57
+ class BulkDeleteMeta < DeleteMetaBase
58
+ http_method :delete
59
+ meta_type :bulk_delete
60
+
61
+ def pre_run
62
+ super
63
+ action.parent.parent.*.menu(:menu).option_after :default_order, :confirm_bulk_delete, icon: "trash", show: "action.selected_size() > 0"
64
+ end
65
+
66
+ def invoke handler
67
+ ids = handler.param_to_json(:ids)
68
+ handler.permit ids.is_a?(Array)
69
+ invoke_delete_db(handler, ids)
70
+ end
71
+ end
72
+
73
+ end
@@ -0,0 +1,144 @@
1
+ # coding: utf-8
2
+
3
+ module Engine2
4
+ class FormMeta < Meta
5
+ include MetaAPISupport, MetaModelSupport, MetaQuerySupport, MetaTabSupport, MetaPanelSupport, MetaMenuSupport, MetaAngularSupport, MetaOnChangeSupport
6
+
7
+ def field_template template
8
+ panel[:field_template] = template
9
+ end
10
+
11
+ def pre_run
12
+ super
13
+ panel_template 'scaffold/form'
14
+ field_template 'scaffold/fields'
15
+ panel_class 'modal-large'
16
+
17
+ menu :panel_menu do
18
+ option :approve, icon: "ok", disabled: 'action.action_pending' # text: true,
19
+ option :cancel, icon: "remove" # text: true,
20
+ end
21
+ # modal_action false
22
+ end
23
+
24
+ def record handler, record
25
+ end
26
+
27
+ def post_process
28
+ if fields = @meta[:fields]
29
+ fields = fields - static.get[:fields] if dynamic?
30
+
31
+ decorate(fields)
32
+
33
+ fields.each do |name|
34
+ # type_info = model.type_info.fetch(name)
35
+ type_info = get_type_info(name)
36
+
37
+ info[name][:render] ||= begin
38
+ renderer = DefaultFormRenderers[type_info[:type]] # .merge(default: true)
39
+ raise E2Error.new("No form renderer found for field '#{type_info[:name]}' of type '#{type_info[:type]}'") unless renderer
40
+ renderer.(self, type_info)
41
+ end
42
+
43
+ proc = FormRendererPostProcessors[type_info[:type]]
44
+ proc.(self, name, type_info) if proc
45
+ end
46
+
47
+ assoc = assets[:assoc]
48
+ if assoc && assoc[:type] == :one_to_many
49
+ # fields.select{|f| assoc[:keys].include? f}.each do |key|
50
+ # # hide_fields(key) if self[:info, key, :hidden] == nil
51
+ # info! key, disabled: true
52
+ # end
53
+ assoc[:keys].each do |key|
54
+ info! key, disabled: true if fields.include? key
55
+ end
56
+ end
57
+ end
58
+
59
+ super
60
+ end
61
+
62
+ def post_run
63
+ super
64
+ @meta[:primary_fields] = assets[:model].primary_keys
65
+ end
66
+
67
+ def template
68
+ Templates
69
+ end
70
+
71
+ def hr_after field, message = '-'
72
+ info! field, hr: message
73
+ end
74
+ end
75
+
76
+ class CreateMeta < FormMeta
77
+ meta_type :create
78
+
79
+ def pre_run
80
+ super
81
+ panel_title LOCS[:create_title]
82
+ action.parent.*.menu(:menu).option_at 0, action.name, icon: "plus-sign", button_loc: false
83
+
84
+ hide_pk unless assets[:model].natural_key
85
+ end
86
+
87
+ def record handler, record
88
+ create_record(handler, record)
89
+ end
90
+
91
+ def create_record handler, record
92
+ end
93
+
94
+ def invoke handler
95
+ record = {}
96
+ if assoc = assets[:assoc]
97
+ case assoc[:type]
98
+ when :one_to_many
99
+ handler.permit parent = handler.params[:parent_id]
100
+ assoc[:keys].zip(split_keys(parent)).each{|key, val| record[key] = val}
101
+ end
102
+ end
103
+ static.record(handler, record)
104
+ {record: record, new: true}
105
+ end
106
+ end
107
+
108
+ class ModifyMeta < FormMeta
109
+ meta_type :modify
110
+
111
+ def pre_run
112
+ super
113
+ panel_title LOCS[:modify_title]
114
+ action.parent.*.menu(:item_menu).option action.name, icon: "pencil", button_loc: false
115
+ end
116
+
117
+ def record handler, record
118
+ modify_record(handler, record)
119
+ end
120
+
121
+ def modify_record handler, record
122
+ end
123
+
124
+ def invoke handler
125
+ handler.permit id = handler.params[:id]
126
+ record = get_query[assets[:model].primary_keys_hash_qualified(split_keys(id))]
127
+
128
+ if record
129
+ static.record(handler, record)
130
+ {record: record}
131
+ else
132
+ handler.halt_not_found LOCS[:no_entry]
133
+ end
134
+ end
135
+
136
+ def post_run
137
+ super
138
+ assets[:model].primary_keys.each do |key| # pre_run ?
139
+ info! key, disabled: true
140
+ end
141
+ end
142
+ end
143
+
144
+ end
@@ -0,0 +1,292 @@
1
+ # coding: utf-8
2
+
3
+ module Engine2
4
+ SCHEMES::define_scheme :infra! do |user_info_model = UserInfo|
5
+ define_action :infra! do
6
+ self.* do
7
+ extend MetaPanelSupport, MetaMenuSupport
8
+ panel_panel_template false
9
+ panel_template 'infra/index'
10
+ menu :menu do
11
+ properties group_class: "btn-group-sm"
12
+ option :inspect_modal, icon: :wrench, button_loc: false # , show: "action.logged_on"
13
+
14
+ option :logout_form, icon: :"log-out" # , show: "action.logged_on"
15
+ option :login_form, icon: :"log-in" # , show: "!action.logged_on"
16
+ end
17
+
18
+ @meta_type = :infra
19
+
20
+ def invoke handler
21
+ user = handler.user
22
+ {user: user ? user.to_hash : nil}
23
+ end
24
+ end
25
+
26
+ define_action :login_form, LoginFormMeta, model: user_info_model do
27
+ access!{|h|!h.logged_in?}
28
+ define_action :login, LoginMeta
29
+ end
30
+
31
+ define_action :logout_form, LogoutFormMeta do
32
+ access! &:logged_in?
33
+ define_action :logout, LogoutMeta
34
+ end
35
+
36
+ define_action :inspect_modal do
37
+ access! &:logged_in?
38
+ # access!{|h|h.user[:name] == 'admin'}
39
+ self.* do
40
+ extend MetaPanelSupport, MetaMenuSupport
41
+ modal_action
42
+ panel_template 'infra/inspect'
43
+ panel_title "Inspect"
44
+ panel_class "modal-huge"
45
+ menu(:panel_menu).option :cancel, icon: "remove"
46
+
47
+ # def invoke handler;{};end
48
+ end
49
+ define_action :inspect do
50
+ self.* do
51
+ @meta_type = :inspect
52
+ end
53
+
54
+ define_action :models do
55
+ def (self.*).invoke handler
56
+ {models: Sequel::DATABASES.map{|db| {name: db.uri, models: db.models.keys} }}
57
+ end
58
+ end
59
+
60
+ define_action :model_info do
61
+ def (self.*).invoke handler
62
+ db_name = handler.params[:db]
63
+ handler.permit db = Sequel::DATABASES.find{|d|d.uri == db_name || (d.uri && d.uri.start_with?(db_name))}
64
+ handler.permit model = db.models[handler.params[:model].to_sym]
65
+ {
66
+ model: {
67
+ info: {
68
+ name: model.to_s,
69
+ table: model.table_name
70
+ },
71
+ assoc: model.association_reflections,
72
+ schema: model.db_schema,
73
+ type_info: model.type_info
74
+ }
75
+ }
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ SCHEMES::define_scheme :menu! do
84
+ define_action :menu!, MenuMeta do
85
+ end
86
+ end
87
+
88
+ class FileStoreMeta < Meta
89
+ meta_type :file_store
90
+
91
+ attr_accessor :model, :field
92
+
93
+ def invoke handler
94
+ handler.permit owner = handler.params[:owner]
95
+ {files: E2Files.db[:files].select(:id, :name, :mime, :uploaded).where(owner: owner, model: model.name.to_s, field: field.to_s).all}
96
+ end
97
+ end
98
+
99
+
100
+ module BlobSupportMeta
101
+ def serve_blob handler, entry, inf
102
+ handler.permit entry
103
+ handler.attachment entry[inf[:name_field]]
104
+ handler.content_type (entry[inf[:mime_field]].to_s.empty? ? "application/octet-stream" : entry[inf[:mime_field]])
105
+ entry[inf[:bytes_field]].getBinaryStream().to_io.read
106
+ end
107
+ end
108
+
109
+ class DownloadFileStoreMeta < Meta
110
+ meta_type :download
111
+
112
+ def invoke handler
113
+ handler.permit id = handler.params[:id]
114
+ entry = E2Files.db[:files].select(:name, :mime)[id: id]
115
+ handler.permit entry
116
+ handler.attachment entry[:name]
117
+ handler.content_type (entry[:mime].to_s.empty? ? "application/octet-stream" : entry[:mime])
118
+ info = action.parent.*.model.type_info[action.parent.*.field]
119
+ open("#{info[:store][:files]}/#{entry[:name]}_#{id}", 'rb'){|f|f.read}
120
+ end
121
+ end
122
+
123
+ class UploadFileStoreMeta < Meta
124
+ http_method :post
125
+ meta_type :upload
126
+
127
+ def invoke handler
128
+ file = handler.params[:file]
129
+ temp = file[:tempfile]
130
+ temp.close
131
+ rackname = File.basename(temp.path)
132
+ info = action.parent.*.model.type_info[action.parent.*.field]
133
+ File.rename(temp.path, "#{info[:store][:upload]}/#{rackname}")
134
+ {rackname: rackname}
135
+ end
136
+ end
137
+
138
+ class BlobStoreMeta < Meta
139
+ meta_type :blob_store
140
+
141
+ attr_accessor :model, :field
142
+
143
+ def invoke handler
144
+ handler.permit id = handler.params[:owner]
145
+ inf = model.type_info[field]
146
+ result = model.naked.select(inf[:name_field], Sequel.char_length(inf[:bytes_field]).as(:length)).where(model.primary_keys_hash(split_keys(id))).first
147
+ handler.permit result
148
+ {file_name: result[inf[:name_field]], blob_length: result[:length]}
149
+ end
150
+ end
151
+
152
+ class DownloadBlobStoreMeta < Meta
153
+ include BlobSupportMeta
154
+ meta_type :download_blob
155
+
156
+ def invoke handler
157
+ model = action.parent.*.model
158
+ inf = model.type_info[action.parent.*.field]
159
+ handler.permit id = handler.params[:id]
160
+
161
+ entry = model.naked.select(inf[:bytes_field], inf[:name_field], inf[:mime_field]).where(model.primary_keys_hash(split_keys(id))).first
162
+ serve_blob(handler, entry, inf)
163
+ end
164
+ end
165
+
166
+ class UploadBlobStoreMeta < Meta
167
+ http_method :post
168
+ meta_type :upload_blob
169
+
170
+ def invoke handler
171
+ file = handler.params[:file]
172
+ temp = file[:tempfile]
173
+ temp.close
174
+ rackname = File.basename(temp.path)
175
+ info = action.parent.*.model.type_info[action.parent.*.field]
176
+ File.rename(temp.path, "#{info[:store][:upload]}/#{rackname}")
177
+ {rackname: rackname}
178
+ end
179
+ end
180
+
181
+ class ForeignBlobStoreMeta < Meta
182
+ meta_type :blob_store
183
+
184
+ attr_accessor :model, :field
185
+
186
+ def invoke handler
187
+ handler.permit id = handler.params[:owner]
188
+
189
+ inf = model.type_info[field]
190
+ assoc = model.association_reflections[inf[:assoc_name]]
191
+ blob_model = Object.const_get(assoc[:class_name])
192
+
193
+ rec = model.naked.select(assoc[:key]).where(model.primary_keys_hash(split_keys(id))).first
194
+ handler.permit rec
195
+ result = blob_model.naked.select(inf[:name_field], Sequel.char_length(inf[:bytes_field]).as(:length)).where(blob_model.primary_key => rec[assoc[:key]]).first
196
+
197
+ # handler.permit result
198
+ {file_name: result ? result[inf[:name_field]] : :empty, blob_length: result ? result[:length] : 0}
199
+ end
200
+ end
201
+
202
+ class DownloadForeignBlobStoreMeta < Meta
203
+ include BlobSupportMeta
204
+ meta_type :download_blob
205
+
206
+ def invoke handler
207
+ model = action.parent.*.model
208
+ inf = model.type_info[action.parent.*.field]
209
+ assoc = model.association_reflections[inf[:assoc_name]]
210
+ blob_model = Object.const_get(assoc[:class_name])
211
+ handler.permit id = handler.params[:id]
212
+ rec = model.naked.select(assoc[:key]).where(model.primary_keys_hash(split_keys(id))).first
213
+ handler.permit rec
214
+
215
+ entry = blob_model.naked.select(inf[:bytes_field], inf[:name_field], inf[:mime_field]).where(blob_model.primary_key => rec[assoc[:key]]).first
216
+ serve_blob(handler, entry, inf)
217
+ end
218
+ end
219
+
220
+ # class UploadForeignBlobStoreMeta < Meta
221
+ # http_method :post
222
+ # meta_type :upload_blob
223
+
224
+ # def invoke handler
225
+ # file = handler.params[:file]
226
+ # temp = file[:tempfile]
227
+ # temp.close
228
+ # rackname = File.basename(temp.path)
229
+ # File.rename(temp.path, "#{UPLOAD_DIR}/#{rackname}")
230
+ # {rackname: rackname}
231
+ # end
232
+ # end
233
+
234
+ class LoginFormMeta < FormMeta
235
+ meta_type :login_form
236
+
237
+ def pre_run
238
+ super
239
+ panel_class 'modal-default'
240
+ panel_title LOCS[:login_title]
241
+ info! :name, loc: LOCS[:user_name]
242
+ menu(:panel_menu).modify_option :approve, name: :login, icon: :"log-in"
243
+ end
244
+
245
+ def invoke handler
246
+ {record: {}, new: true}
247
+ end
248
+ end
249
+
250
+ class LoginMeta < Meta
251
+ include MetaApproveSupport
252
+ http_method :post
253
+ meta_type :login
254
+
255
+ def after_approve handler, record
256
+ handler.session[:user] = record
257
+ end
258
+
259
+ def record handler, record
260
+ {errors: nil, user: handler.user.to_hash}
261
+ end
262
+ end
263
+
264
+ class LogoutFormMeta < Meta
265
+ include MetaPanelSupport, MetaMenuSupport
266
+ meta_type :logout_form
267
+ def pre_run
268
+ super
269
+ panel_template 'scaffold/message'
270
+ panel_title LOCS[:logout_title]
271
+ panel_class 'modal-default'
272
+ menu :panel_menu do
273
+ option :logout, icon: "ok", loc: LOCS[:ok]
274
+ option :cancel, icon: "remove"
275
+ end
276
+ end
277
+
278
+ def invoke handler
279
+ {message: LOCS[:logout_message]}
280
+ end
281
+ end
282
+
283
+ class LogoutMeta < Meta
284
+ meta_type :logout
285
+
286
+ def invoke handler
287
+ handler.session.clear
288
+ # handler.session[:logged_in] = false
289
+ {}
290
+ end
291
+ end
292
+ end