para 0.5.4 → 0.6.2

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