para 0.6.9 → 0.7.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 (180) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/images/para/admin/tangram-mosaic.png +0 -0
  3. data/app/assets/images/para/admin/template-section.svg +23 -0
  4. data/app/assets/javascripts/para/admin.coffee +20 -0
  5. data/app/assets/javascripts/para/admin/tabs.coffee +22 -5
  6. data/app/assets/javascripts/para/admin/theme_actions.coffee +3 -3
  7. data/app/assets/javascripts/para/inputs/material-input.coffee +4 -0
  8. data/app/assets/javascripts/para/inputs/multi-select-input.coffee +173 -0
  9. data/app/assets/javascripts/para/inputs/nested_many.coffee +9 -9
  10. data/app/assets/stylesheets/para/admin.sass +5 -5
  11. data/app/assets/stylesheets/para/admin/main.sass +32 -0
  12. data/app/assets/stylesheets/para/admin/src/_alert.sass +17 -0
  13. data/app/assets/stylesheets/para/admin/src/_base.sass +51 -0
  14. data/app/assets/stylesheets/para/admin/src/_bootstrap-variables.scss +874 -0
  15. data/app/assets/stylesheets/para/admin/src/_breadcrumb.sass +22 -0
  16. data/app/assets/stylesheets/para/admin/src/_buttons.sass +107 -0
  17. data/app/assets/stylesheets/para/admin/src/_checkable.sass +94 -0
  18. data/app/assets/stylesheets/para/admin/src/_common.sass +203 -0
  19. data/app/assets/stylesheets/para/admin/src/_dropdown.sass +40 -0
  20. data/app/assets/stylesheets/para/admin/src/_form.sass +271 -0
  21. data/app/assets/stylesheets/para/admin/src/_list.sass +61 -0
  22. data/app/assets/stylesheets/para/admin/src/_mixins.sass +113 -0
  23. data/app/assets/stylesheets/para/admin/src/_multi-select.sass +91 -0
  24. data/app/assets/stylesheets/para/admin/src/_navigation.sass +150 -0
  25. data/app/assets/stylesheets/para/admin/src/_navtabs.sass +41 -0
  26. data/app/assets/stylesheets/para/admin/src/_nested-many.sass +69 -0
  27. data/app/assets/stylesheets/para/admin/src/_nested_one.sass +13 -0
  28. data/app/assets/stylesheets/para/admin/src/_orderable.sass +44 -0
  29. data/app/assets/stylesheets/para/admin/src/_page-loading.sass +39 -0
  30. data/app/assets/stylesheets/para/admin/src/_pagination.sass +34 -0
  31. data/app/assets/stylesheets/para/admin/src/_panel.sass +55 -0
  32. data/app/assets/stylesheets/para/admin/src/_responsive.sass +100 -0
  33. data/app/assets/stylesheets/para/admin/{theme → src}/_sorting.sass +11 -3
  34. data/app/assets/stylesheets/para/admin/src/_statcard.sass +41 -0
  35. data/app/assets/stylesheets/para/admin/src/_table.sass +33 -0
  36. data/app/assets/stylesheets/para/admin/src/_tree.sass +52 -0
  37. data/app/assets/stylesheets/para/admin/src/_variables.sass +37 -0
  38. data/app/assets/stylesheets/para/admin/src/_well.sass +12 -0
  39. data/app/assets/stylesheets/para/{admin/src → lib}/datetimepicker.sass +1 -3
  40. data/app/assets/stylesheets/para/{admin/src → lib}/fuelux.sass +3 -5
  41. data/app/assets/stylesheets/para/lib/jasny-bootstrap.sass +97 -0
  42. data/app/assets/stylesheets/para/{admin/src → lib}/redactor.sass +2 -5
  43. data/app/assets/stylesheets/para/lib/selectize.sass +112 -0
  44. data/app/assets/stylesheets/para/lib/slider.sass +12 -0
  45. data/app/controllers/concerns/para/admin/resource_controller_concerns.rb +14 -2
  46. data/app/controllers/para/admin/base_controller.rb +7 -0
  47. data/app/controllers/para/admin/crud_resources_controller.rb +13 -6
  48. data/app/controllers/para/admin/form_resources_controller.rb +5 -3
  49. data/app/controllers/para/admin/resources_controller.rb +9 -3
  50. data/app/controllers/para/admin/search_controller.rb +15 -0
  51. data/app/controllers/para/application_controller.rb +10 -2
  52. data/app/helpers/para/admin/page_helper.rb +9 -9
  53. data/app/helpers/para/application_helper.rb +1 -10
  54. data/app/helpers/para/form_helper.rb +5 -6
  55. data/app/helpers/para/model_helper.rb +13 -3
  56. data/app/helpers/para/ordering_helper.rb +1 -1
  57. data/app/helpers/para/search_results_helper.rb +16 -0
  58. data/app/helpers/para/tree_helper.rb +10 -6
  59. data/app/models/para/page.rb +5 -0
  60. data/app/models/para/page/section.rb +42 -0
  61. data/app/models/para/page/section_resource.rb +12 -0
  62. data/app/views/layouts/para/admin.html.haml +5 -8
  63. data/app/views/para/admin/dashboard.html.haml +21 -8
  64. data/app/views/para/admin/form_resources/show.html.haml +1 -1
  65. data/app/views/para/admin/imports/new.html.haml +2 -2
  66. data/app/views/para/admin/resources/_actions.html.haml +3 -3
  67. data/app/views/para/admin/resources/_add_button.html.haml +1 -1
  68. data/app/views/para/admin/resources/_exports_menu.html.haml +6 -6
  69. data/app/views/para/admin/resources/_filters.html.haml +7 -8
  70. data/app/views/para/admin/resources/_imports_menu.html.haml +6 -6
  71. data/app/views/para/admin/resources/_list.html.haml +14 -15
  72. data/app/views/para/admin/resources/_per_page_select.html.haml +4 -3
  73. data/app/views/para/admin/resources/_subclassable_add_button.html.haml +3 -3
  74. data/app/views/para/admin/resources/_tree.html.haml +7 -9
  75. data/app/views/para/admin/resources/_tree_item.html.haml +5 -6
  76. data/app/views/para/admin/resources/edit.html.haml +1 -1
  77. data/app/views/para/admin/resources/new.html.haml +1 -1
  78. data/app/views/para/admin/search/_result.html.haml +7 -0
  79. data/app/views/para/admin/search/index.html.haml +1 -0
  80. data/app/views/para/admin/settings_component/show.html.haml +1 -1
  81. data/app/views/para/admin/shared/_breadcrumbs.html.haml +2 -3
  82. data/app/views/para/admin/shared/_header.html.haml +16 -27
  83. data/app/views/para/admin/shared/_navigation.html.haml +20 -19
  84. data/app/views/para/form/_tabs.html.haml +6 -7
  85. data/app/views/para/inputs/_multi_select.html.haml +40 -0
  86. data/app/views/para/inputs/_nested_many.html.haml +4 -4
  87. data/app/views/para/inputs/multi_select/_no_items.html.haml +6 -0
  88. data/app/views/para/inputs/nested_many/_add.html.haml +1 -2
  89. data/app/views/para/inputs/nested_many/_add_with_subclasses.html.haml +4 -5
  90. data/app/views/para/inputs/nested_many/_container.html.haml +7 -8
  91. data/app/views/shared/_breadcrumbs.html.haml +4 -0
  92. data/config/locales/fr.yml +29 -4
  93. data/db/migrate/20170324125547_create_para_page_section_resources.rb +15 -0
  94. data/lib/generators/para/filters/templates/_filters.html.haml +3 -5
  95. data/lib/generators/para/install/templates/initializer.rb +12 -0
  96. data/lib/generators/para/page/section/section_generator.rb +13 -2
  97. data/lib/generators/para/page/section/templates/section.html.haml.erb +3 -0
  98. data/lib/generators/para/resource/resource_generator.rb +1 -1
  99. data/lib/para.rb +25 -21
  100. data/lib/para/attribute_field.rb +2 -0
  101. data/lib/para/attribute_field/base.rb +1 -1
  102. data/lib/para/attribute_field/belongs_to.rb +1 -1
  103. data/lib/para/attribute_field/boolean.rb +1 -1
  104. data/lib/para/attribute_field/friendly_id.rb +19 -0
  105. data/lib/para/attribute_field/has_many.rb +43 -4
  106. data/lib/para/attribute_field/nested_field.rb +46 -0
  107. data/lib/para/attribute_field/nested_many.rb +30 -9
  108. data/lib/para/attribute_field/nested_one.rb +28 -9
  109. data/lib/para/attribute_field/password.rb +1 -1
  110. data/lib/para/attribute_field/relation.rb +19 -1
  111. data/lib/para/attribute_field_mappings.rb +9 -3
  112. data/lib/para/breadcrumbs/breadcrumb.rb +57 -10
  113. data/lib/para/breadcrumbs/controller.rb +19 -6
  114. data/lib/para/breadcrumbs/manager.rb +9 -1
  115. data/lib/para/config.rb +3 -0
  116. data/lib/para/controller_resource.rb +34 -0
  117. data/lib/para/engine.rb +33 -14
  118. data/lib/para/exporter/base.rb +1 -1
  119. data/lib/para/ext.rb +1 -1
  120. data/lib/para/ext/active_record_nested_attributes.rb +51 -0
  121. data/lib/para/ext/paperclip.rb +2 -6
  122. data/lib/para/form_builder.rb +3 -0
  123. data/lib/para/form_builder/attributes_mappings_tracker.rb +45 -0
  124. data/lib/para/form_builder/containers.rb +13 -17
  125. data/lib/para/form_builder/nested_form.rb +2 -3
  126. data/lib/para/helpers.rb +8 -0
  127. data/lib/para/helpers/attributes_mappings.rb +13 -0
  128. data/lib/para/helpers/resource_name.rb +14 -0
  129. data/lib/para/inputs.rb +1 -0
  130. data/lib/para/inputs/multi_select_input.rb +90 -0
  131. data/lib/para/inputs/nested_many_input.rb +6 -1
  132. data/lib/para/markup/resources_table.rb +15 -11
  133. data/lib/para/model_field_parsers.rb +3 -2
  134. data/lib/para/model_field_parsers/base.rb +7 -2
  135. data/lib/para/model_field_parsers/friendly_id.rb +19 -0
  136. data/lib/para/model_field_parsers/relations.rb +20 -1
  137. data/lib/para/page/model.rb +3 -1
  138. data/lib/para/routes.rb +1 -0
  139. data/lib/para/simple_form_config.rb +85 -0
  140. data/lib/para/version.rb +1 -1
  141. data/lib/rails/routing_mapper.rb +5 -8
  142. data/vendor/assets/javascripts/jasny-bootstrap.js +577 -0
  143. data/vendor/assets/stylesheets/animate.css +3340 -0
  144. data/vendor/assets/stylesheets/hint.css +649 -0
  145. data/vendor/assets/stylesheets/jasny-bootstrap.css +287 -0
  146. metadata +114 -67
  147. data/app/assets/images/para/admin/bg.png +0 -0
  148. data/app/assets/javascripts/para/admin.js +0 -20
  149. data/app/assets/stylesheets/admin/theme.sass +0 -0
  150. data/app/assets/stylesheets/para/admin/src/jasny.bootstrap.sass +0 -40
  151. data/app/assets/stylesheets/para/admin/src/nested_many.sass +0 -5
  152. data/app/assets/stylesheets/para/admin/src/page-loading.sass +0 -24
  153. data/app/assets/stylesheets/para/admin/src/page-top-bar.sass +0 -9
  154. data/app/assets/stylesheets/para/admin/src/selectize.sass +0 -46
  155. data/app/assets/stylesheets/para/admin/src/slider.sass +0 -12
  156. data/app/assets/stylesheets/para/admin/theme.sass +0 -22
  157. data/app/assets/stylesheets/para/admin/theme/_base.sass +0 -176
  158. data/app/assets/stylesheets/para/admin/theme/_breadcrumb.sass +0 -75
  159. data/app/assets/stylesheets/para/admin/theme/_buttons.sass +0 -27
  160. data/app/assets/stylesheets/para/admin/theme/_checkable.sass +0 -108
  161. data/app/assets/stylesheets/para/admin/theme/_commonds.sass +0 -210
  162. data/app/assets/stylesheets/para/admin/theme/_dropdown.sass +0 -52
  163. data/app/assets/stylesheets/para/admin/theme/_form.sass +0 -150
  164. data/app/assets/stylesheets/para/admin/theme/_list.sass +0 -139
  165. data/app/assets/stylesheets/para/admin/theme/_navigation.sass +0 -229
  166. data/app/assets/stylesheets/para/admin/theme/_navtabs.sass +0 -151
  167. data/app/assets/stylesheets/para/admin/theme/_orderable.sass +0 -45
  168. data/app/assets/stylesheets/para/admin/theme/_panel.sass +0 -264
  169. data/app/assets/stylesheets/para/admin/theme/_responsive.sass +0 -123
  170. data/app/assets/stylesheets/para/admin/theme/_table.sass +0 -28
  171. data/app/assets/stylesheets/para/admin/theme/_tree.sass +0 -74
  172. data/app/assets/stylesheets/para/admin/theme/_variables.sass +0 -31
  173. data/app/assets/stylesheets/para/application.css +0 -15
  174. data/app/assets/stylesheets/para/lib/_variables.scss +0 -646
  175. data/app/assets/stylesheets/para/overrides/responsive.sass +0 -62
  176. data/app/assets/stylesheets/para/overrides/theme.sass +0 -172
  177. data/lib/para/ext/cancan.rb +0 -26
  178. data/vendor/assets/javascripts/jasny.bootstrap.min.js +0 -7
  179. data/vendor/assets/stylesheets/animate.min.css +0 -6
  180. data/vendor/assets/stylesheets/jasny.bootstrap.min.css +0 -7
