para 0.5.4 → 0.6.2

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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/para/admin/async-progress.coffee +29 -0
  3. data/app/assets/javascripts/para/admin/filters-form.coffee +1 -1
  4. data/app/assets/javascripts/para/admin/importers.coffee +38 -0
  5. data/app/assets/javascripts/para/admin/table.coffee +1 -1
  6. data/app/assets/javascripts/para/admin/tabs.coffee +1 -1
  7. data/app/assets/javascripts/para/admin/theme_actions.coffee +1 -1
  8. data/app/assets/javascripts/para/admin/tree.coffee +5 -5
  9. data/app/assets/javascripts/para/admin.coffee +2 -1
  10. data/app/assets/javascripts/para/inputs/nested_many.coffee +1 -1
  11. data/app/assets/javascripts/para/lib/remote-file-forms.coffee +60 -0
  12. data/app/assets/javascripts/para/lib/turbolinks-loading.coffee +1 -1
  13. data/app/assets/javascripts/para/lib/turbolinks-reloader.coffee +19 -0
  14. data/app/assets/stylesheets/para/admin/src/datetimepicker.sass +3 -1
  15. data/app/assets/stylesheets/para/admin/src/fuelux.sass +4 -2
  16. data/app/assets/stylesheets/para/admin/src/nested_many.sass +5 -0
  17. data/app/assets/stylesheets/para/admin/src/redactor.sass +8 -5
  18. data/app/assets/stylesheets/para/admin/src/selectize.sass +7 -4
  19. data/app/assets/stylesheets/para/admin/theme/_breadcrumb.sass +20 -12
  20. data/app/assets/stylesheets/para/admin/theme/_buttons.sass +6 -2
  21. data/app/assets/stylesheets/para/admin/theme/_checkable.sass +4 -2
  22. data/app/assets/stylesheets/para/admin/theme/_commonds.sass +6 -3
  23. data/app/assets/stylesheets/para/admin/theme/_dropdown.sass +3 -1
  24. data/app/assets/stylesheets/para/admin/theme/_form.sass +4 -2
  25. data/app/assets/stylesheets/para/admin/theme/_list.sass +7 -5
  26. data/app/assets/stylesheets/para/admin/theme/_navigation.sass +12 -66
  27. data/app/assets/stylesheets/para/admin/theme/_navtabs.sass +18 -14
  28. data/app/assets/stylesheets/para/admin/theme/_orderable.sass +7 -3
  29. data/app/assets/stylesheets/para/admin/theme/_panel.sass +19 -13
  30. data/app/assets/stylesheets/para/admin/theme/_tree.sass +4 -2
  31. data/app/assets/stylesheets/para/overrides/theme.sass +14 -10
  32. data/app/controllers/para/admin/base_controller.rb +0 -5
  33. data/app/controllers/para/admin/crud_resources_controller.rb +5 -19
  34. data/app/controllers/para/admin/{singleton_resources_controller.rb → form_resources_controller.rb} +4 -4
  35. data/app/controllers/para/admin/imports_controller.rb +68 -0
  36. data/app/controllers/para/admin/resources_controller.rb +0 -1
  37. data/app/decorators/para/component/base_decorator.rb +6 -18
  38. data/app/decorators/para/component/crud_decorator.rb +3 -3
  39. data/app/decorators/para/component/{singleton_resource_decorator.rb → form_decorator.rb} +6 -6
  40. data/app/helpers/para/admin/components_helper.rb +12 -1
  41. data/app/helpers/para/admin/decorators_helper.rb +13 -0
  42. data/app/helpers/para/markup_helper.rb +4 -0
  43. data/app/helpers/para/search_helper.rb +1 -4
  44. data/app/models/para/component/base.rb +17 -14
  45. data/app/models/para/component/crud.rb +17 -0
  46. data/app/models/para/component/form.rb +36 -0
  47. data/app/models/para/component/resource.rb +9 -20
  48. data/app/models/para/component/settings.rb +1 -1
  49. data/app/models/para/library/file.rb +23 -0
  50. data/app/models/para/library.rb +5 -0
  51. data/app/views/para/admin/form_resources/show.html.haml +4 -0
  52. data/app/views/para/admin/imports/_completed.html.haml +16 -0
  53. data/app/views/para/admin/imports/_failed.html.haml +7 -0
  54. data/app/views/para/admin/imports/_progress.html.haml +5 -0
  55. data/app/views/para/admin/imports/new.html.haml +14 -0
  56. data/app/views/para/admin/imports/show.html.haml +10 -0
  57. data/app/views/para/admin/resources/_fields.html.haml +1 -4
  58. data/app/views/para/admin/resources/_imports_menu.html.haml +17 -20
  59. data/app/views/para/admin/resources/_tree.html.haml +1 -1
  60. data/app/views/para/admin/shared/_breadcrumbs.html.haml +1 -4
  61. data/app/views/para/admin/shared/_navigation.html.haml +2 -2
  62. data/app/views/para/form/_tabs.html.haml +1 -1
  63. data/config/locales/en.yml +10 -0
  64. data/config/locales/fr.yml +12 -2
  65. data/db/migrate/20160905134106_create_para_library_files.rb +9 -0
  66. data/lib/generators/para/importer/templates/base_importer.rb +8 -1
  67. data/lib/generators/para/install/install_generator.rb +1 -2
  68. data/lib/generators/para/install/templates/components.rb +9 -5
  69. data/lib/generators/para/tree_item/tree_item_generator.rb +17 -0
  70. data/lib/para/attribute_field/base.rb +12 -4
  71. data/lib/para/attribute_field/enum.rb +7 -3
  72. data/lib/para/component/importable.rb +4 -4
  73. data/lib/para/component.rb +1 -1
  74. data/lib/para/components_cleaner.rb +66 -0
  75. data/lib/para/components_configuration.rb +3 -1
  76. data/lib/para/engine.rb +36 -3
  77. data/lib/para/ext/active_job_status.rb +13 -0
  78. data/lib/para/ext/paperclip.rb +10 -0
  79. data/lib/para/ext/simple_form_extension.rb +6 -0
  80. data/lib/para/ext.rb +1 -0
  81. data/lib/para/form_builder/nested_form.rb +5 -2
  82. data/lib/para/importer/base.rb +84 -7
  83. data/lib/para/inputs/nested_many_input.rb +1 -1
  84. data/lib/para/logging/active_job_log_subscriber.rb +48 -0
  85. data/lib/para/logging.rb +8 -0
  86. data/lib/para/markup/resources_table.rb +2 -4
  87. data/lib/para/model_field_parsers/enums.rb +19 -0
  88. data/lib/para/model_field_parsers/relations.rb +1 -1
  89. data/lib/para/model_field_parsers/store.rb +1 -1
  90. data/lib/para/model_field_parsers.rb +1 -0
  91. data/lib/para/plugins/routes.rb +2 -4
  92. data/lib/para/postgres_extensions_checker.rb +30 -0
  93. data/lib/para/routes.rb +5 -4
  94. data/lib/para/routing/component_controller_constraint.rb +36 -0
  95. data/lib/para/routing.rb +7 -0
  96. data/lib/para/sti/root_model.rb +8 -2
  97. data/lib/para/version.rb +1 -1
  98. data/lib/para.rb +10 -0
  99. data/lib/rails/routing_mapper.rb +59 -27
  100. data/lib/tasks/para_tasks.rake +21 -11
  101. metadata +73 -15
  102. data/app/models/para/component/singleton_resource.rb +0 -35
  103. data/app/views/para/admin/singleton_resources/show.html.haml +0 -4
