para 0.4.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 (203) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +65 -0
  4. data/Rakefile +21 -0
  5. data/app/assets/images/para/admin/bg.png +0 -0
  6. data/app/assets/javascripts/admin/app.coffee +1 -0
  7. data/app/assets/javascripts/para/admin/table.coffee +46 -0
  8. data/app/assets/javascripts/para/admin/theme_actions.coffee +26 -0
  9. data/app/assets/javascripts/para/admin/tree.coffee +53 -0
  10. data/app/assets/javascripts/para/admin.coffee +18 -0
  11. data/app/assets/javascripts/para/application.js +13 -0
  12. data/app/assets/javascripts/para/inputs/nested_many.coffee +57 -0
  13. data/app/assets/javascripts/para/lib/ajax.coffee +11 -0
  14. data/app/assets/stylesheets/admin/app.sass +0 -0
  15. data/app/assets/stylesheets/admin/theme.sass +0 -0
  16. data/app/assets/stylesheets/para/admin/theme/_base.sass +170 -0
  17. data/app/assets/stylesheets/para/admin/theme/_breadcrumb.sass +29 -0
  18. data/app/assets/stylesheets/para/admin/theme/_buttons.sass +24 -0
  19. data/app/assets/stylesheets/para/admin/theme/_checkable.sass +107 -0
  20. data/app/assets/stylesheets/para/admin/theme/_commonds.sass +213 -0
  21. data/app/assets/stylesheets/para/admin/theme/_dropdown.sass +53 -0
  22. data/app/assets/stylesheets/para/admin/theme/_form.sass +141 -0
  23. data/app/assets/stylesheets/para/admin/theme/_list.sass +138 -0
  24. data/app/assets/stylesheets/para/admin/theme/_navigation.sass +287 -0
  25. data/app/assets/stylesheets/para/admin/theme/_navtabs.sass +127 -0
  26. data/app/assets/stylesheets/para/admin/theme/_orderable.sass +44 -0
  27. data/app/assets/stylesheets/para/admin/theme/_panel.sass +288 -0
  28. data/app/assets/stylesheets/para/admin/theme/_responsive.sass +123 -0
  29. data/app/assets/stylesheets/para/admin/theme/_sorting.sass +30 -0
  30. data/app/assets/stylesheets/para/admin/theme/_table.sass +24 -0
  31. data/app/assets/stylesheets/para/admin/theme/_tree.sass +74 -0
  32. data/app/assets/stylesheets/para/admin/theme/_variables.sass +31 -0
  33. data/app/assets/stylesheets/para/admin/theme.sass +22 -0
  34. data/app/assets/stylesheets/para/admin.sass +10 -0
  35. data/app/assets/stylesheets/para/application.css +15 -0
  36. data/app/assets/stylesheets/para/lib/_variables.scss +646 -0
  37. data/app/assets/stylesheets/para/overrides/datetimepicker.sass +17 -0
  38. data/app/assets/stylesheets/para/overrides/fuelux.sass +43 -0
  39. data/app/assets/stylesheets/para/overrides/jasny.bootstrap.sass +41 -0
  40. data/app/assets/stylesheets/para/overrides/redactor.sass +88 -0
  41. data/app/assets/stylesheets/para/overrides/responsive.sass +63 -0
  42. data/app/assets/stylesheets/para/overrides/selectize.sass +45 -0
  43. data/app/assets/stylesheets/para/overrides/slider.sass +13 -0
  44. data/app/assets/stylesheets/para/overrides/theme.sass +172 -0
  45. data/app/controllers/concerns/para/admin/resource_controller_concerns.rb +17 -0
  46. data/app/controllers/para/admin/base_controller.rb +46 -0
  47. data/app/controllers/para/admin/component_controller.rb +9 -0
  48. data/app/controllers/para/admin/crud_component_controller.rb +17 -0
  49. data/app/controllers/para/admin/crud_resources_controller.rb +71 -0
  50. data/app/controllers/para/admin/main_controller.rb +8 -0
  51. data/app/controllers/para/admin/resources_controller.rb +163 -0
  52. data/app/controllers/para/admin/settings_component_controller.rb +11 -0
  53. data/app/controllers/para/admin/settings_form_controller.rb +21 -0
  54. data/app/controllers/para/admin/singleton_resource_component_controller.rb +14 -0
  55. data/app/controllers/para/admin/singleton_resources_controller.rb +30 -0
  56. data/app/controllers/para/application_controller.rb +7 -0
  57. data/app/decorators/para/component/base_decorator.rb +48 -0
  58. data/app/decorators/para/component/crud_decorator.rb +32 -0
  59. data/app/decorators/para/component/settings_decorator.rb +11 -0
  60. data/app/decorators/para/component/singleton_resource_decorator.rb +11 -0
  61. data/app/decorators/para/component_section_decorator.rb +4 -0
  62. data/app/helpers/para/admin/base_helper.rb +57 -0
  63. data/app/helpers/para/admin/component_groups_helper.rb +4 -0
  64. data/app/helpers/para/admin/components_helper.rb +4 -0
  65. data/app/helpers/para/admin/resources_helper.rb +27 -0
  66. data/app/helpers/para/application_helper.rb +14 -0
  67. data/app/helpers/para/exports_helper.rb +11 -0
  68. data/app/helpers/para/flash_helper.rb +48 -0
  69. data/app/helpers/para/form_helper.rb +28 -0
  70. data/app/helpers/para/markup_helper.rb +15 -0
  71. data/app/helpers/para/model_helper.rb +38 -0
  72. data/app/helpers/para/navigation_helper.rb +8 -0
  73. data/app/helpers/para/ordering_helper.rb +20 -0
  74. data/app/helpers/para/search_helper.rb +22 -0
  75. data/app/helpers/para/tag_helper.rb +50 -0
  76. data/app/helpers/para/tree_helper.rb +41 -0
  77. data/app/models/para/component/base.rb +70 -0
  78. data/app/models/para/component/crud.rb +45 -0
  79. data/app/models/para/component/resource.rb +33 -0
  80. data/app/models/para/component/settings.rb +17 -0
  81. data/app/models/para/component/singleton_resource.rb +25 -0
  82. data/app/models/para/component_resource.rb +6 -0
  83. data/app/models/para/component_section.rb +18 -0
  84. data/app/views/layouts/para/admin.html.haml +32 -0
  85. data/app/views/layouts/para/application.html.erb +14 -0
  86. data/app/views/para/admin/component_sections/_form.html.haml +10 -0
  87. data/app/views/para/admin/component_sections/edit.html.haml +5 -0
  88. data/app/views/para/admin/component_sections/new.html.haml +5 -0
  89. data/app/views/para/admin/components/_form.html.haml +15 -0
  90. data/app/views/para/admin/components/new.html.haml +4 -0
  91. data/app/views/para/admin/crud_component/show.html.haml +4 -0
  92. data/app/views/para/admin/dashboard.html.haml +10 -0
  93. data/app/views/para/admin/main/index.html.haml +1 -0
  94. data/app/views/para/admin/resources/_exports_menu.html.haml +12 -0
  95. data/app/views/para/admin/resources/_fields.html.haml +5 -0
  96. data/app/views/para/admin/resources/_filters.html.haml +9 -0
  97. data/app/views/para/admin/resources/_form.html.haml +6 -0
  98. data/app/views/para/admin/resources/_list.html.haml +22 -0
  99. data/app/views/para/admin/resources/_table.html.haml +8 -0
  100. data/app/views/para/admin/resources/_tree.html.haml +15 -0
  101. data/app/views/para/admin/resources/_tree_item.html.haml +16 -0
  102. data/app/views/para/admin/resources/edit.html.haml +6 -0
  103. data/app/views/para/admin/resources/new.html.haml +6 -0
  104. data/app/views/para/admin/settings_component/show.html.haml +12 -0
  105. data/app/views/para/admin/shared/_breadcrumb.html.haml +3 -0
  106. data/app/views/para/admin/shared/_header.html.haml +27 -0
  107. data/app/views/para/admin/shared/_navigation.html.haml +21 -0
  108. data/app/views/para/admin/singleton_resource_component/show.html.haml +5 -0
  109. data/app/views/para/inputs/_nested_many.html.haml +10 -0
  110. data/app/views/para/inputs/_nested_many_container.html.haml +19 -0
  111. data/app/views/para/inputs/_nested_one.html.haml +3 -0
  112. data/config/locales/en.yml +86 -0
  113. data/config/locales/fr.yml +101 -0
  114. data/db/migrate/20140911091225_create_para_components.rb +19 -0
  115. data/db/migrate/20140911112150_add_slug_to_para_components.rb +6 -0
  116. data/db/migrate/20140929125733_create_para_component_sections.rb +8 -0
  117. data/db/migrate/20140929131111_add_identifier_to_para_component_sections.rb +5 -0
  118. data/db/migrate/20140930121822_add_position_to_para_component_section.rb +5 -0
  119. data/db/migrate/20150129170710_create_para_component_resources.rb +13 -0
  120. data/db/migrate/20150203173219_add_identifier_to_para_components.rb +5 -0
  121. data/lib/generators/para/admin_user/admin_user_generator.rb +16 -0
  122. data/lib/generators/para/component/component_generator.rb +59 -0
  123. data/lib/generators/para/component/templates/component.rb +3 -0
  124. data/lib/generators/para/component/templates/component_controller.rb +7 -0
  125. data/lib/generators/para/component/templates/show.html.haml +2 -0
  126. data/lib/generators/para/exporter/exporter_generator.rb +42 -0
  127. data/lib/generators/para/exporter/templates/base_exporter.rb +15 -0
  128. data/lib/generators/para/exporter/templates/csv_exporter.rb +13 -0
  129. data/lib/generators/para/form/form_generator.rb +15 -0
  130. data/lib/generators/para/form/templates/_form.html.haml +7 -0
  131. data/lib/generators/para/install/install_generator.rb +112 -0
  132. data/lib/generators/para/install/templates/components.rb +13 -0
  133. data/lib/generators/para/install/templates/initializer.rb +23 -0
  134. data/lib/generators/para/nested_fields/nested_fields_generator.rb +15 -0
  135. data/lib/generators/para/nested_fields/templates/_nested_fields.html.haml +3 -0
  136. data/lib/generators/para/orderable/orderable_generator.rb +53 -0
  137. data/lib/generators/para/orderable/templates/orderable_migration.rb +6 -0
  138. data/lib/generators/para/resource/resource_generator.rb +71 -0
  139. data/lib/generators/para/resource/templates/resource_controller.rb +5 -0
  140. data/lib/generators/para/table/table_generator.rb +34 -0
  141. data/lib/generators/para/table/templates/_table.html.haml +10 -0
  142. data/lib/para/attribute_field/base.rb +77 -0
  143. data/lib/para/attribute_field/belongs_to.rb +29 -0
  144. data/lib/para/attribute_field/boolean.rb +15 -0
  145. data/lib/para/attribute_field/datetime.rb +9 -0
  146. data/lib/para/attribute_field/file.rb +20 -0
  147. data/lib/para/attribute_field/has_many.rb +31 -0
  148. data/lib/para/attribute_field/image.rb +25 -0
  149. data/lib/para/attribute_field/nested_many.rb +26 -0
  150. data/lib/para/attribute_field/nested_one.rb +27 -0
  151. data/lib/para/attribute_field/password.rb +18 -0
  152. data/lib/para/attribute_field/redactor.rb +20 -0
  153. data/lib/para/attribute_field/relation.rb +52 -0
  154. data/lib/para/attribute_field/translation.rb +9 -0
  155. data/lib/para/attribute_field_mappings.rb +82 -0
  156. data/lib/para/cloneable.rb +20 -0
  157. data/lib/para/component/exportable.rb +25 -0
  158. data/lib/para/component.rb +27 -0
  159. data/lib/para/components_configuration.rb +156 -0
  160. data/lib/para/config.rb +21 -0
  161. data/lib/para/engine.rb +59 -0
  162. data/lib/para/errors.rb +15 -0
  163. data/lib/para/exporter/base.rb +27 -0
  164. data/lib/para/exporter/csv.rb +49 -0
  165. data/lib/para/exporter.rb +57 -0
  166. data/lib/para/ext/cancan.rb +26 -0
  167. data/lib/para/ext/paperclip.rb +67 -0
  168. data/lib/para/ext.rb +13 -0
  169. data/lib/para/form_builder/containers.rb +70 -0
  170. data/lib/para/form_builder/field_mappings.rb +9 -0
  171. data/lib/para/form_builder/nested_form.rb +80 -0
  172. data/lib/para/form_builder/ordering.rb +15 -0
  173. data/lib/para/form_builder/settings.rb +24 -0
  174. data/lib/para/form_builder.rb +22 -0
  175. data/lib/para/generators/field_helpers.rb +32 -0
  176. data/lib/para/generators/name_helpers.rb +9 -0
  177. data/lib/para/generators/named_base.rb +7 -0
  178. data/lib/para/generators.rb +11 -0
  179. data/lib/para/inputs/nested_many_input.rb +40 -0
  180. data/lib/para/inputs/nested_one_input.rb +24 -0
  181. data/lib/para/inputs.rb +11 -0
  182. data/lib/para/markup/alert.rb +37 -0
  183. data/lib/para/markup/component.rb +20 -0
  184. data/lib/para/markup/modal.rb +52 -0
  185. data/lib/para/markup/panel.rb +41 -0
  186. data/lib/para/markup/resources_table.rb +178 -0
  187. data/lib/para/markup.rb +10 -0
  188. data/lib/para/model_field_parsers/base.rb +20 -0
  189. data/lib/para/model_field_parsers/devise.rb +37 -0
  190. data/lib/para/model_field_parsers/globalize.rb +23 -0
  191. data/lib/para/model_field_parsers/orderable.rb +15 -0
  192. data/lib/para/model_field_parsers/paperclip.rb +51 -0
  193. data/lib/para/model_field_parsers/redactor.rb +19 -0
  194. data/lib/para/model_field_parsers/relations.rb +67 -0
  195. data/lib/para/model_field_parsers.rb +22 -0
  196. data/lib/para/orderable.rb +44 -0
  197. data/lib/para/routes.rb +49 -0
  198. data/lib/para/version.rb +3 -0
  199. data/lib/para.rb +65 -0
  200. data/lib/rails/relation_length_validator.rb +45 -0
  201. data/lib/rails/routing_mapper.rb +27 -0
  202. data/lib/tasks/para_tasks.rake +22 -0
  203. metadata +595 -0