@@ -19,7 +19,7 @@ module Para
19
19
  reflection.klass.all
20
20
  end
21
21
 
22
- def parse_input(params)
22
+ def parse_input(params, resource)
23
23
  if (id = params[reflection.foreign_key].presence) && !reflection.klass.exists?(id: id)
24
24
  on_the_fly_creation(id) do |resource|
25
25
  params[reflection.foreign_key] = resource.id
@@ -10,7 +10,7 @@ module Para
10
10
  end
11
11
 
12
12
  def wrapper_name
13
- :horizontal_radio_and_checkboxes
13
+ :vertical_radio_and_checkboxes
14
14
  end
15
15
  end
16
16
  end
@@ -0,0 +1,19 @@
1
+ module Para
2
+ module AttributeField
3
+ class FriendlyId < Base
4
+ register :friendly_id, self
5
+
6
+ # Set empty string as nil to allow default friendly id methods to
7
+ # generate the slug when the field is empty
8
+ def parse_input(params, resource)
9
+ params[slug_column] = nil if slug_column && params[slug_column] == ''
10
+ end
11
+
12
+ private
13
+
14
+ def slug_column
15
+ model.try(:friendly_id_config).try(:slug_column)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -13,21 +13,60 @@ module Para
13
13
  end.join(', ')