@@ -1,23 +1,23 @@
1
1
  module Para
2
2
  module Component
3
- module SingletonResourceDecorator
3
+ module FormDecorator
4
4
  include Para::Component::BaseDecorator
5
5
 
6
- def path(options = {})
7
- find_path([:admin, self, :resource], options)
6
+ def path(namespace: :resource, **options)
7
+ find_path([:admin, self, namespace], options)
8
8
  end
9
9
 
10
10
  def relation_path(controller_or_resource, *nested_resources, **options)
11
11
  nested = nested_resources.any?
12
- nested_resources << :resource unless nested
13
12
 
14
13
  if Hash === controller_or_resource
15
14
  options = controller_or_resource
16
15
  end
17
16
 
18
17
  options[:action] = action_option_for(options, nested: nested)
19
- data = [:admin, self, *nested_resources]
20
- polymorphic_path(data, options)
18
+ data = [:admin, self, :resource, *nested_resources]
19
+
20
+ find_path(data, options)
21
21
  end
22
22
 
23
23
  def action_option_for(options, nested: false)
@@ -1,7 +1,18 @@
1
1
  module Para
2
2
  module Admin::ComponentsHelper
3
+ # Return the sections / components structure, with components properly
4
+ # decorated
5
+ #
6
+ def admin_component_sections
7
+ @admin_component_sections ||= begin
8
+ Para::ComponentSection.ordered.includes(:components).tap do |sections|
9
+ sections.flat_map(&:components).each(&method(:decorate))
10
+ end
11
+ end
12
+ end
13
+
3
14
  def ordered_components
