netzke-basepack-zh 0.7.6

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 (268) hide show
  1. data/.autotest +1 -0
  2. data/.travis.yml +11 -0
  3. data/CHANGELOG.rdoc +445 -0
  4. data/LICENSE +1 -0
  5. data/README.md +94 -0
  6. data/Rakefile +44 -0
  7. data/TODO.rdoc +8 -0
  8. data/config/ci/before-travis.sh +28 -0
  9. data/init.rb +1 -0
  10. data/install.rb +1 -0
  11. data/javascripts/basepack.js +139 -0
  12. data/javascripts/xdatetime.js +196 -0
  13. data/lib/generators/netzke/basepack_generator.rb +10 -0
  14. data/lib/generators/netzke/templates/assets/ts-checkbox.gif +0 -0
  15. data/lib/generators/netzke/templates/create_netzke_field_lists.rb +18 -0
  16. data/lib/netzke-basepack.rb +26 -0
  17. data/lib/netzke/active_record.rb +20 -0
  18. data/lib/netzke/active_record/attributes.rb +256 -0
  19. data/lib/netzke/active_record/combobox_options.rb +16 -0
  20. data/lib/netzke/active_record/relation_extensions.rb +37 -0
  21. data/lib/netzke/basepack.rb +45 -0
  22. data/lib/netzke/basepack/accordion_panel.rb +39 -0
  23. data/lib/netzke/basepack/action_column.rb +68 -0
  24. data/lib/netzke/basepack/action_column/javascripts/action_column.js +61 -0
  25. data/lib/netzke/basepack/auth_app.rb +159 -0
  26. data/lib/netzke/basepack/basic_app.rb +7 -0
  27. data/lib/netzke/basepack/border_layout_panel.rb +53 -0
  28. data/lib/netzke/basepack/border_layout_panel/javascripts/border_layout_panel.js +40 -0
  29. data/lib/netzke/basepack/data_accessor.rb +53 -0
  30. data/lib/netzke/basepack/data_adapters/abstract_adapter.rb +164 -0
  31. data/lib/netzke/basepack/data_adapters/active_record_adapter.rb +279 -0
  32. data/lib/netzke/basepack/data_adapters/data_mapper_adapter.rb +264 -0
  33. data/lib/netzke/basepack/data_adapters/sequel_adapter.rb +260 -0
  34. data/lib/netzke/basepack/form_panel.rb +144 -0
  35. data/lib/netzke/basepack/form_panel/fields.rb +208 -0
  36. data/lib/netzke/basepack/form_panel/javascripts/comma_list_cbg.js +51 -0
  37. data/lib/netzke/basepack/form_panel/javascripts/form_panel.js +225 -0
  38. data/lib/netzke/basepack/form_panel/javascripts/misc.js +4 -0
  39. data/lib/netzke/basepack/form_panel/javascripts/n_radio_group.js +43 -0
  40. data/lib/netzke/basepack/form_panel/javascripts/readonly_mode.js +35 -0
  41. data/lib/netzke/basepack/form_panel/services.rb +142 -0
  42. data/lib/netzke/basepack/form_panel/stylesheets/readonly_mode.css +14 -0
  43. data/lib/netzke/basepack/grid_panel.rb +440 -0
  44. data/lib/netzke/basepack/grid_panel/columns.rb +394 -0
  45. data/lib/netzke/basepack/grid_panel/javascripts/advanced_search.js +27 -0
  46. data/lib/netzke/basepack/grid_panel/javascripts/check_column_fix.js +6 -0
  47. data/lib/netzke/basepack/grid_panel/javascripts/edit_in_form.js +51 -0
  48. data/lib/netzke/basepack/grid_panel/javascripts/event_handling.js +179 -0
  49. data/lib/netzke/basepack/grid_panel/javascripts/grid_panel.js +438 -0
  50. data/lib/netzke/basepack/grid_panel/javascripts/misc.js +4 -0
  51. data/lib/netzke/basepack/grid_panel/javascripts/rows-dd.js +281 -0
  52. data/lib/netzke/basepack/grid_panel/record_form_window.rb +41 -0
  53. data/lib/netzke/basepack/grid_panel/services.rb +235 -0
  54. data/lib/netzke/basepack/paging_form_panel.rb +72 -0
  55. data/lib/netzke/basepack/paging_form_panel/javascripts/paging_form_panel.js +76 -0
  56. data/lib/netzke/basepack/panel.rb +11 -0
  57. data/lib/netzke/basepack/query_builder.rb +107 -0
  58. data/lib/netzke/basepack/query_builder/javascripts/query_builder.js +153 -0
  59. data/lib/netzke/basepack/search_panel.rb +79 -0
  60. data/lib/netzke/basepack/search_panel/javascripts/condition_field.js +160 -0
  61. data/lib/netzke/basepack/search_panel/javascripts/search_panel.js +65 -0
  62. data/lib/netzke/basepack/search_window.rb +66 -0
  63. data/lib/netzke/basepack/simple_app.rb +104 -0
  64. data/lib/netzke/basepack/simple_app/javascripts/simple_app.js +64 -0
  65. data/lib/netzke/basepack/simple_app/javascripts/statusbar_ext.js +8 -0
  66. data/lib/netzke/basepack/tab_panel.rb +21 -0
  67. data/lib/netzke/basepack/tab_panel/javascripts/tab_panel.js +11 -0
  68. data/lib/netzke/basepack/version.rb +11 -0
  69. data/lib/netzke/basepack/window.rb +29 -0
  70. data/lib/netzke/basepack/window/javascripts/window.js +20 -0
  71. data/lib/netzke/basepack/wrap_lazy_loaded.rb +28 -0
  72. data/lib/netzke/basepack/wrapper.rb +28 -0
  73. data/lib/netzke/data_mapper.rb +18 -0
  74. data/lib/netzke/data_mapper/attributes.rb +273 -0
  75. data/lib/netzke/data_mapper/combobox_options.rb +11 -0
  76. data/lib/netzke/data_mapper/relation_extensions.rb +38 -0
  77. data/lib/netzke/sequel.rb +18 -0
  78. data/lib/netzke/sequel/attributes.rb +274 -0
  79. data/lib/netzke/sequel/combobox_options.rb +10 -0
  80. data/lib/netzke/sequel/relation_extensions.rb +40 -0
  81. data/lib/tasks/netzke_basepack_tasks.rake +4 -0
  82. data/locales/de.yml +79 -0
  83. data/locales/en.yml +79 -0
  84. data/netzke-basepack.gemspec +306 -0
  85. data/stylesheets/basepack.css +72 -0
  86. data/stylesheets/datetimefield.css +54 -0
  87. data/test/basepack_test_app/.gitignore +6 -0
  88. data/test/basepack_test_app/.rvmrc +1 -0
  89. data/test/basepack_test_app/Gemfile +59 -0
  90. data/test/basepack_test_app/Gemfile.lock +196 -0
  91. data/test/basepack_test_app/Guardfile +46 -0
  92. data/test/basepack_test_app/README +1 -0
  93. data/test/basepack_test_app/Rakefile +7 -0
  94. data/test/basepack_test_app/app/components/author_form.rb +32 -0
  95. data/test/basepack_test_app/app/components/author_grid.rb +3 -0
  96. data/test/basepack_test_app/app/components/book_form.rb +38 -0
  97. data/test/basepack_test_app/app/components/book_form_with_custom_fields.rb +21 -0
  98. data/test/basepack_test_app/app/components/book_form_with_defaults.rb +8 -0
  99. data/test/basepack_test_app/app/components/book_form_with_nested_attributes.rb +18 -0
  100. data/test/basepack_test_app/app/components/book_grid.rb +12 -0
  101. data/test/basepack_test_app/app/components/book_grid_filtering.rb +10 -0
  102. data/test/basepack_test_app/app/components/book_grid_loader.rb +24 -0
  103. data/test/basepack_test_app/app/components/book_grid_with_column_actions.rb +15 -0
  104. data/test/basepack_test_app/app/components/book_grid_with_custom_columns.rb +27 -0
  105. data/test/basepack_test_app/app/components/book_grid_with_default_values.rb +9 -0
  106. data/test/basepack_test_app/app/components/book_grid_with_extra_feedback.rb +8 -0
  107. data/test/basepack_test_app/app/components/book_grid_with_extra_filters.rb +14 -0
  108. data/test/basepack_test_app/app/components/book_grid_with_nested_attributes.rb +13 -0
  109. data/test/basepack_test_app/app/components/book_grid_with_overridden_columns.rb +15 -0
  110. data/test/basepack_test_app/app/components/book_grid_with_paging.rb +10 -0
  111. data/test/basepack_test_app/app/components/book_grid_with_persistence.rb +8 -0
  112. data/test/basepack_test_app/app/components/book_grid_with_scoped_authors.rb +8 -0
  113. data/test/basepack_test_app/app/components/book_grid_with_virtual_attributes.rb +21 -0
  114. data/test/basepack_test_app/app/components/book_paging_form_panel.rb +22 -0
  115. data/test/basepack_test_app/app/components/book_query_builder.rb +8 -0
  116. data/test/basepack_test_app/app/components/book_search_panel.rb +5 -0
  117. data/test/basepack_test_app/app/components/book_search_panel/javascripts/i18n_de.js +6 -0
  118. data/test/basepack_test_app/app/components/book_with_custom_primary_key_grid.rb +10 -0
  119. data/test/basepack_test_app/app/components/books_bound_to_author.rb +10 -0
  120. data/test/basepack_test_app/app/components/double_book_grid.rb +18 -0
  121. data/test/basepack_test_app/app/components/extras/book_presentation.rb +27 -0
  122. data/test/basepack_test_app/app/components/form_without_model.rb +21 -0
  123. data/test/basepack_test_app/app/components/generic_user_form.rb +12 -0
  124. data/test/basepack_test_app/app/components/lockable_book_form.rb +17 -0
  125. data/test/basepack_test_app/app/components/lockable_user_form.rb +7 -0
  126. data/test/basepack_test_app/app/components/paging_form_with_search.rb +40 -0
  127. data/test/basepack_test_app/app/components/simple_accordion.rb +11 -0
  128. data/test/basepack_test_app/app/components/simple_panel.rb +17 -0
  129. data/test/basepack_test_app/app/components/simple_tab_panel.rb +11 -0
  130. data/test/basepack_test_app/app/components/simple_window.rb +10 -0
  131. data/test/basepack_test_app/app/components/simple_wrapper.rb +7 -0
  132. data/test/basepack_test_app/app/components/some_accordion_panel.rb +22 -0
  133. data/test/basepack_test_app/app/components/some_auth_app.rb +32 -0
  134. data/test/basepack_test_app/app/components/some_border_layout.rb +28 -0
  135. data/test/basepack_test_app/app/components/some_simple_app.rb +35 -0
  136. data/test/basepack_test_app/app/components/some_tab_panel.rb +20 -0
  137. data/test/basepack_test_app/app/components/user_form.rb +25 -0
  138. data/test/basepack_test_app/app/components/user_form_with_default_fields.rb +8 -0
  139. data/test/basepack_test_app/app/components/user_grid.rb +8 -0
  140. data/test/basepack_test_app/app/components/user_grid_with_customized_form_fields.rb +18 -0
  141. data/test/basepack_test_app/app/components/window_component_loader.rb +27 -0
  142. data/test/basepack_test_app/app/controllers/application_controller.rb +9 -0
  143. data/test/basepack_test_app/app/controllers/components_controller.rb +10 -0
  144. data/test/basepack_test_app/app/controllers/welcome_controller.rb +9 -0
  145. data/test/basepack_test_app/app/helpers/application_helper.rb +8 -0
  146. data/test/basepack_test_app/app/helpers/embedded_components_helper.rb +2 -0
  147. data/test/basepack_test_app/app/models/address.rb +29 -0
  148. data/test/basepack_test_app/app/models/author.rb +38 -0
  149. data/test/basepack_test_app/app/models/book.rb +49 -0
  150. data/test/basepack_test_app/app/models/book_with_custom_primary_key.rb +26 -0
  151. data/test/basepack_test_app/app/models/role.rb +24 -0
  152. data/test/basepack_test_app/app/models/user.rb +29 -0
  153. data/test/basepack_test_app/app/presenters/forms/generic_user.rb +6 -0
  154. data/test/basepack_test_app/app/views/components/loadable_window.html.erb +9 -0
  155. data/test/basepack_test_app/app/views/components/simple_panel.html.erb +1 -0
  156. data/test/basepack_test_app/app/views/layouts/application.html.erb +12 -0
  157. data/test/basepack_test_app/app/views/layouts/components.html.erb +13 -0
  158. data/test/basepack_test_app/app/views/layouts/nested.html.erb +5 -0
  159. data/test/basepack_test_app/app/views/welcome/index.html.erb +10 -0
  160. data/test/basepack_test_app/config.ru +4 -0
  161. data/test/basepack_test_app/config/application.rb +57 -0
  162. data/test/basepack_test_app/config/boot.rb +13 -0
  163. data/test/basepack_test_app/config/cucumber.yml +8 -0
  164. data/test/basepack_test_app/config/database.yml.sample +41 -0
  165. data/test/basepack_test_app/config/database.yml.travis +15 -0
  166. data/test/basepack_test_app/config/environment.rb +6 -0
  167. data/test/basepack_test_app/config/environments/development.rb +22 -0
  168. data/test/basepack_test_app/config/environments/production.rb +49 -0
  169. data/test/basepack_test_app/config/environments/test.rb +35 -0
  170. data/test/basepack_test_app/config/initializers/backtrace_silencers.rb +7 -0
  171. data/test/basepack_test_app/config/initializers/data_mapper_logging.rb +3 -0
  172. data/test/basepack_test_app/config/initializers/inflections.rb +10 -0
  173. data/test/basepack_test_app/config/initializers/mime_types.rb +5 -0
  174. data/test/basepack_test_app/config/initializers/netzke.rb +9 -0
  175. data/test/basepack_test_app/config/initializers/secret_token.rb +7 -0
  176. data/test/basepack_test_app/config/initializers/sequel.rb +26 -0
  177. data/test/basepack_test_app/config/initializers/session_store.rb +8 -0
  178. data/test/basepack_test_app/config/locales/de.yml +35 -0
  179. data/test/basepack_test_app/config/locales/es.yml +96 -0
  180. data/test/basepack_test_app/config/routes.rb +68 -0
  181. data/test/basepack_test_app/db/development_structure.sql +88 -0
  182. data/test/basepack_test_app/db/migrate/20100914104207_create_users.rb +15 -0
  183. data/test/basepack_test_app/db/migrate/20100914104236_create_roles.rb +13 -0
  184. data/test/basepack_test_app/db/migrate/20101026185816_create_authors.rb +14 -0
  185. data/test/basepack_test_app/db/migrate/20101026190021_create_books.rb +19 -0
  186. data/test/basepack_test_app/db/migrate/20110101143818_create_addresses.rb +17 -0
  187. data/test/basepack_test_app/db/migrate/20110213213050_create_netzke_component_states.rb +20 -0
  188. data/test/basepack_test_app/db/migrate/20110701070052_create_book_with_custom_primary_keys.rb +15 -0
  189. data/test/basepack_test_app/db/migrate/20110901114016_add_last_read_at_to_books.rb +9 -0
  190. data/test/basepack_test_app/db/migrate/20110909071740_add_published_on_to_books.rb +5 -0
  191. data/test/basepack_test_app/db/schema.rb +81 -0
  192. data/test/basepack_test_app/db/seeds.rb +44 -0
  193. data/test/basepack_test_app/features/accordion_panel.feature +12 -0
  194. data/test/basepack_test_app/features/components_in_view.feature +11 -0
  195. data/test/basepack_test_app/features/form_panel.feature +142 -0
  196. data/test/basepack_test_app/features/grid_panel.feature +277 -0
  197. data/test/basepack_test_app/features/grid_panel_filters.feature +73 -0
  198. data/test/basepack_test_app/features/grid_panel_with_custom_primary_key.feature +15 -0
  199. data/test/basepack_test_app/features/grid_sorting.feature +47 -0
  200. data/test/basepack_test_app/features/i18n.feature +18 -0
  201. data/test/basepack_test_app/features/nested_attributes.feature +26 -0
  202. data/test/basepack_test_app/features/paging_form_panel.feature +43 -0
  203. data/test/basepack_test_app/features/search_in_grid.feature +49 -0
  204. data/test/basepack_test_app/features/simple_app.feature +15 -0
  205. data/test/basepack_test_app/features/simple_panel.feature +11 -0
  206. data/test/basepack_test_app/features/step_definitions/accordion_steps.rb +5 -0
  207. data/test/basepack_test_app/features/step_definitions/ext_steps.rb +16 -0
  208. data/test/basepack_test_app/features/step_definitions/form_panel_steps.rb +46 -0
  209. data/test/basepack_test_app/features/step_definitions/generic_steps.rb +44 -0
  210. data/test/basepack_test_app/features/step_definitions/grid_panel_steps.rb +186 -0
  211. data/test/basepack_test_app/features/step_definitions/pickle_steps.rb +100 -0
  212. data/test/basepack_test_app/features/step_definitions/web_steps.rb +219 -0
  213. data/test/basepack_test_app/features/support/env.rb +81 -0
  214. data/test/basepack_test_app/features/support/paths.rb +65 -0
  215. data/test/basepack_test_app/features/support/pickle.rb +24 -0
  216. data/test/basepack_test_app/features/support/selectors.rb +39 -0
  217. data/test/basepack_test_app/features/tab_panel.feature +12 -0
  218. data/test/basepack_test_app/features/validations_in_grid.feature +13 -0
  219. data/test/basepack_test_app/features/virtual_attributes.feature +16 -0
  220. data/test/basepack_test_app/features/window.feature +11 -0
  221. data/test/basepack_test_app/lib/tasks/.gitkeep +0 -0
  222. data/test/basepack_test_app/lib/tasks/cucumber.rake +71 -0
  223. data/test/basepack_test_app/lib/tasks/travis.rake +7 -0
  224. data/test/basepack_test_app/public/404.html +26 -0
  225. data/test/basepack_test_app/public/422.html +26 -0
  226. data/test/basepack_test_app/public/500.html +26 -0
  227. data/test/basepack_test_app/public/favicon.ico +0 -0
  228. data/test/basepack_test_app/public/images/header-deco.gif +0 -0
  229. data/test/basepack_test_app/public/images/rails.png +0 -0
  230. data/test/basepack_test_app/public/javascripts/application.js +2 -0
  231. data/test/basepack_test_app/public/javascripts/controls.js +965 -0
  232. data/test/basepack_test_app/public/javascripts/dragdrop.js +974 -0
  233. data/test/basepack_test_app/public/javascripts/effects.js +1123 -0
  234. data/test/basepack_test_app/public/javascripts/prototype.js +6001 -0
  235. data/test/basepack_test_app/public/javascripts/rails.js +175 -0
  236. data/test/basepack_test_app/public/robots.txt +5 -0
  237. data/test/basepack_test_app/public/stylesheets/.gitkeep +0 -0
  238. data/test/basepack_test_app/script/cucumber +10 -0
  239. data/test/basepack_test_app/script/rails +6 -0
  240. data/test/basepack_test_app/spec/components/form_panel_spec.rb +53 -0
  241. data/test/basepack_test_app/spec/components/grid_panel_spec.rb +10 -0
  242. data/test/basepack_test_app/spec/data_adapter/adapter_spec.rb +68 -0
  243. data/test/basepack_test_app/spec/data_adapter/attributes_spec.rb +56 -0
  244. data/test/basepack_test_app/spec/data_adapter/relation_extensions_spec.rb +125 -0
  245. data/test/basepack_test_app/spec/factories.rb +28 -0
  246. data/test/basepack_test_app/spec/spec_helper.rb +39 -0
  247. data/test/basepack_test_app/test/performance/browsing_test.rb +9 -0
  248. data/test/basepack_test_app/test/test_helper.rb +13 -0
  249. data/test/basepack_test_app/vendor/plugins/.gitkeep +0 -0
  250. data/test/console_with_fixtures.rb +4 -0
  251. data/test/fixtures/books.yml +11 -0
  252. data/test/fixtures/categories.yml +7 -0
  253. data/test/fixtures/cities.yml +21 -0
  254. data/test/fixtures/continents.yml +7 -0
  255. data/test/fixtures/countries.yml +9 -0
  256. data/test/fixtures/genres.yml +9 -0
  257. data/test/fixtures/roles.yml +8 -0
  258. data/test/fixtures/users.yml +11 -0
  259. data/test/schema.rb +10 -0
  260. data/test/test_helper.rb +21 -0
  261. data/test/unit/accordion_panel_test.rb +20 -0
  262. data/test/unit/active_record_basepack_test.rb +54 -0
  263. data/test/unit/fields_configuration_test.rb +18 -0
  264. data/test/unit/grid_panel_test.rb +52 -0
  265. data/test/unit/netzke_basepack_test.rb +4 -0
  266. data/test/unit/tab_panel_test.rb +21 -0
  267. data/uninstall.rb +1 -0
  268. metadata +332 -0