14
14
  end
15
15
 
16
- def parse_input(params)
16
+ def parse_input(params, resource)
17
17
  if (ids = params[plural_foreign_key].presence) && String === ids
18
18
  # Format selectize value for Rails
19
- ids = params[plural_foreign_key] = ids.split(',')
19
+ ids = params[plural_foreign_key] = ids.split(',').map(&:to_i)
20
20
 
21
- on_the_fly_creation(ids) do |resource, value|
21
+ on_the_fly_creation(ids) do |res, value|
22
22
  params[plural_foreign_key].delete(value)
23
- params[plural_foreign_key] << resource.id
23
+ params[plural_foreign_key] << res.id
24
24
  end
25
25
  end
26
+
27
+ assign_ids(params, resource)
26
28
  end
27
29
 
28
30
  def plural_foreign_key
29
31
  foreign_key.to_s.pluralize
30
32
  end
33
+
34
+ def assign_ids(params, resource)
35
+ return unless Array === params[plural_foreign_key]
36
+
37
+ ids = params.delete(plural_foreign_key)
38
+
39
+ if through_reflection && through_reflection.klass.orderable?
40
+ assign_ordered_through_reflection_ids(through_reflection, resource, ids)
41
+ else
42
+ resource.assign_attributes(plural_foreign_key => ids)
43
+ end
44
+ end
45
+
46
+ def assign_ordered_through_reflection_ids(through_reflection, resource, ids)
47
+ association = resource.association(through_reflection.name)
48
+ join_resources = association.load_target
49
+
50
+ return association.replace([]) if ids.empty?
51
+
52
+ new_resources = ids.each_with_index.map do |id, position|
53
+ join_resource = join_resources.find do |res|
54
+ res.send(through_relation_source_foreign_key) == id &&
55
+ (!polymorphic_through_reflection? || res.send(reflection.foreign_type) == reflection.klass.name)
56
+ end
57
+
58
+ unless join_resource
59
+ attributes = { through_relation_source_foreign_key => id }
60
+ attributes[reflection.foreign_type] = reflection.klass.name if polymorphic_through_reflection?
61
+ join_resource = association.build(attributes)
62
+ end
63
+
64
+ join_resource.position = position
65
+ join_resource
66
+ end
67
+
68
+ association.replace(new_resources)
69
+ end
31
70
  end