4
- @component_sections.each_with_object([]) do |section, components|
15
+ admin_component_sections.each_with_object([]) do |section, components|
5
16
  section.components.each do |component|
6
17
  components << component if can?(:read, component)
7
18
  end
@@ -0,0 +1,13 @@
1
+ module Para
2
+ module Admin
3
+ module DecoratorsHelper
4
+ def decorated(object)
5
+ object.tap(&method(:decorate))
6
+ end
7
+
8
+ def decorate(object)
9
+ ActiveDecorator::Decorator.instance.decorate(object)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -11,5 +11,9 @@ module Para
11
11
  def panel(options = {}, &block)
12
12
  Para::Markup::Panel.new(self).container(options, &block)
13
13
  end
14
+
15
+ def modal(options = {}, &block)
16
+ Para::Markup::Modal.new(self).container(options, &block)
17
+ end
14
18
  end
15
19
  end
@@ -9,10 +9,7 @@ module Para
9
9
  end
10
10
 
11
11
  def searchable_attributes(attributes)
12
- whitelist = attributes.select do |attribute|
13
- [:string, :text].include?(attribute.type.to_sym) &&
14
- !attribute.name.match(/password/)
15
- end
12
+ whitelist = attributes.select(&:searchable?)
16
13
 
17
14
  whitelist.map do |attribute|
18
15
  attribute.attribute_column_path.join('_')
@@ -3,13 +3,19 @@ module Para
3
3
  class Base < ActiveRecord::Base
4
4
  self.table_name = 'para_components'
5
5
 
6
- class_attribute :component_name, :configurable_attributes
6
+ class_attribute :component_name
7
7
 
8
8
  def self.register(name, component)
9
9
  self.component_name = name
10
10
  Para::Component.registered_components[name] = component
11
11
  end
12
12
 
13
+ def self.configurable_on(key, options = {})
14
+ store_accessor(:configuration, key)
15
+ end
16
+
17
+ configurable_on :controller
18
+
13
19
  belongs_to :component_section, class_name: 'Para::ComponentSection'
14
20
 
15
21
  validates :identifier, :type, presence: true
@@ -37,19 +43,6 @@ module Para
37
43
  @model_name ||= ModelName.new(self)
38
44
  end
39
45
 
40
- def self.configurable_on(key, options = {})
41
- store_accessor(:configuration, key)
42
- configurable_attributes[key] = options
43
- end
44
-
45
- def self.configurable?
46
- configurable_attributes.length > 0
47
- end
48
-
49
- def self.configurable_attributes
50
- @configurable_attributes ||= {}
51
- end
52
-
53
46
  def default_form_actions