@@ -0,0 +1,24 @@
1
+ module Para
2
+ module FormBuilder
3
+ module Settings
4
+ def settings_input(name, options = {}, &block)
5
+ options.reverse_merge!(as: :setting)
6
+
7
+ setting = ::Settings.get(name, options[:type])
8
+
9
+ input(name, options) do
10
+ fields_for(:settings, setting) do |fields|
11
+ fields.hidden_field(:key) +
12
+ fields.hidden_field(:_type) +
13
+
14
+ if block
15
+ block.call(fields)
16
+ else
17
+ fields.text_field(:value, class: 'form-control')
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,22 @@
1
+ require 'simple_form/form_builder'
2
+
3
+ require 'para/form_builder/containers'
4
+ require 'para/form_builder/field_mappings'
5
+ require 'para/form_builder/nested_form'
6
+ require 'para/form_builder/ordering'
7
+ require 'para/form_builder/settings'
8
+
9
+ # We'll implement our own form builder later, but for now it would need to
10
+ # patch or override Cocoon to allow creating nested fields with our custom
11
+ # form builder instead of `simple_fields_for` which uses SimpleForm::FormBuilder
12
+ # explicitly
13
+ #
14
+ SimpleForm::FormBuilder.class_eval do
15
+ include Para::FormBuilder::Containers
16
+ include Para::FormBuilder::FieldMappings
17
+ include Para::FormBuilder::NestedForm
18
+ include Para::FormBuilder::Ordering
19
+ include Para::FormBuilder::Settings
20
+ end
21
+
22
+ SimpleForm::FormBuilder.map_type(:inet, to: SimpleForm::Inputs::StringInput)
@@ -0,0 +1,32 @@
1
+ module Para
2
+ module Generators
3
+ module FieldHelpers
4
+ private
5
+
6
+ def attributes
7
+ @attributes ||= begin
8
+ model = Para.const_get(class_name)
9
+ AttributeFieldMappings.new(model).fields
10
+ end
11
+ end
12
+
13
+ def field_options_for(field)
14
+ field_options = field.field_options
15
+
16
+ options = field_options.each_with_object([]) do |(key, value), ary|
17
+ if writable_value?(value)
18
+ ary << "#{ key.inspect } => #{ value.inspect }"
19
+ end
20
+ end
21
+
22
+ ", #{ options.join(', ') }" if options.any?
23
+ end
24
+
25
+ def writable_value?(value)
26
+ [String, Symbol].any? do |type|
27
+ value.kind_of?(type)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,9 @@
1
+ module Para
2
+ module Generators
3
+ module NameHelpers
4
+ def plural_namespaced_path
5
+ @plural_namespaced_path ||= File.join(*class_path, plural_name)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ module Para
2
+ module Generators
3
+ class NamedBase < Rails::Generators::NamedBase
4
+ include Para::Generators::NameHelpers
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ require 'para/generators/name_helpers'
2
+
3
+ module Para
4
+ module Generators
5
+ extend ActiveSupport::Autoload
6
+
7
+ autoload :FieldHelpers
8
+ autoload :NamedBase
9
+ autoload :NameHelpers
10
+ end
11
+ end
@@ -0,0 +1,40 @@
1
+ module Para
2
+ module Inputs
3
+ class NestedManyInput < SimpleForm::Inputs::Base
4
+ def input(wrapper_options = nil)
5
+ input_html_options[:class] << "nested-many"
6
+
7
+ parent_model = @builder.object.class
8
+ model = parent_model.reflect_on_association(attribute_name).klass
9
+ orderable = options.fetch(:orderable, model.orderable?)
10
+ add_button = options.fetch(:add_button, true)
11
+ # Load existing resources
12
+ resources = object.send(attribute_name)
13
+ # Order them if the list should be orderable
14
+ resources = resources.order(:position) if orderable
15
+
16
+ template.render(
17
+ partial: 'para/inputs/nested_many',
18
+ locals: {
19
+ form: @builder,
20
+ model: model,
21
+ attribute_name: attribute_name,
22
+ orderable: orderable,
23
+ add_button: add_button,
24
+ dom_identifier: dom_identifier,
25
+ resources: resources
26
+ }
27
+ )
28
+ end
29
+
30
+ def dom_identifier
31
+ @dom_identifier ||= begin
32
+ name = attribute_name
33
+ time = (Time.now.to_f * 1000).to_i
34
+ random = (rand * 1000).to_i
35
+ [name, time, random].join('-')
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,24 @@
1
+ module Para
2
+ module Inputs
3
+ class NestedOneInput < SimpleForm::Inputs::Base
4
+ def input(wrapper_options = nil)
5
+ input_html_options[:class] << "nested-one"
6
+
7
+ parent_model = object.class
8
+ model = parent_model.reflect_on_association(attribute_name).klass
9
+ resource = object.send(:"#{ attribute_name }") ||
10
+ object.send(:"#{ attribute_name }=", model.new)
11
+
12
+ template.render(
13
+ partial: 'para/inputs/nested_one',
14
+ locals: {
15
+ form: @builder,
16
+ model: model,
17
+ resource: resource,
18
+ attribute_name: attribute_name
19
+ }
20
+ )
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,11 @@
1
+ module Para
2
+ module Inputs
3
+ extend ActiveSupport::Autoload
4
+
5
+ autoload :NestedOneInput
6
+ autoload :NestedManyInput
7
+ autoload :HasOneInput
8
+ end
9
+ end
10
+
11
+ SimpleForm.custom_inputs_namespaces << 'Para::Inputs'
@@ -0,0 +1,37 @@
1
+ module Para
2
+ module Markup
3
+ class Alert < Para::Markup::Component
4
+ def container(message, options = {}, &block)
5
+ if block
6
+ options = message
7
+ message = capture { block.call }
8
+ end
9
+
10
+ type = options.delete(:type) || 'info'
11
+
12
+ merge_class!(options, "alert")
13
+ merge_class!(options, "alert-#{ type }")
14
+
15
+ dismissable = !options.key?(:dismissable) || options.delete(:dismissable)
16
+
17
+ merge_class!(options, "alert-dismissable") if dismissable
18
+
19
+ content_tag :div, options do
20
+ if dismissable
21
+ close_button + message
22
+ else
23
+ message
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def close_button
31
+ content_tag(:button, type: "button", class: "close", "data-dismiss" => "alert") do
32
+ content_tag(:span, '&times;'.html_safe)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,20 @@
1
+ module Para
2
+ module Markup
3
+ class Component
4
+ attr_reader :view
5
+
6
+ delegate :content_tag, :capture, to: :view
7
+
8
+ def initialize view
9
+ @view = view
10
+ end
11
+
12
+ protected
13
+
14
+ def merge_class!(options, klass)
15
+ options[:class] ||= ""
16
+ options[:class] += " #{ klass }"
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,52 @@
1
+ module Para
2
+ module Markup
3
+ class Modal < Para::Markup::Component
4
+ def container(options = {}, &block)
5
+ merge_class!(options, "modal")
6
+ merge_class!(options, "fade") unless options.delete(:fade) == false
7
+ merge_class!(options, "in") if options.delete(:displayed)
8
+
9
+ options.reverse_merge!(
10
+ tabindex: "-1", role: "dialog", "aria-hidden" => "true", "aria-labelledby" => (options[:id] || "modal")
11
+ )
12
+
13
+ content_tag(:div, options) do
14
+ content_tag(:div, class: "modal-dialog") do
15
+ content_tag(:div, class: "modal-content") do
16
+ capture { block.call(self) }
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ def header(options = {}, &block)
23
+ merge_class!(options, "modal-header")
24
+
25
+ content_tag(:div, options) do
26
+ content_tag(:button, "&times;".html_safe, class: "close", type: "button",
27
+ "data-dismiss" => "modal", "aria-hidden" => "true"
28
+ ) +
29
+ content_tag(:h4, class: "modal-title") do
30
+ capture { block.call }
31
+ end
32
+ end
33
+ end
34
+
35
+ def body(options = {}, &block)
36
+ merge_class!(options, "modal-body")
37
+
38
+ content_tag(:div, options) do
39
+ capture { block.call }
40
+ end
41
+ end
42
+
43
+ def footer(options = {}, &block)
44
+ merge_class!(options, "modal-footer")
45
+
46
+ content_tag(:div, options) do
47
+ capture { block.call }
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,41 @@
1
+ module Para
2
+ module Markup
3
+ class Panel < Para::Markup::Component
4
+ def container(options = {}, &block)
5
+ merge_class!(options, "panel")
6
+
7
+ if (type = options.fetch(:type, 'default'))
8
+ merge_class!(options, "panel-#{ type }")
9
+ end
10
+
11
+ content_tag(:div, options) do
12
+ capture { block.call(self) }
13
+ end
14
+ end
15
+
16
+ def header(options = {}, &block)
17
+ merge_class!(options, "panel-heading")
18
+
19
+ content_tag(:div, options) do
20
+ capture { block.call }
21
+ end
22
+ end
23
+
24
+ def body(options = {}, &block)
25
+ merge_class!(options, "panel-body")
26
+
27
+ content_tag(:div, options) do
28
+ capture { block.call }
29
+ end
30
+ end
31
+
32
+ def footer(options = {}, &block)
33
+ merge_class!(options, "panel-footer")
34
+
35
+ content_tag(:div, options) do
36
+ capture { block.call }
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,178 @@
1
+ module Para
2
+ module Markup
3
+ class ResourcesTable < Para::Markup::Component
4
+ attr_reader :model, :component, :orderable, :actions
5
+
6
+ def container(options = {}, &block)
7
+ @model = options.delete(:model)
8
+ @component = options.delete(:component)
9
+
10
+ if !options.key?(:orderable) || options.delete(:orderable)
11
+ @orderable = model.orderable?
12
+ end
13
+
14
+ if !options.key?(:actions) || options.delete(:actions)
15
+ @actions = true
16
+ end
17
+
18
+ merge_class!(options, 'table')
19
+ merge_class!(options, 'para-component-relation-table')
20
+ merge_class!(options, 'table-hover') if options.fetch(:hover, true)
21
+
22
+ if orderable
23
+ merge_class!(options, 'orderable')
24
+ options[:data] ||= {}
25
+ options[:data][:'order-url'] = component.relation_path(model.model_name.route_key, action: :order)
26
+ end
27
+
28
+ table = content_tag(:table, options) do
29
+ capture { block.call(self) }
30
+ end
31
+
32
+ if options.fetch(:responsive, true)
33
+ content_tag(:div, table, class: 'table-responsive')
34
+ else
35
+ table
36
+ end
37
+ end
38
+
39
+ def header(&block)
40
+ cells = []
41
+ # Add orderable empty header
42
+ cells << content_tag(:th, '') if orderable
43
+ # Append cells
44
+ cells << capture { block.call }
45
+ # Append actions empty cell
46
+ cells << content_tag(:th, '', class: 'actions') if actions
47
+
48
+ # Output full header
49
+ content_tag(:thead) do
50
+ content_tag(:tr, cells.join("\n").html_safe)
51
+ end
52
+ end
53
+
54
+ def rows(resources, &block)
55
+ rows = resources.each_with_object(ActiveSupport::SafeBuffer.new('')) do |resource, buffer|
56
+ buffer << content_tag(:tr, row(resource, &block))
57
+ end
58
+
59
+ # Output full header
60
+ content_tag(:tbody, rows)
61
+ end
62
+
63
+ def row(resource, &block)
64
+ cells = []
65
+ # Add orderable cell with "move" thumb
66
+ cells << order_cell(resource) if orderable
67
+ # Add data cells
68
+ cells << capture { block.call(resource) }
69
+ # Add actions links to the last cell
70
+ cells << actions_cell(resource) if actions
71
+
72
+ cells.join("\n").html_safe
73
+ end
74
+
75
+ def header_for(field_name, sort: field_name)
76
+ label = model.human_attribute_name(field_name)
77
+
78
+ content_tag(:th) do
79
+ if sort != field_name
80
+ view.sort_link(search, *sort, label, hide_indicator: true)
81
+ elsif searchable?(field_name)
82
+ view.sort_link(search, field_name, label, hide_indicator: true)
83
+ else
84
+ label
85
+ end
86
+ end
87
+ end
88
+
89
+ # Data for can accept 2 versions of arguments :
90
+ #
91
+ # - resource, field_name, type : cell value will be retrieved from
92
+ # the field_value_for helper
93
+ #
94
+ # - a single value : The value to display in the cell directly
95
+ # which will be processed to be shorter than 100 chars
96
+ #
97
+ def data_for(*args)
98
+ value = if args.length >= 2
99
+ resource, field_name, type = args
100
+ view.field_value_for(resource, field_name, type).to_s
101
+ else
102
+ view.excerpt_value_for(args.first)
103
+ end
104
+
105
+ content_tag(:td) do
106
+ value
107
+ end
108
+ end
109
+
110
+ def order_cell(resource)
111
+ order_cell = content_tag(:td) do
112
+ view.reorder_anchor(
113
+ value: resource.position,
114
+ data: { id: resource.id }
115
+ )
116
+ end
117
+ end
118
+
119
+ def actions_cell(resource)
120
+ content_tag(:td) do
121
+ content_tag(:div, class: 'pull-right btn-group') do
122
+ edit_button(resource) +
123
+ clone_button(resource) +
124
+ delete_button(resource)
125
+ end
126
+ end
127
+ end
128
+
129
+ def clone_button(resource)
130
+ return unless resource.class.cloneable?
131
+
132
+ view.link_to(
133
+ component.relation_path(
134
+ resource, action: :clone, return_to: view.request.fullpath
135
+ ),
136
+ method: :post,
137
+ class: 'btn btn-info'
138
+ ) do
139
+ content_tag(:i, '', class: 'fa fa-refresh')
140
+ end
141
+ end
142
+
143
+ def edit_button(resource)
144
+ view.link_to(
145
+ component.relation_path(
146
+ resource, action: :edit, return_to: view.request.fullpath
147
+ ),
148
+ class: 'btn btn-primary'
149
+ ) do
150
+ content_tag(:i, '', class: 'fa fa-pencil')
151
+ end
152
+ end
153
+
154
+ def delete_button(resource)
155
+ view.link_to(
156
+ component.relation_path(resource),
157
+ method: :delete,
158
+ data: {
159
+ confirm: I18n.t('para.list.delete_confirmation')
160
+ },
161
+ class: 'btn btn-danger'
162
+ ) do
163
+ content_tag(:i, '', class: 'fa fa-trash')
164
+ end
165
+ end
166
+
167
+ private
168
+
169
+ def search
170
+ @search ||= view.instance_variable_get(:@q)
171
+ end
172
+
173
+ def searchable?(field_name)
174
+ model.columns_hash.keys.include?(field_name.to_s)
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,10 @@
1
+ require 'para/markup/component'
2
+ require 'para/markup/resources_table'
3
+ require 'para/markup/modal'
4
+ require 'para/markup/panel'
5
+ require 'para/markup/alert'
6
+
7
+ module Para
8
+ module Markup
9
+ end
10
+ end
@@ -0,0 +1,20 @@
1
+ module Para
2
+ module ModelFieldParsers
3
+ class Base
4
+ def self.register(key, parser)
5
+ ModelFieldParsers.registered_parsers[key] = parser
6
+ end
7
+
8
+ attr_reader :model, :fields_hash
9
+
10
+ def initialize(model, fields_hash)
11
+ @model = model
12
+ @fields_hash = fields_hash
13
+ end
14
+
15
+ def applicable?
16
+ true
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,37 @@
1
+ module Para
2
+ module ModelFieldParsers
3
+ class Devise < Para::ModelFieldParsers::Base
4
+ register :devise, self
5
+
6
+ def parse!
7
+ hidden_fields.each(&fields_hash.method(:delete))
8
+
9
+ added_fields.each do |key|
10
+ fields_hash[key] = AttributeField::PasswordField.new(model, name: key)
11
+ end
12
+ end
13
+
14
+ def applicable?
15
+ fields_hash.key?(:encrypted_password)
16
+ end
17
+
18
+ private
19
+
20
+ def hidden_fields
21
+ [
22
+ :encrypted_password,
23
+ :password_salt,
24
+ :reset_password_token,
25
+ :remember_token
26
+ ]
27
+ end
28
+
29
+ def added_fields
30
+ [
31
+ :password,
32
+ :password_confirmation
33
+ ]
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,23 @@
1
+ module Para
2
+ module ModelFieldParsers
3
+ class Globalize < Para::ModelFieldParsers::Base
4
+ register :globalize, self
5
+
6
+ def parse!
7
+ fields_hash.delete(:translations)
8
+
9
+ model.translated_attribute_names.each do |attribute_name|
10
+ column = model.translation_class.columns_hash[attribute_name.to_s]
11
+
12
+ fields_hash[column.name] = AttributeField::Translation.new(
13
+ model, name: column.name, type: column.type
14
+ )
15
+ end
16
+ end
17
+
18
+ def applicable?
19
+ defined?(::Globalize) && model.translates?
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,15 @@
1
+ module Para
2
+ module ModelFieldParsers
3
+ class Orderable < Para::ModelFieldParsers::Base
4
+ register :orderable, self
5
+
6
+ def parse!
7
+ fields_hash.delete(:position)
8
+ end
9
+
10
+ def applicable?
11
+ model.orderable?
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,51 @@
1
+ module Para
2
+ module ModelFieldParsers
3
+ class Paperclip < Para::ModelFieldParsers::Base
4
+ register :paperclip, self
5
+
6
+ def parse!
7
+ model.attachment_definitions.each do |key, options|
8
+ paperclip_suffixes.each do |suffix|
9
+ field_name = [key, suffix].join('_').to_sym
10
+ @fields_hash.delete(field_name)
11
+ end
12
+
13
+ @fields_hash[key] = if image?(options)
14
+ AttributeField::ImageField.new(
15
+ model, name: key, type: 'image', field_type: 'image'
16
+ )
17
+ else
18
+ AttributeField::FileField.new(
19
+ model, name: key, type: 'file', field_type: 'file'
20
+ )
21
+ end
22
+ end
23
+ end
24
+
25
+ def applicable?
26
+ model.respond_to?(:attachment_definitions)
27
+ end
28
+
29
+ private
30
+
31
+ def paperclip_suffixes
32
+ [
33
+ :file_name,
34
+ :content_type,
35
+ :file_size,
36
+ :updated_at
37
+ ]
38
+ end
39
+
40
+ # For our default, we tell that an attachment is an image if it
41
+ # has at least one style and that this style starts with a digit or the
42
+ # "x" letter, which is used for styles like "x200" to only force height
43
+ #
44
+ def image?(options)
45
+ if (styles = options[:styles]) && !styles.empty?
46
+ styles.values.first.match(/^(\d|x)/i)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end