32
71
  end
33
72
  end
@@ -0,0 +1,46 @@
1
+ module Para
2
+ module AttributeField
3
+ module NestedField
4
+ def nested_model_mappings(nested_attributes)
5
+ model = if (type = nested_attributes[:type]).present?
6
+ nested_attributes[:type].try(:constantize)
7
+ else
8
+ reflection.klass
9
+ end
10
+
11
+ mappings = attributes_mappings_for(nested_attributes)
12
+
13
+ AttributeFieldMappings.new(model, mappings: mappings)
14
+ end
15
+
16
+ def nested_attributes_key
17
+ @nested_attributes_key ||= :"#{ name }_attributes"
18
+ end
19
+
20
+ # This method extends the current resource so its #id method returns a
21
+ # fake id based on the given attributes, but immediately returns to its
22
+ # standard behavior of returning nil as soon as the `#assign_attributes`
23
+ # method is called.
24
+ #
25
+ # During nested attributes assignation, the id of the resource is used
26
+ # to assign it its nested params. When it is nil, a new resource is
27
+ # created, but since we already need our resource to be created at the
28
+ # time we parse the input params, we fake the presence of an id while the
29
+ # nested attributes assignation is running, and remove that behavior as
30
+ # soon as we don't need it anymore.
31
+ #
32
+ def temporarily_extend_new_resource(resource, attributes)
33
+ resource.instance_variable_set(:@_waiting_for_attributes_assignation, true)
34
+
35
+ resource.define_singleton_method(:assign_attributes) do |*args|
36
+ @_waiting_for_attributes_assignation = false
37
+ super(*args)
38
+ end
39
+
40
+ resource.define_singleton_method(:id) do
41
+ @_waiting_for_attributes_assignation ? attributes['id'] : read_attribute(:id)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -1,28 +1,49 @@
1
1
  module Para