54
47
  [:submit, :submit_and_edit, :submit_and_add_another, :cancel]
55
48
  end
@@ -58,6 +51,16 @@ module Para
58
51
  slug
59
52
  end
60
53
 
54
+ # This method is used by the components configuration system to assign
55
+ # updated attributes from the config file to the component.
56
+ #
57
+ # This is meant to be overriden by components that have to define specific
58
+ # behavior, like for the Crud component
59
+ #
60
+ def update_with(attributes)
61
+ assign_attributes(attributes)
62
+ end
63
+
61
64
  private
62
65
 
63
66
  def ensure_slug
@@ -13,6 +13,8 @@ module Para
13
13
  has_many :component_resources, class_name: 'Para::ComponentResource',
14
14
  foreign_key: :component_id, autosave: true, dependent: :destroy
15
15
 
16
+ before_validation :ensure_model_type
17
+
16
18
  def namespaced?
17
19
  case namespaced
18
20
  when 'true' then true
@@ -32,6 +34,17 @@ module Para
32
34
  component_resources.where(resource: resource).first.destroy
33
35
  end
34
36
 
37
+ def update_with(attributes)
38
+ # If no model_type is provided in the configuration file, default to
39
+ # the singular and camelized version of the identifier, allowing to
40
+ # create crud components without setting the :model_type option, when
41
+ # given a conventional name
42
+ attributes[:model_type] ||= identifier.to_s.camelize.singularize if identifier
43
+ attributes[:controller] ||= '/para/admin/crud_resources'
44
+
45
+ super
46
+ end
47
+
35
48
  private
36
49
 
37
50
  def namespaced_resources
@@ -42,6 +55,10 @@ module Para
42
55
  para_component_resources: { component_id: id }
43
56
  )
44
57
  end
58
+
59
+ def ensure_model_type
60
+ self.model_type ||= identifier
61
+ end
45
62
  end
46
63
  end
47
64
  end
@@ -0,0 +1,36 @@
1
+ module Para
2
+ module Component
3
+ class Form < Para::Component::Resource
4
+ register :form, self
5
+
6
+ configurable_on :model_type
7
+
8
+ has_one :component_resource, class_name: 'Para::ComponentResource',
9
+ foreign_key: :component_id, autosave: true, dependent: :destroy
10
+
11
+ def resource
12
+ build_component_resource(resource: model.new) unless component_resource
13
+ component_resource.resource ||= model.new
14
+ end
15
+
16
+ def resource=(value)
17
+ build_component_resource(resource: value) unless component_resource
18
+ end
19
+
20
+ def default_form_actions
21
+ [:submit]
22
+ end
23
+
24
+ def update_with(attributes)
25
+ # If no model_type is provided in the configuration file, default to
26
+ # the camelized version of the identifier, allowing to create
27
+ # form components without setting the :model_type option,
28
+ # when given a conventional name
29
+ attributes[:model_type] ||= identifier.to_s.camelize.singularize if identifier
30
+ attributes[:controller] ||= '/para/admin/form_resources'
31
+
32
+ super
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,33 +1,22 @@
1
1
  module Para
2
2
  module Component
3
3
  class Resource < Para::Component::Base
4
+ class ModelNotFound < NameError; end
5
+
4
6
  def model
5
7
  @model ||= model_type.presence && model_type.constantize
6
- end
7
-
8
- def available_models
9
- Rails.application.eager_load!
10
-
11
- ActiveRecord::Base.descendants.map(&:name).sort.reject do |name|
12
- next true if name == 'ActiveRecord::SchemaMigration'
13
- next true if name.match(/^HABTM_/)
14
- next true if name.match(/Component$/)
15
- next true if name.match(/^Para::Component/)
16
- next true if excluded_models.include?(name)
17
-
18
- false
19
- end
8
+ rescue NameError
9
+ raise ModelNotFound,
10
+ "The model #{ model_type } was not found. You may need to set " +
11
+ "the :model_type option in your component definition in the " +
12
+ "config/components.rb file. If no component should actually " +
13
+ "reference this model, you may need to run the " +
14
+ "`rake para:components:clean` task to clean up your components index."
20
15
  end
