engine2 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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