2
2
  module AttributeField
3
3
  class NestedManyField < AttributeField::HasManyField
4
+ include Para::Helpers::AttributesMappings
5
+ include Para::AttributeField::NestedField
6
+
4
7
  register :nested_many, self
5
8
 
6
- def parse_input(params)
9
+ def parse_input(params, resource)
7
10
  if (nested_attributes = params[nested_attributes_key])
8
11
  nested_attributes.each do |index, attributes|
9
- nested_model_mappings.fields.each do |field|
10
- field.parse_input(attributes)
12
+ mappings = nested_model_mappings(attributes)
13
+ nested_resource = fetch_or_build_nested_resource_for(resource, index, attributes)
14
+
15
+ mappings.fields.each do |field|
16
+ field.parse_input(attributes, nested_resource)
11
17
  end
12
18
 
13
19
  params[nested_attributes_key][index] = attributes
14
20
  end
15
21
  else
16
- super(params)
22
+ super
17
23
  end
18
24
  end
19
25
 
20
- def nested_model_mappings
21
- @nested_model_mappings ||= AttributeFieldMappings.new(reflection.klass)
22
- end
26
+ private
27
+
28
+ # Force loading association and look for a resource matching the provided
29
+ # attributes. If no resource is found, one is created and a fake `id` is
30
+ # assigned to it to hack Rails' nested resources lookup from new
31
+ # attributes params.
32
+ #
33
+ # This is necessary to be able to provide a resource to the #parse_input
34
+ # method when called on the nested resources hash
35
+ #
36
+ def fetch_or_build_nested_resource_for(parent, index, attributes)
37
+ nested_resources = parent.association(name).load_target
23
38
 