21
16
 
22
17
  def model_table_name
23
18
  model && model.table_name
24
19
  end
25
-
26
- private
27
-
28
- def excluded_models
29
- %w(FriendlyId::Slug)
30
- end
31
20
  end
32
21
  end
33
22
  end
@@ -3,7 +3,7 @@ module Para
3
3
  class Settings < Para::Component::Base
4
4
  register :settings, self
5
5
 
6
- configurable_on :settings, as: :selectize
6
+ configurable_on :settings
7
7
 
8
8
  def settings
9
9
  eval(configuration['settings'])
@@ -0,0 +1,23 @@
1
+ module Para
2
+ module Library
3
+ class File < ActiveRecord::Base
4
+ has_attached_file :attachment
5
+
6
+ validates :attachment, presence: true
7
+ do_not_validate_attachment_file_type :attachment
8
+
9
+ # Return attachment.path or attachment.url depending on the storage
10
+ # backend, allowing 'openuri' and 'roo' libraries to load easily the
11
+ # file at the right path, on filesystem or othe storage systems, like S3.
12
+ #
13
+ def attachment_path
14
+ return unless attachment?
15
+
16
+ case attachment.options[:storage]
17
+ when :filesystem then attachment.path
18
+ else attachment.url
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,5 @@
1
+ module Para::Library
2
+ def self.table_name_prefix
3
+ 'para_library_'
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ = page_top_bar(title: @component.name, type: 'form/show')
2
+
3
+ .page-content-wrap
4
+ = render find_partial_for(resource, :form)
@@ -0,0 +1,16 @@
1
+ = modal.body do
2
+ - if status[:errors].any?
3
+ .alert.alert-warning
4
+ = t('para.flash.shared.import.success_with_errors')
5
+
6
+ %ul
7
+ - status[:errors].each do |message|
8
+ %li= message.html_safe
9
+
10
+ - else
11
+ .alert.alert-success
12
+ = t('para.flash.shared.import.success')
13
+
14
+ = modal.footer do
15
+ %button.btn.btn-default{ data: { dismiss: 'modal' } }
16
+ = t('para.shared.close')
@@ -0,0 +1,7 @@
1
+ = modal.body do
2
+ .alert.alert-danger
3
+ = t('para.flash.shared.import.error')
4
+
5
+ = modal.footer do
6
+ %button.btn.btn-default{ data: { dismiss: 'modal' } }
7
+ = t('para.shared.close')
@@ -0,0 +1,5 @@
1
+ = modal.body do
2
+ %p= t('para.import.importing_file')
3
+
4
+ .progress{ data: { :'async-progress' => 'import' } }
5
+ .progress-bar.progress-bar-striped.active{ role: "progressbar", style: "width: #{ status.progress * 100 }%" }
@@ -0,0 +1,14 @@
1
+ = modal id: "component-import-#{ @importer.model_name.route_key }" do |modal|
2
+ = modal.header do
3
+ = @importer.model_name.human
4
+
5
+ = simple_form_for @file, as: :file, url: @component.path(namespace: :imports, importer: @importer.name), remote: true do |form|
6
+ = modal.body do
7
+ %p= t('para.import.help_text')
8
+
9
+ = form.input :attachment, label: false, placeholder: t('para.import.placeholder')
10
+
11
+ = modal.footer do
12
+ %button.btn.btn-primary{ type: 'submit' }
13
+ = fa_icon 'upload'
14
+ = t('para.import.name')
@@ -0,0 +1,10 @@
1
+ = modal id: "component-import-#{ @importer.model_name.route_key }", data: { :'import-status-url' => @component.path(namespace: :import, importer: @importer.name, id: @status.job_id) } do |modal|
2
+ = modal.header do
3
+ = @importer.model_name.human
4
+
5
+ - if @status.failed?
6
+ = render partial: 'failed', locals: { modal: modal }
7
+ - elsif @status.completed?
8
+ = render partial: 'completed', locals: { modal: modal, status: @status }
9
+ - else
10
+ = render partial: 'progress', locals: { modal: modal, status: @status }
@@ -1,5 +1,2 @@
1
1
  - form.nested_fields.each do |field|