@@ -0,0 +1,260 @@
1
+ module Netzke::Basepack::DataAdapters
2
+ class SequelAdapter < AbstractAdapter
3
+ def self.for_class?(model_class)
4
+ model_class <= Sequel::Model
5
+ end
6
+
7
+ def get_records(params, columns=[])
8
+ get_dataset(params, columns).all
9
+ end
10
+
11
+ def count_records(params, columns=[])
12
+ # dont pass columns, JOINs will be done as necessary for filters
13
+ get_dataset(params, [], true).count
14
+ end
15
+
16
+ def map_type type
17
+ type
18
+ end
19
+
20
+ def get_assoc_property_type assoc_name, prop_name
21
+ db_schema=class_for(assoc_name.to_sym).db_schema
22
+ # return nil if prop_name not present in db schema (virtual column)
23
+ db_schema[prop_name.to_sym] ? db_schema[prop_name.to_sym][:type] : nil
24
+ end
25
+
26
+ # like get_assoc_property_type but for non-association columns
27
+ def get_property_type column
28
+ column[:type]
29
+ end
30
+
31
+ def column_virtual? c
32
+ assoc, method = c[:name].split '__'
33
+ if method
34
+ !class_for(assoc.to_sym).columns.include? method.to_sym
35
+ else
36
+ !@model_class.columns.include? assoc.to_sym
37
+ end
38
+ end
39
+
40
+ # Returns options for comboboxes in grids/forms
41
+ def combobox_options_for_column(column, method_options = {})
42
+ query = method_options[:query]
43
+
44
+ # First, check if we have options for this column defined in persistent storage
45
+ options = column[:combobox_options] && column[:combobox_options].split("\n")
46
+ if options
47
+ query ? options.select{ |o| o.index(/^#{query}/) }.map{ |el| [el] } : options
48
+ else
49
+ assoc_name, assoc_method = column[:name].split '__'
50
+
51
+ if assoc_name
52
+ # Options for an asssociation attribute
53
+ dataset = class_for(assoc_name)
54
+
55
+ dataset = dataset.extend_with(method_options[:scope]) if method_options[:scope]
56
+
57
+ if class_for(assoc_name).column_names.include?(assoc_method)
58
+ # apply query
59
+ dataset = dataset.where(assoc_method.to_sym.like("%#{query}%")) if query.present?
60
+ dataset.all.map{ |r| [r.id, r.send(assoc_method)] }
61
+ else
62
+ dataset.all.map{ |r| [r.id, r.send(assoc_method)] }.select{ |id,value| value =~ /^#{query}/ }
63
+ end
64
+ else
65
+ # Options for a non-association attribute
66
+ res=@model_class.netzke_combo_options_for(column[:name], method_options)
67
+
68
+ # ensure it is an array-in-array, as Ext will fail otherwise
69
+ raise RuntimeError, "netzke_combo_options_for should return an Array" unless res.kind_of? Array
70
+ return [[]] if res.empty?
71
+
72
+ unless res.first.kind_of? Array
73
+ res=res.map do |v|
74
+ [v]
75
+ end
76
+ end
77
+ return res
78
+ end
79
+ end
80
+ end
81
+
82
+ def foreign_key_for assoc_name
83
+ @model_class.association_reflection(assoc_name.to_sym)[:key].to_s
84
+ end
85
+
86
+ # Returns the model class for an association
87
+ def class_for assoc_name
88
+ @model_class.association_reflection(assoc_name.to_sym)[:class_name].constantize
89
+ end
90
+
91
+ def destroy(ids)
92
+ @model_class.where(:id => ids).destroy
93
+ end
94
+
95
+ def find_record(id)
96
+ @model_class[id]
97
+ end
98
+
99
+ # Build a hash of foreign keys and the associated model
100
+ def hash_fk_model
101
+ @model_class.all_association_reflections.inject({}) do |res, assoc|
102
+ res[assoc[:key]] = assoc[:class_name].constantize.model_name.underscore.to_sym
103
+ res
104
+ end
105
+ end
106
+
107
+ # TODO: is this possible with Sequel?
108
+ def move_records(params)
109
+ end
110
+
111
+ # give the data adapter the opportunity the set special options for
112
+ # saving
113
+ def save_record(record)
114
+ # don't raise an error on saving. basepack will evaluate record.errors
115
+ # to get validation errors
116
+ record.raise_on_save_failure = false
117
+ record.save
118
+ end
119
+
120
+ # give the data adapter the opporunity to process error messages
121
+ # must return an raay of the form ["Title can't be blank", "Foo can't be blank"]
122
+ def errors_array(record)
123
+ record.errors.to_a.inject([]) do |errors, error|
124
+ field, message = error
125
+ errors << "#{record.class.human_attribute_name(field)} #{message.join ', '}"
126
+ errors
127
+ end
128
+ end
129
+
130
+ # Needed for seed and tests
131
+ def last
132
+ @model_class.last
133
+ end
134
+
135
+ # Needed for seed and tests
136
+ def destroy_all
137
+ @model_class.destroy
138
+ end
139
+
140
+ private
141
+ def get_dataset params, columns, for_count=false
142
+ dataset = @model_class
143
+
144
+ graphed=[]
145
+
146
+ # Parses and applies grid column filters
147
+ #
148
+ # Example column grid data:
149
+ #
150
+ # {"0" => {
151
+ # "data" => {
152
+ # "type" => "numeric",
153
+ # "comparison" => "gt",
154
+ # "value" => 10 },
155
+ # "field" => "id"
156
+ # },
157
+ # "1" => {
158
+ # "data" => {
159
+ # "type" => "string",
160
+ # "value" => "pizza"
161
+ # },
162
+ # "field" => "food_name"
163
+ # }}
164
+ #
165
+
166
+ if params[:filter]
167
+ # these are still JSON-encoded due to the migration to Ext.direct
168
+ column_filter=JSON.parse(params[:filter])
169
+
170
+ column_filter.each do |v|
171
+ field = v["field"]
172
+ assoc, method = field.split('__')
173
+ if method
174
+ # when filtering on association's columns, we need to graph for LEFT OUTER JOIN
175
+ dataset = dataset.eager_graph assoc.to_sym unless graphed.include? assoc.to_sym
176
+ graphed << assoc.to_sym
177
+ end
178
+
179
+ value = v["value"]
180
+ type = v["type"]
181
+ op = v["comparison"]
182
+
183
+ if type == "string"
184
+ # strings are always LIKEd (case-insensitive)
185
+ dataset = dataset.filter field.to_sym.ilike("%#{value}%")
186
+ else
187
+ if type == "date"
188
+ # convert value to the DB date
189
+ value.match /(\d\d)\/(\d\d)\/(\d\d\d\d)/
190
+ value = "#{$3}-#{$1}-#{$2}"
191
+ end
192
+ # if it's NOT an association column, we need to qualify column name with model's table_name
193
+ qualified_column_name = method ? field.to_sym : field.to_sym.qualify(@model_class.table_name)
194
+ case op
195
+ when 'lt'
196
+ dataset = dataset.filter ":column < '#{value}'", :column => qualified_column_name
197
+ when 'gt'
198
+ dataset = dataset.filter ":column > '#{value}'", :column => qualified_column_name
199
+ else
200
+ dataset = dataset.filter qualified_column_name => value
201
+ end
202
+ end
203
+ end
204
+ end
205
+ # skip sorting, eager joining and paging if dataset is used for count
206
+ unless for_count
207
+ if params[:sort] && sort_params = params[:sort]
208
+ sort_params.each do |sort_param|
209
+ assoc, method = sort_param["property"].split("__")
210
+ dir = sort_param["direction"].downcase
211
+
212
+ # if a sorting scope is set, call the scope with the given direction
213
+ column = columns.detect { |c| c[:name] == sort_param["property"] }
214
+ if column.try(:'has_key?', :sorting_scope)
215
+ dataset = dataset.send(column[:sorting_scope].to_sym, dir.to_sym)
216
+ else
217
+ if method # sorting on associations column
218
+ # graph the association for LEFT OUTER JOIN
219
+ dataset = dataset.eager_graph(assoc.to_sym) unless graphed.include? assoc.to_sym
220
+ graphed << assoc.to_sym
221
+ end
222
+ # coincidentally, netzkes convention of specifying association's attributes
223
+ # i.e. "author__name" on Book matches sequel's convention
224
+ # so we can just pass symbolized property here
225
+ dataset = dataset.order(sort_param["property"].to_sym.send(dir))
226
+ end
227
+ end
228
+ end
229
+
230
+ # eager load the associations indicated by columns,
231
+ # but only if we didn't eager_graph them before (for ordering/filtering)
232
+ # because this saves a ID IN query
233
+ columns.each do |column|
234
+ if column[:name].index('__')
235
+ assoc, _ = column[:name].split('__')
236
+ dataset = dataset.eager(assoc.to_sym) unless graphed.include? assoc.to_sym
237
+ end
238
+ end
239
+
240
+ # apply paging
241
+ if params[:limit]
242
+ if params[:start]
243
+ dataset = dataset.limit params[:limit], params[:start]
244
+ else
245
+ dataset = dataset.limit params[:limit]
246
+ end
247
+ end
248
+ end
249
+
250
+ # apply scope
251
+ # need to symbolize_keys, because when the request is made from client-side (as opposed
252
+ # to server-side on inital render), the scope's keys are given as string {"author_id" => 1}
253
+ # If we give Sequel a filter like this, it will (correctly) do WHERE 'author_id' = 1 - note the quotes
254
+ # making the database match the string author_id to 1 and to the column.
255
+ dataset = dataset.extend_with(params[:scope].symbolize_keys) if params[:scope]
256
+ dataset
257
+ end
258
+ end
259
+
260
+ end
@@ -0,0 +1,144 @@
1
+ require "netzke/basepack/form_panel/fields"
2
+ require "netzke/basepack/form_panel/services"
3
+ # require "netzke/plugins/configuration_tool"
4
+
5
+ module Netzke
6
+ module Basepack
7
+ # Ext.form.Panel-based component
8
+ #
9
+ # == Netzke-specific config options
10
+ #
11
+ # * +model+ - name of the ActiveRecord model that provides data to this GridPanel.
12
+ # * +record+ - record to be displayd in the form. Takes precedence over +:record_id+
13
+ # * +record_id+ - id of the record to be displayd in the form. Also see +:record+
14
+ # * +items+ - the layout of the fields as an array. See "Layout configuration".
15
+ # * +mode+ - render mode, accepted options:
16
+ # * +lockable+ - makes the form panel load initially in "display mode", then lets "unlock" it, change the values, and "lock" it again, while updating the values on the server
17
+ # * +updateMask+ - +Ext.LoadMask+ config options for the mask shown while the form is submitting its values
18
+ #
19
+ # === Layout configuration
20
+ #
21
+ # The layout of the form is configured by supplying the +item+ config option, same way it would be configured in Ext (thus allowing for complex form layouts). FormPanel will expand fields by looking at their names (unless +no_binding+ set to +true+ is specified for a specific field).
22
+ #
23
+ # == Endpoints
24
+ # FormPanel implements the following endpoints:
25
+ #
26
+ # * +netzke_load+ - loads a record with a given id from the server, e.g.:
27
+ #
28
+ # someFormPanel.netzkeLoad({id: 100});
29
+ #
30
+ # * +netzke_submit+ - gets called when the form gets submitted (e.g. by pressing the Apply button, or by calling onApply)
31
+ # * +get_combobox_options+ - gets called when a 'remote' combobox field gets expanded
32
+ class FormPanel < Netzke::Base
33
+
34
+ js_base_class "Ext.form.Panel"
35
+
36
+ # Class-level configuration
37
+ class_attribute :config_tool_available
38
+ self.config_tool_available = true
39
+
40
+ include self::Services
41
+ include self::Fields
42
+ include Netzke::Basepack::DataAccessor
43
+
44
+ delegates_to_dsl :model, :record_id
45
+
46
+ action :apply do
47
+ {
48
+ :text => I18n.t('netzke.basepack.form_panel.actions.apply'),
49
+ :tooltip => I18n.t('netzke.basepack.form_panel.actions.apply_tooltip'),
50
+ :icon => :tick
51
+ }
52
+ end
53
+
54
+ action :edit do
55
+ {
56
+ :text => I18n.t('netzke.basepack.form_panel.actions.edit'),
57
+ :tooltip => I18n.t('netzke.basepack.form_panel.actions.edit_tooltip'),
58
+ :icon => :pencil
59
+ }
60
+ end
61
+
62
+ action :cancel do
63
+ {
64
+ :text => I18n.t('netzke.basepack.form_panel.actions.cancel'),
65
+ :tooltip => I18n.t('netzke.basepack.form_panel.actions.cancel_tooltip'),
66
+ :icon => :cancel
67
+ }
68
+ end
69
+
70
+ def configuration
71
+ super.tap do |sup|
72
+ configure_locked(sup)
73
+ configure_bbar(sup)
74
+
75
+ sup[:record_id] = sup[:record] = nil if sup[:multi_edit] # never set record_id in multi-edit mode
76
+ end
77
+ end
78
+
79
+ def configure_locked(c)
80
+ c[:locked] = c[:locked].nil? ? (c[:mode] == :lockable) : c[:locked]
81
+ end
82
+
83
+ def configure_bbar(c)
84
+ c[:bbar] = [:apply.action] if c[:bbar].nil? && !c[:read_only]
85
+ end
86
+
87
+ # Extra JavaScripts and stylesheets
88
+ js_mixin :form_panel
89
+ js_include :comma_list_cbg
90
+ js_include :n_radio_group, :readonly_mode
91
+ css_include :readonly_mode
92
+
93
+ # WIP
94
+ # js_include Netzke::Core.ext_path.join("examples/ux/fileuploadfield/FileUploadField.js")
95
+ # css_include Netzke::Core.ext_path.join("examples/ux/fileuploadfield/css/fileuploadfield.css")
96
+
97
+ # WIP: Needed for FileUploadField
98
+ # js_include :misc
99
+
100
+ def js_config
101
+ super.tap do |res|
102
+ res[:pri] = data_class && data_class.primary_key.to_s
103
+ res[:record] = js_record_data if record
104
+ end
105
+ end
106
+
107
+ # A hash of record data including the meta field
108
+ def js_record_data
109
+ record.netzke_hash(fields).merge(:_meta => meta_field).literalize_keys
110
+ end
111
+
112
+ def record
113
+ @record ||= config[:record] || config[:record_id] && data_class && data_adapter.find_record(config[:record_id])
114
+ end
115
+
116
+ private
117
+
118
+ def self.server_side_config_options
119
+ super + [:record, :scope]
120
+ end
121
+
122
+ def meta_field
123
+ {}.tap do |res|
124
+ assoc_values = get_association_values
125
+ res[:association_values] = assoc_values.literalize_keys if record && !assoc_values.empty?
126
+ end
127
+ end
128
+
129
+ def get_association_values
130
+ fields_that_need_associated_values = fields.select{ |k,v| k.to_s.index("__") && !fields[k][:nested_attribute] }
131
+ # Take care of Ruby 1.8.7
132
+ if fields_that_need_associated_values.is_a?(Array)
133
+ fields_that_need_associated_values = fields_that_need_associated_values.inject({}){|r,(k,v)| r.merge(k => v)}
134
+ end
135
+
136
+ fields_that_need_associated_values.each_pair.inject({}) do |r,(k,v)|
137
+ r.merge(k => record.value_for_attribute(fields_that_need_associated_values[k], true))
138
+ end
139
+ end
140
+
141
+ # include ::Netzke::Plugins::ConfigurationTool if config_tool_available # it will load ConfigurationPanel into a modal window
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,208 @@
1
+ module Netzke
2
+ module Basepack
3
+ class FormPanel < Netzke::Base
4
+ # Because FormPanel allows for arbitrary layout of fields, we need to have all fields configured in one place (the +fields+ method), and then have references to those fields from +items+.
5
+ module Fields
6
+ extend ActiveSupport::Concern
7
+
8
+ # Items with normalized fields (i.e. containing all the necessary attributes needed by Ext.form.FormPanel to render a field)
9
+ def items
10
+ @form_panel_items ||= begin
11
+ res = normalize_fields(super || data_class && data_class.netzke_attributes || []) # netzke_attributes as default items
12
+ # if primary key isn't there, insert it as first
13
+ if data_class && !res.detect{ |f| f[:name] == data_class.primary_key.to_s}
14
+ primary_key_item = normalize_field(data_class.primary_key.to_sym)
15
+ @fields_from_config[data_class.primary_key.to_sym] = primary_key_item
16
+ res.insert(0, primary_key_item)
17
+ end
18
+ res
19
+ end
20
+ end
21
+
22
+ # Hash of fully configured fields, that are referenced in the items. E.g.:
23
+ # {
24
+ # :role__name => {:xtype => 'netzkeremotecombo', :disabled => true, :value => "admin"},
25
+ # :created_at => {:xtype => 'datetime', :disabled => true, :value => "2010-10-10 10:10"}
26
+ # }
27
+ def fields
28
+ @fields ||= begin
29
+ if static_layout?
30
+ # extract incomplete field configs from +config+
31
+ flds = fields_from_config
32
+ # and merged them with fields from the model
33
+ deep_merge_existing_fields(flds, fields_from_model) if data_class
34
+ else
35
+ # extract flds configs from the model
36
+ flds = fields_from_model
37
+ end
38
+ flds
39
+ end
40
+ end
41
+
42
+ # The array of fields as specified on the model level (using +netzke_attribute+ and alike)
43
+ def fields_array_from_model
44
+ data_class && data_class.netzke_attributes
45
+ end
46
+
47
+ # Hash of fields as specified on the model level
48
+ def fields_from_model
49
+ @fields_from_model ||= fields_array_from_model && fields_array_from_model.inject({}){ |hsh, f| hsh.merge(f[:name].to_sym => f) }
50
+ end
51
+
52
+ # Hash of normalized field configs extracted from :items, e.g.:
53
+ #
54
+ # {:role__name => {:xtype => "netzkeremotecombo"}, :password => {:xtype => "passwordfield"}}
55
+ def fields_from_config
56
+ items if @fields_from_config.nil? # by calling +items+ we initiate building of @fields_from_config
57
+ @fields_from_config ||= {}
58
+ end
59
+
60
+ module ClassMethods
61
+ # Columns to be displayed by the FieldConfigurator, "meta-columns". Each corresponds to a configuration
62
+ # option for each field in the form.
63
+ def meta_columns
64
+ [
65
+ {:name => "included", :attr_type => :boolean, :width => 40, :header => "Incl", :default_value => true},
66
+ {:name => "name", :attr_type => :string, :editor => :netzkeremotecombo, :width => 200},
67
+ {:name => "label", :attr_type => :string, :header => "Label"},
68
+ {:name => "default_value", :attr_type => :string}
69
+ ]
70
+ end
71
+ end
72
+
73
+ private
74
+ def load_persistent_fields
75
+ # NetzkeFieldList.read_list(global_id) if persistent_config_enabled?
76
+ end
77
+
78
+ def load_model_level_attrs
79
+ # NetzkeModelAttrList.read_list(data_class.name) if persistent_config_enabled? && data_class
80
+ end
81
+
82
+ # This is where we expand our basic field config with all the defaults
83
+ def normalize_field(field)
84
+ # field can only be a string, a symbol, or a hash
85
+ if field.is_a?(Hash)
86
+ field = field.dup # we don't want to modify original hash
87
+ return field if field[:no_binding] # stop here if no normalization is needed
88
+ field[:name] = field[:name].to_s if field[:name] # all names should be strings
89
+ else
90
+ field = {:name => field.to_s}
91
+ end
92
+
93
+ field_from_model = fields_from_model && fields_from_model[field[:name].to_sym]
94
+
95
+ field_from_model && field.merge!(field_from_model)
96
+
97
+ detect_association_with_method(field) # xtype for an association field
98
+ set_default_field_label(field)
99
+ set_default_field_xtype(field) if field[:xtype].nil?
100
+ set_default_read_only(field)
101
+
102
+ # temporal datetime setup, while we don't have real datetime field
103
+ if field[:attr_type] == :date
104
+ field[:format] ||= "Y-m-d"
105
+ end
106
+
107
+ # provide our special combobox with our id
108
+ field[:parent_id] = self.global_id if field[:xtype] == :netzkeremotecombo
109
+
110
+ field[:hidden] = field[:hide_label] = true if field[:hidden].nil? && primary_key_attr?(field)
111
+
112
+ # checkbox setup
113
+ field[:checked] = field[:value] if field[:attr_type] == :boolean
114
+ field[:input_value] = true if field[:attr_type] == :boolean
115
+
116
+ field
117
+ end
118
+
119
+ # Sets the proper xtype of an asociation field
120
+ def detect_association_with_method(c)
121
+ if c[:name].index('__')
122
+ assoc_name, method = c[:name].split('__').map(&:to_sym)
123
+ assoc_method_type = data_adapter.get_assoc_property_type(assoc_name, method)
124
+ if c[:nested_attribute]
125
+ c[:xtype] ||= xtype_for_attr_type(assoc_method_type)
126
+ else
127
+ c[:xtype] ||= assoc_method_type == :boolean ? xtype_for_attr_type(assoc_method_type) : xtype_for_association
128
+ end
129
+ end
130
+ end
131
+
132
+ # RECURSIVELY extracts fields configuration from :items
133
+ def normalize_fields(items)
134
+ @fields_from_config ||= {}
135
+ items.map do |item|
136
+ # at this moment, item is a hash or a symbol
137
+ if is_field_config?(item)
138
+ item = normalize_field(item)
139
+ @fields_from_config[item[:name].to_sym] = item
140
+ item #.reject{ |k,v| k == :name } # do we really need to remove the :name key?
141
+ elsif item.is_a?(Hash)
142
+ item = item.dup # we don't want to modify original hash
143
+ item[:items].is_a?(Array) ? item.merge(:items => normalize_fields(item[:items])) : item
144
+ else
145
+ item
146
+ end
147
+ end
148
+ end
149
+
150
+ def is_field_config?(item)
151
+ item.is_a?(String) || item.is_a?(Symbol) || item[:name] # && !is_component_config?(item)
152
+ end
153
+
154
+ # Deeply merges only those key/values at the top level that are already there
155
+ def deep_merge_existing_fields(dest, src)
156
+ dest.each_pair do |k,v|
157
+ v.deep_merge!(src[k] || {})
158
+ end
159
+ end
160
+
161
+ def set_default_field_label(c)
162
+ # multiple spaces (in case of association attrs) get replaced with one
163
+ c[:field_label] ||= data_class ? data_class.human_attribute_name(c[:name]) : c[:name].humanize
164
+ c[:field_label].gsub!(/\s+/, " ")
165
+ end
166
+
167
+ def set_default_field_xtype(field)
168
+ field[:xtype] = xtype_for_attr_type(field[:attr_type]) unless xtype_for_attr_type(field[:attr_type]).nil?
169
+ end
170
+
171
+ def set_default_read_only(field)
172
+ enabled_if = !data_class || data_class.column_names.include?(field[:name])
173
+ enabled_if ||= data_class.instance_methods.map(&:to_s).include?("#{field[:name]}=")
174
+ enabled_if ||= record && record.respond_to?("#{field[:name]}=")
175
+ enabled_if ||= association_attr?(field[:name])
176
+
177
+ field[:read_only] = !enabled_if if field[:read_only].nil?
178
+ end
179
+
180
+ def attr_type_to_xtype_map
181
+ {
182
+ :integer => :numberfield,
183
+ :boolean => config[:multi_edit] ? :tricheckbox : :checkboxfield,
184
+ :date => :datefield,
185
+ :datetime => :xdatetime,
186
+ :text => :textarea,
187
+ :json => :jsonfield,
188
+ :string => :textfield
189
+ }
190
+ end
191
+
192
+ def xtype_for_attr_type(type)
193
+ attr_type_to_xtype_map[type] || :textfield
194
+ end
195
+
196
+ def xtype_for_association
197
+ :netzkeremotecombo
198
+ end
199
+
200
+ # Are we provided with a static field layout?
201
+ def static_layout?
202
+ !!config[:items]
203
+ end
204
+
205
+ end
206
+ end
207
+ end
208
+ end