24
- def nested_attributes_key
25
- @nested_attributes_key ||= :"#{ name }_attributes"
39
+ if (id = attributes['id'].presence)
40
+ nested_resources.find { |res| res.id == id.to_i }
41
+ else
42
+ parent.association(name).build(attributes.slice('type')).tap do |resource|
43
+ attributes['id'] = "__#{ index }"
44
+ temporarily_extend_new_resource(resource, attributes)
45
+ end
46
+ end
26
47
  end
27
48
  end
28
49
  end
@@ -1,26 +1,45 @@
1
1
  module Para
2
2
  module AttributeField
3
3
  class NestedOneField < AttributeField::BelongsToField
4
+ include Para::Helpers::AttributesMappings
5
+ include Para::AttributeField::NestedField
6
+
4
7
  register :nested_one, self
5
8
 
6
- def parse_input(params)
9
+ def parse_input(params, resource)
7
10
  if (nested_attributes = params[nested_attributes_key])
8
- nested_model_mappings.fields.each do |field|
9
- field.parse_input(nested_attributes)
11
+ mappings = nested_model_mappings(nested_attributes)
12
+ nested_resource = fetch_or_build_nested_resource_for(resource, nested_attributes)
13
+
14
+ mappings.fields.each do |field|
15
+ field.parse_input(nested_attributes, nested_resource)
10
16
  end
11
17
 
12
18
  params[nested_attributes_key] = nested_attributes
13
19
  else
14
- super(params)
20
+ super
15
21
  end
16
22
  end
17
23
 
18
- def nested_model_mappings
19
- @nested_model_mappings ||= AttributeFieldMappings.new(reflection.klass)
20
- end
24
+ private
21
25
 
22
- def nested_attributes_key
23
- @nested_attributes_key ||= :"#{ name }_attributes"
26
+ # Force loading association and look for a resource matching the provided
27
+ # attributes. If no resource is found, one is created and a fake `id` is
28
+ # assigned to it to hack Rails' nested resources lookup from new
29
+ # attributes params.
30
+ #
31
+ # This is necessary to be able to provide a resource to the #parse_input
32
+ # method when called on the nested resources hash
33
+ #
34
+ def fetch_or_build_nested_resource_for(parent, attributes)
35
+ if (nested_resource = parent.association(name).load_target)
36
+ return nested_resource
37
+ end
38
+
39
+ parent.association(name).build(attributes.slice('type')).tap do |resource|
40
+ attributes['id'] = "__#{ object_id }"
41
+ temporarily_extend_new_resource(resource, attributes)
42
+ end
24
43
  end