2
- - if field.field_type
3
- = form.send(field.field_method, field.field_name, as: field.field_type)
4
- - else
5
- = form.send(field.field_method, field.field_name)
2
+ = form.send(field.field_method, field.field_name, field.field_options)
@@ -1,22 +1,19 @@
1
1
  - if component.importable?
2
- - component.imports.each do |importer|
3
- = form_tag component.relation_path(model.model_name.route_key, action: :import, importer: importer), method: 'post', multipart: 'true' do
4
- .fileinput.fileinput-new.input-group{ data: { provides: 'fileinput' } }
5
- .form-control.uneditable-input{ data: { trigger: 'fileinput' } }
6
- %span.fileinput-placeholder
7
- = t('para.import.placeholder')
8
- = fa_icon 'file', class: 'fileinput-exists'
9
- %span.fileinput-filename
10
- .input-group-btn
11
- .btn.btn-default.btn-file
12
- %span.fileinput-new
13
- = t('para.import.select')
14
- %span.fileinput-exists
15
- = t('para.import.change')
16
- = file_field_tag 'file', class: 'file file-upload'
17
- .btn.btn-danger.fileinput-exists{ type: 'button', data: { dismiss: 'fileinput' } }
18
- = fa_icon 'times'
2
+ - if component.importers.length == 1
3
+ - importer = component.importers.first
19
4
 
20
- %button.btn.btn-default{ type: 'submit' }
21
- = fa_icon 'upload'
22
- = t('para.import.name')
5
+ = link_to component.path(namespace: :import, action: :new, importer: importer.model_name.singular_route_key), class: 'btn btn-default', remote: true, data: { :'importer-button' => true } do
6
+ = fa_icon 'upload'
7
+ = importer.model_name.human
8
+
9
+ - else
10
+ .btn-group
11
+ %button.btn.btn-default{ type: 'button', data: { toggle: 'dropdown' } }
12
+ = fa_icon 'upload'
13
+ = t('para.import.name')
14
+ %span.caret
15
+ %ul.dropdown-menu
16
+ - component.importers.each do |importer|
17
+ %li
18
+ = link_to component.path(:imports, action: :new, importer: importer.model_name.singular_route_key), remote: true, data: { :'importer-button' => true } do
19
+ = importer.model_name.human
@@ -1,7 +1,7 @@
1
1
  .page-content-wrap
2
2
  = panel do |panel|
3
3
  = panel.header do
4
- = icon_link_to t('para.shared.add'), component.relation_path(model.model_name.singular_route_key, action: :new), icon: 'plus', class: 'btn btn-primary'
4
+ = render partial: find_partial_for(relation, :actions), locals: { relation: relation, component: component, model: model, allow_adding_resource: allow_adding_resource }
5
5
 
6
6
  - if resources.length > 0
7
7
  = panel.body do
@@ -2,7 +2,4 @@
2
2
  - breadcrumbs.each do |breadcrumb|
3
3
  - active = breadcrumb.active?(request)
4
4
  %li{ class: ('active' if active) }
5
- - if active
6
- = breadcrumb.title
7
- - else
8
- = link_to breadcrumb.title, breadcrumb.path
5
+ = link_to breadcrumb.title, breadcrumb.path
@@ -3,8 +3,8 @@
3
3
  = link_to admin_path do
4
4
  = t('para.admin.title')
5
5
 