25
44
  end
26
45
  end
@@ -12,7 +12,7 @@ module Para
12
12
  nil
13
13
  end
14
14
 
15
- def parse_input(params)
15
+ def parse_input(params, resource)
16
16
  params[name] = params[name].presence
17
17
  end
18
18
  end
@@ -1,7 +1,7 @@
1
1
  module Para
2
2
  module AttributeField
3
3
  class RelationField < AttributeField::Base
4
- private
4
+ include Para::Helpers::ResourceName
5
5
 
6
6
  def reflection
7
7
  @reflection ||= model.reflect_on_association(name)
@@ -15,6 +15,24 @@ module Para
15
15
  end
16
16
  end
17
17
 
18
+ def through_relation
19
+ @through_relation ||= reflection.options[:through]
20
+ end
21
+
22
+ def through_reflection
23
+ @through_reflection ||= through_relation && model.reflect_on_association(through_relation)
24
+ end
25
+
26
+ def through_relation_source_foreign_key
27
+ @through_relation_source_foreign_key ||= reflection.source_reflection.foreign_key
28
+ end
29
+
30
+ def polymorphic_through_reflection?
31
+ !!(through_relation && reflection.source_reflection.options[:polymorphic])
32
+ end
33
+
34
+ private
35
+
18
36
  def resource_name(resource)
19
37
  Para.config.resource_name_methods.each do |method|
20
38
  return resource.send(method) if resource.respond_to?(method)
@@ -2,11 +2,17 @@ module Para
2
2
  class AttributeFieldMappings
3
3
  UNEDITABLE_ATTRIBUTES = %w(id component_id created_at updated_at type)
4
4
 
5
- attr_reader :model, :fields_hash, :whitelist_attributes
5
+ attr_reader :model, :fields_hash, :whitelist_attributes, :mappings
6
6
 
7
- def initialize(model, whitelist_attributes: nil)
7
+ # The mappings hash is a form provided hash of attributes and input types
8
+ # mapping that are automatically generated in the form builder and that
9
+ # allows our model field parsers to spot unregistered fields and process
10
+ # them when needed.
11
+ #
12
+ def initialize(model, whitelist_attributes: nil, mappings: nil)
8
13
  @model = model
9
14
  @whitelist_attributes = whitelist_attributes
15
+ @mappings = mappings || {}
10
16
 
11
17
  process_fields!
12
18
  end
@@ -52,7 +58,7 @@ module Para
52
58
  end
53
59
  end.with_indifferent_access
54
60
 
55
- Para::ModelFieldParsers.parse!(model, fields_hash)
61
+ Para::ModelFieldParsers.parse!(model, fields_hash, mappings)
56
62
  end
57
63
 
58
64
  def build_field_for(attribute_name, type)
@@ -1,42 +1,89 @@
1
1
  module Para
2
2
  module Breadcrumbs
3
3
  class Breadcrumb
4
- attr_reader :identifier, :_path, :options
4
+ include ActionDispatch::Routing::PolymorphicRoutes
5
+ include Para::Helpers::ResourceName
5
6
 
6
- def initialize(identifier, path, *options)
7
- @identifier = identifier
7
+ attr_reader :resource_or_identifier, :_path, :controller, :options
8
+
9
+ def initialize(resource_or_identifier, path, controller, *options)
10
+ @resource_or_identifier = resource_or_identifier
8
11
  @_path = path
12
+ @controller = controller
9
13
  @options = options
10
14
  end
11
15
 
12
16
  def title
13
- @title ||= if Symbol === identifier
14
- ::I18n.t("admin.breadcrumbs.#{ identifier }")
17
+ @title ||= if resource_or_identifier.is_a?(Symbol)
18
+ translate(resource_or_identifier)
19
+ elsif resource_or_identifier.is_a?(ActiveRecord::Base)
20
+ resource_name(resource_or_identifier)
15
21
  else
16
- identifier
22
+ resource_or_identifier
17
23
  end
18
24
  end
19
25
 
20
26
  # Allow lazy evaluation of routes to define breadcrumbs before being
21
27
  # able to access request or routes
28
+ #
22
29
  def path
23
- @path ||= if Symbol === _path
30
+ @path ||= if _path.is_a?(Symbol)
24
31
  find_route_for(_path, *options)
25
32
  elsif _path
26
33
  _path
27
34
  else
28
- '#'
35
+ begin
36
+ polymorphic_path(resource_or_identifier)
37
+ rescue
38
+ '#'
39
+ end
29
40
  end
30
41
  end
31
42
 
32
43
  def active?(request)
33
- path == request.path || !_path
44
+ path == request.path || path == '#'
34
45
  end
35
46
 
36
47
  private
37
48
 
49
+ def translate(key)
50
+ if controller.admin?
51
+ ::I18n.t("admin.breadcrumbs.#{ resource_or_identifier }")
52
+ else
53
+ # Check if a specific translation has been defined
54
+ if (translation = ::I18n.t("breadcrumbs.#{ resource_or_identifier }", default: '')).present?
55
+ translation
56
+ else
57
+ # If no translation is defined, we check if there's a plural model
58
+ # translation associated to the given key
59
+ begin
60
+ klass = key.to_s.singularize.camelize.constantize
61
+ klass.try(:model_name).try(:human, count: 2) || key
62
+ rescue NameError
63
+ key
64
+ end
65
+ end
66
+ end
67
+ end
68
+
38
69
  def find_route_for(path, *options)
39
- Rails.application.routes.url_helpers.send(:"#{ path }_path", *options)
70
+ path = path_method?(path) ? path.to_sym : :"#{ path }_path"
71
+ Rails.application.routes.url_helpers.send(path, *options)
72
+ end
73
+
74
+ # Allow #polymorphic_path to work by delegating missing methods ending
75
+ # with _path or _url to be tried on url_helpers
76
+ #
77
+ def method_missing(method_name, *args, &block)
78
+ if path_method?(method_name)
79
+ Rails.application.routes.url_helpers.try(method_name, *args) || super
80
+ else
81
+ super
82
+ end
83
+ end
84
+
85
+ def path_method?(path)
86
+ path.to_s.match(/_(path|url)\z/)
40
87
  end
41
88
  end
42
89
  end
@@ -6,6 +6,8 @@ module Para
6
6
  included do
7
7
  class_attribute :_class_level_breadcrumbs
8
8
 
9
+ before_action :build_breadcrumbs_manager
10
+
9
11
  helper_method :add_breadcrumb, :breadcrumbs
10
12
  helper ViewHelper
11
13
  end
@@ -15,11 +17,18 @@ module Para
15
17
  end
16
18
 
17
19
  def breadcrumbs
18
- Para.store[:breadcrumbs] ||= begin
19
- manager = Breadcrumbs::Manager.new
20
- _class_level_breadcrumbs.each { |args| manager.add(*args) }
21
- manager
22
- end
20
+ build_breadcrumbs_manager
21
+ end
22
+
23
+ private
24
+
25
+ def build_breadcrumbs_manager
26
+ Para.store['para.breadcrumbs'] ||=
27
+ Breadcrumbs::Manager.new(self).tap do |manager|
28
+ if _class_level_breadcrumbs
29
+ _class_level_breadcrumbs.each { |args| manager.add(*args) }
30
+ end
31
+ end
23
32
  end
24
33
 
25
34
  module ClassMethods
@@ -30,8 +39,12 @@ module Para
30
39
  end
31
40
 
32
41
  module ViewHelper
42
+ # Render the breadcrumbs view depending wether it is in the admin or
43
+ # in front, allowing the front view to be overriden in app
44
+ #
33
45
  def render_breadcrumbs
34
- render partial: 'para/admin/shared/breadcrumbs'
46
+ prefix = controller.admin? ? 'para/admin/' : ''
47
+ render partial: "#{ prefix }shared/breadcrumbs"
35
48
  end
36
49
  end
37
50
  end