6
- - if @component_sections.any?
7
- - @component_sections.each do |component_section|
6
+ - if admin_component_sections.any?
7
+ - admin_component_sections.each do |component_section|
8
8
  - next unless component_section.components.any? { |component| can?(:manage, component) }
9
9
 
10
10
  %li.component-section-item
@@ -1,4 +1,4 @@
1
- .form-tabs{ data: { 'form-tabs': true } }
1
+ .form-tabs{ data: { :'form-tabs' => true } }
2
2
  %nav.navbar.navbar-inverse
3
3
  %ul.nav.navbar-nav{ role: 'tablist' }
4
4
  - tabs.each_with_index do |tab, index|
@@ -14,6 +14,9 @@ en:
14
14
 
15
15
  admin:
16
16
  title: "Admin"
17
+ toggle_navigation: "Open navigation"
18
+ back_to_app: "Back to app"
19
+ sign_out: "Sign out"
17
20
 
18
21
  confirmation:
19
22
  shared:
@@ -28,6 +31,9 @@ en:
28
31
  singleton_resource:
29
32
  name: "Resource form"
30
33
 
34
+ dashboard:
35
+ title: "Dashboard"
36
+
31
37
  component_section:
32
38
  add: "Add a component section"
33
39
  edit: 'Edit section'
@@ -73,6 +79,10 @@ en:
73
79
  "true": "Yes"
74
80
  "false": "No"
75
81
 
82
+ admin:
83
+ breadcrumbs:
84
+ home: "Dashboard"
85
+
76
86
  activerecord:
77
87
  errors:
78
88
  relation_length_is_smaller: "must contain at least %{minimum} elements"
@@ -15,8 +15,12 @@ fr:
15
15
  success: "%{model} cloné(e)"
16
16
  error: "Impossible de cloner le(a) %{model}"
17
17
  import:
18
- success: "Import réussi"
19
- error: "Merci de sélectionner un fichier à importer"
18
+ success: "L'import du fichier a été effectué avec succès"
19
+ success_with_errors: |
20
+ L'import du fichier a été effectué, mais certaines lignes n'ont pas
21
+ été prises en compte à causes d'erreurs :
22
+ other_errors: "<br>Et <b>%{count}</b> autres erreurs ..."
23
+ error: "Le fichier choisi contient des erreurs et n'a pu être importé"
20
24
 
21
25
  admin:
22
26
  title: "Administration"
@@ -60,7 +64,12 @@ fr:
60
64
  name: Importer
61
65
  select: "Sélectionnez un fichier"
62
66
  change: "Changer"
67
+ help_text: |
68
+ Sélectionnez un fichier Excel (.xls, .xlsx) ou CSV (.csv) à importer et
69
+ cliquez sur le bouton "Importer".
63
70
  placeholder: Fichier au format ( .csv .xlsx )
71
+ row_error_prefix: "Ligne %{number} :"
72
+ importing_file: "Le fichier est en cours d'import, merci de patienter quelques instants ..."
64
73
 
65
74
  shared:
66
75
  save: "Enregistrer"
@@ -70,6 +79,7 @@ fr:
70
79
  save_and_edit: "Enregistrer et éditer"
71
80
  save_and_add_another_button: "Enregistrer et créer un(e) autre"
72
81
  destroy: "Supprimer"
82
+ close: "Fermer"
73
83
 
74
84
  types:
75
85
  boolean:
@@ -0,0 +1,9 @@
1
+ class CreateParaLibraryFiles < ActiveRecord::Migration
2
+ def change
3
+ create_table :para_library_files do |t|
4
+ t.attachment :attachment
5
+
6
+ t.timestamps null: false
7
+ end
8
+ end
9
+ end
@@ -1,5 +1,12 @@
1
1
  class <%= model_importer_name %> < Para::Importer::Base
2
+ # Let the importer rescue ActiveRecord::RecordInvalid errors and display them
3
+ # as flash messages after importing valid records
4
+ #
5
+ # Remove or comment this line to disable this behavior
6
+ #
7
+ allow_import_errors!
8
+
2
9
  def import_from_row(row)
3
10
  # Add your import logic here
4
11
  end
5
- end
12
+ end
@@ -24,7 +24,7 @@ module Para
24
24
  gemfile_contents = File.read(Rails.root.join('Gemfile'))
25
25
 
26
26
  [
27
- ['devise', '~> 3.0'],
27
+ ['devise', '>= 3.0'],
28
28
  # Allows for installing default wrappers and bootstrap adapters
29
29
  # This should be avoided when add an initializer namespaced to the
30
30
  # para environment
@@ -32,7 +32,6 @@ module Para
32
32
  ['simple_form_extension'],
33
33
  # Pull requests are pending, and I don't want to release the gem
34
34
  # under another name to be able to depend on it
35
- ['active_decorator', github: 'glyph-fr/active_decorator', branch: 'dev'],
36
35
  ['kaminari', '>= 0.16.1'],
37
36
  ['ransack', '>= 1.4.1'],
38
37
  ['bootstrap-kaminari-views', '>= 0.0.5']
@@ -3,11 +3,15 @@ Para.components.draw do
3
3
  # You can translate the section title at: components.section.menu
4
4
  #
5
5
  # section :menu do
6
- #
7
- # # Create a component of type :crud in the the section :menu, with the
8
- # # identifier :pages, with the model 'Page' as the target type and
9
- # # namespacing it's managed to resources to the component
6
+ # # Creating crud components can be done the following way
7
+ # #
8
+ # component :pages, :crud
9
+ # component :members, :crud, model_type: 'User'
10
+ # component :news, :crud, model_type: 'Page', namespaced: true
11
+
12
+ # # Creating a component allowing you to edit a single resource is done
13
+ # # with the `singleton_resource` component, the following way
10
14
  # #
11
- # component :pages, :crud, model_type: 'Page', namespaced: true
15
+ # component :home, :singleton_resource, model_type: 'HomePage'
12
16
  # end
13
17
  end
@@ -0,0 +1,17 @@
1
+ module Para
2
+ class TreeItemGenerator < Para::Generators::NamedBase
3
+ include Para::Admin::BaseHelper
4
+ include Para::Generators::FieldHelpers
5
+
6
+ source_root File.expand_path('../../../../../app/views/para/admin/resources', __FILE__)
7
+
8
+ desc 'Para resource tree item generator'
9
+
10
+ def generate_table
11
+ template(
12
+ '_tree_item.html.haml',
13
+ "app/views/admin/#{ plural_namespaced_path }/_tree_item.haml"
14
+ )
15
+ end
16
+ end
17
+ end
@@ -4,7 +4,7 @@ module Para
4
4
  class_attribute :_field_options
5
5
  cattr_accessor :_field_types
6
6
 
7
- attr_reader :model, :name, :type, :field_type, :field_method
7
+ attr_reader :model, :name, :type, :field_type, :field_method, :options
8
8
 
9
9
  def self.field_option(key, method_name, options = {})
10
10
  self._field_options ||= []
@@ -43,9 +43,10 @@ module Para
43
43
 
44
44
  def initialize(model, options = {})
45
45
  @model = model
46
- @name = options[:name]
47
- @type = options[:type]
48
- @field_type = options[:field_type]
46
+ @name = options.delete(:name)
47
+ @type = options.delete(:type)
48
+ @field_type = options.delete(:field_type)
49
+ @options = options
49
50
 
50
51
  determine_name_and_field_method!
51
52
  end
@@ -70,6 +71,13 @@ module Para
70
71
  instance.send(name)
71
72
  end
72
73
 
74
+ #
75
+ def searchable?
76
+ options[:searchable] != false && (
77
+ [:string, :text].include?(type.to_sym) && !name.match(/password/)
78
+ )
79
+ end
80
+
73
81
  # Allows parsing input params before they're passed to the model, so
74
82
  # it can be easy to edit them according to some field type specific
75
83
  # behavior