para 0.7.1 → 0.7.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.
- checksums.yaml +4 -4
- data/app/assets/javascripts/para/admin/tabs.coffee +20 -1
- data/app/assets/javascripts/para/admin.coffee +1 -0
- data/app/assets/javascripts/para/inputs/multi-select-input.coffee +5 -3
- data/app/assets/javascripts/para/inputs/nested_many.coffee +35 -1
- data/app/assets/javascripts/para/lib/remote-file-forms.coffee +6 -7
- data/app/assets/javascripts/para/lib/turbolinks-loading.coffee +1 -1
- data/app/assets/stylesheets/para/admin/main.sass +1 -0
- data/app/assets/stylesheets/para/admin/src/_affix.sass +4 -0
- data/app/assets/stylesheets/para/admin/src/_base.sass +4 -1
- data/app/assets/stylesheets/para/admin/src/_breadcrumb.sass +12 -2
- data/app/assets/stylesheets/para/admin/src/_form.sass +22 -3
- data/app/assets/stylesheets/para/admin/src/_navtabs.sass +23 -6
- data/app/assets/stylesheets/para/admin/src/_nested-many.sass +5 -0
- data/app/assets/stylesheets/para/admin/src/_nested_one.sass +6 -5
- data/app/controllers/para/admin/base_controller.rb +5 -0
- data/app/controllers/para/admin/crud_resources_controller.rb +1 -1
- data/app/controllers/para/admin/exports_controller.rb +2 -1
- data/app/controllers/para/admin/nested_forms_controller.rb +13 -0
- data/app/controllers/para/admin/resources_controller.rb +14 -3
- data/app/controllers/para/admin/search_controller.rb +7 -5
- data/app/decorators/para/component/base_decorator.rb +7 -2
- data/app/decorators/para/component/crud_decorator.rb +4 -0
- data/app/decorators/para/component/form_decorator.rb +6 -0
- data/app/exporters/call_for_projects_votes_exporter.rb +79 -0
- data/app/helpers/para/admin/base_helper.rb +2 -3
- data/app/helpers/para/admin/history_helper.rb +16 -0
- data/app/helpers/para/form_helper.rb +9 -1
- data/app/helpers/para/model_helper.rb +0 -2
- data/app/models/para/cache/item.rb +4 -0
- data/app/models/para/component/base.rb +4 -0
- data/app/models/para/component/crud.rb +1 -0
- data/app/models/para/component/form.rb +2 -0
- data/app/models/para/library/file.rb +40 -12
- data/app/models/para/page/section.rb +13 -1
- data/app/views/admin/para/exporter/bases/_completed.html.haml +1 -1
- data/app/views/layouts/para/admin.html.haml +5 -5
- data/app/views/para/admin/form_resources/show.html.haml +6 -2
- data/app/views/para/admin/nested_forms/show.html.haml +9 -0
- data/app/views/para/admin/resources/_exports_menu.html.haml +2 -2
- data/app/views/para/admin/resources/_list.html.haml +26 -21
- data/app/views/para/admin/resources/_remote_nested_form.html.haml +1 -0
- data/app/views/para/admin/resources/edit.html.haml +7 -2
- data/app/views/para/admin/resources/history/_index.html.haml +3 -0
- data/app/views/para/admin/shared/_header.html.haml +3 -3
- data/app/views/para/admin/shared/_navigation.html.haml +1 -1
- data/app/views/para/form/_tabs.html.haml +12 -9
- data/app/views/para/inputs/_multi_select.html.haml +6 -2
- data/app/views/para/inputs/_nested_many.html.haml +4 -4
- data/app/views/para/inputs/_nested_one.html.haml +12 -1
- data/app/views/para/inputs/multi_select/_no_items.html.haml +8 -4
- data/app/views/para/inputs/nested_many/_add.html.haml +2 -2
- data/app/views/para/inputs/nested_many/_add_with_subclasses.html.haml +3 -3
- data/app/views/para/inputs/nested_many/_container.html.haml +10 -7
- data/lib/generators/para/exporter/templates/csv_exporter.rb +24 -0
- data/lib/generators/para/exporter/templates/xls_exporter.rb +24 -0
- data/lib/para/attribute_field/belongs_to.rb +4 -1
- data/lib/para/attribute_field/nested_field.rb +4 -2
- data/lib/para/attribute_field/nested_many.rb +1 -1
- data/lib/para/attribute_field/nested_one.rb +1 -1
- data/lib/para/cache/database_store.rb +14 -1
- data/lib/para/cloneable.rb +44 -2
- data/lib/para/component/history.rb +15 -0
- data/lib/para/component.rb +1 -0
- data/lib/para/components_configuration.rb +8 -3
- data/lib/para/engine.rb +8 -0
- data/lib/para/exporter/base.rb +49 -1
- data/lib/para/ext/request_iframe_xhr.rb +17 -0
- data/lib/para/ext.rb +1 -0
- data/lib/para/form_builder/attributes_mappings_tracker.rb +15 -3
- data/lib/para/form_builder/containers.rb +6 -1
- data/lib/para/form_builder/nested_form.rb +7 -4
- data/lib/para/form_builder/tabs.rb +49 -8
- data/lib/para/iframe_transport/middleware.rb +58 -0
- data/lib/para/iframe_transport.rb +7 -0
- data/lib/para/importer/base.rb +1 -0
- data/lib/para/inputs/multi_select_input.rb +27 -8
- data/lib/para/inputs/nested_base_input.rb +26 -0
- data/lib/para/inputs/nested_many_input.rb +43 -18
- data/lib/para/inputs/nested_one_input.rb +18 -4
- data/lib/para/inputs.rb +1 -0
- data/lib/para/log_config.rb +14 -0
- data/lib/para/markup/resources_table.rb +2 -2
- data/lib/para/model_field_parsers/relations.rb +12 -1
- data/lib/para/page/model.rb +2 -1
- data/lib/para/postgres_extensions_checker.rb +15 -11
- data/lib/para/routes.rb +3 -3
- data/lib/para/search/distinct.rb +1 -1
- data/lib/para/version.rb +1 -1
- data/lib/para.rb +2 -0
- data/lib/rails/routing_mapper.rb +19 -27
- data/vendor/assets/javascripts/jquery.iframe-transport.js +260 -0
- data/vendor/assets/javascripts/jquery.remote-modal-form.coffee +80 -2
- metadata +23 -9
|
@@ -1,21 +1,49 @@
|
|
|
1
1
|
module Para
|
|
2
2
|
module Library
|
|
3
3
|
class File < ActiveRecord::Base
|
|
4
|
-
|
|
4
|
+
if defined?(ActiveStorage)
|
|
5
|
+
has_one_attached :attachment
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
# Return attachment.path or attachment.url depending on the storage
|
|
8
|
+
# backend, allowing 'openuri' and 'roo' libraries to load easily the
|
|
9
|
+
# file at the right path, on filesystem or othe storage systems, like S3.
|
|
10
|
+
#
|
|
11
|
+
def attachment_path
|
|
12
|
+
return unless attachment.attached?
|
|
8
13
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
attachment.service_url
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
alias_method :attachment_url, :attachment_path
|
|
18
|
+
|
|
19
|
+
def attachment_ext
|
|
20
|
+
::File.extname(attachment.filename.to_s) if attachment.attached?
|
|
21
|
+
end
|
|
22
|
+
else
|
|
23
|
+
has_attached_file :attachment
|
|
24
|
+
|
|
25
|
+
validates :attachment, presence: true
|
|
26
|
+
do_not_validate_attachment_file_type :attachment
|
|
27
|
+
|
|
28
|
+
# Return attachment.path or attachment.url depending on the storage
|
|
29
|
+
# backend, allowing 'openuri' and 'roo' libraries to load easily the
|
|
30
|
+
# file at the right path, on filesystem or othe storage systems, like S3.
|
|
31
|
+
#
|
|
32
|
+
def attachment_path
|
|
33
|
+
return unless attachment?
|
|
34
|
+
|
|
35
|
+
case attachment.options[:storage]
|
|
36
|
+
when :filesystem then attachment.path
|
|
37
|
+
else attachment.url
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def attachment_url
|
|
42
|
+
attachment.url if attachment?
|
|
43
|
+
end
|
|
15
44
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
else attachment.url
|
|
45
|
+
def attachment_ext
|
|
46
|
+
::File.extname(attachment_file_name) if attachment.attached?
|
|
19
47
|
end
|
|
20
48
|
end
|
|
21
49
|
end
|
|
@@ -5,7 +5,16 @@ module Para
|
|
|
5
5
|
|
|
6
6
|
acts_as_orderable
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
page_relation_options = { polymorphic: true }
|
|
9
|
+
|
|
10
|
+
# Make Rails 5+ belongs_to relation optional for the parent page, to allow
|
|
11
|
+
# using sections in other contexts that directly included into pages
|
|
12
|
+
#
|
|
13
|
+
if ActiveRecord::Associations::Builder::BelongsTo.valid_options({}).include?(:optional)
|
|
14
|
+
page_relation_options[:optional] = true
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
belongs_to :page, page_relation_options
|
|
9
18
|
|
|
10
19
|
def css_class
|
|
11
20
|
@css_class ||= self.class.name.demodulize.underscore.gsub(/_/, '-')
|
|
@@ -33,6 +42,7 @@ module Para
|
|
|
33
42
|
has_one :section_resource, -> { ordered },
|
|
34
43
|
foreign_key: 'section_id',
|
|
35
44
|
class_name: '::Para::Page::SectionResource',
|
|
45
|
+
inverse_of: :section,
|
|
36
46
|
dependent: :destroy
|
|
37
47
|
|
|
38
48
|
accepts_nested_attributes_for :section_resource, allow_destroy: true
|
|
@@ -46,7 +56,9 @@ module Para
|
|
|
46
56
|
has_many :section_resources, -> { ordered },
|
|
47
57
|
foreign_key: 'section_id',
|
|
48
58
|
class_name: '::Para::Page::SectionResource',
|
|
59
|
+
inverse_of: :section,
|
|
49
60
|
dependent: :destroy
|
|
61
|
+
|
|
50
62
|
accepts_nested_attributes_for :section_resources, allow_destroy: true
|
|
51
63
|
|
|
52
64
|
@section_resources_already_initialized = true
|
|
@@ -17,13 +17,13 @@
|
|
|
17
17
|
|
|
18
18
|
= csrf_meta_tags
|
|
19
19
|
|
|
20
|
-
%body
|
|
21
|
-
= render partial: 'para/admin/shared/
|
|
22
|
-
= render partial: 'para/admin/shared/
|
|
20
|
+
%body{ class: admin_body_class }
|
|
21
|
+
= render partial: template_path_lookup('admin/shared/_navigation', 'para/admin/shared/_navigation')
|
|
22
|
+
= render partial: template_path_lookup('admin/shared/_header', 'para/admin/shared/_header')
|
|
23
23
|
|
|
24
|
-
.container-fluid.page-content
|
|
24
|
+
.container-fluid.page-content.admin-main-page-content
|
|
25
25
|
= display_admin_flash
|
|
26
26
|
|
|
27
27
|
= yield
|
|
28
28
|
|
|
29
|
-
= javascript_include_tag 'para/admin', :'data-turbolinks-eval' => false
|
|
29
|
+
= javascript_include_tag 'para/admin', :'data-turbolinks-eval' => false, :'data-turbolinks-suppress-warning' => true
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
= page_top_bar(title: @component.name, type: 'form/show')
|
|
2
2
|
|
|
3
|
-
.
|
|
4
|
-
|
|
3
|
+
.row
|
|
4
|
+
.page-content-wrap.form-fixed-actions{ class: @component.page_container_class }
|
|
5
|
+
= render find_partial_for(resource, :form)
|
|
6
|
+
|
|
7
|
+
-# Render history module if configured to be displayed
|
|
8
|
+
= history_for(@component, action: :edit, resource: resource)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
- nested_form = nil
|
|
2
|
+
|
|
3
|
+
- para_form_for(@object, url: "") do |form|
|
|
4
|
+
- form.object_name = @object_name
|
|
5
|
+
- nested_form = capture do
|
|
6
|
+
= render(partial: find_partial_for(@model, :remote_nested_form), locals: { form: form, model: @model, object: @object, object_name: @object_name })
|
|
7
|
+
= form.attributes_mappings_field_for(form)
|
|
8
|
+
|
|
9
|
+
= nested_form
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
- if component.exporters.length == 1
|
|
3
3
|
- exporter = component.exporters.first
|
|
4
4
|
|
|
5
|
-
= link_to component.path(namespace: :exports, exporter: exporter.name, q: params[:q]), class: 'btn btn-default btn-shadow', method: :post, remote: true, data: { :'job-tracker-button' => true, :'refresh-on-close' => false } do
|
|
5
|
+
= link_to component.path(namespace: :exports, exporter: exporter.name, q: params[:q].try(:permit!)), class: 'btn btn-default btn-shadow', method: :post, remote: true, data: { :'job-tracker-button' => true, :'refresh-on-close' => false } do
|
|
6
6
|
%i.fa.fa-download.fa-fw
|
|
7
7
|
= exporter.model_name.human
|
|
8
8
|
|
|
@@ -15,5 +15,5 @@
|
|
|
15
15
|
%ul.dropdown-menu
|
|
16
16
|
- component.exporters.each do |exporter|
|
|
17
17
|
%li
|
|
18
|
-
= link_to component.path(namespace: :exports, exporter: exporter.name, q: params[:q]), method: :post, remote: true, data: { :'job-tracker-button' => true, :'refresh-on-close' => false } do
|
|
18
|
+
= link_to component.path(namespace: :exports, exporter: exporter.name, q: params[:q].try(:permit!)), method: :post, remote: true, data: { :'job-tracker-button' => true, :'refresh-on-close' => false } do
|
|
19
19
|
= exporter.model_name.human
|
|
@@ -1,27 +1,32 @@
|
|
|
1
|
-
.page-content-
|
|
2
|
-
.
|
|
3
|
-
.page-
|
|
4
|
-
|
|
1
|
+
.row.page-content-row
|
|
2
|
+
%div{ class: @component.page_container_class }
|
|
3
|
+
.page-content-wrap
|
|
4
|
+
.page-list
|
|
5
|
+
.page-list-heading
|
|
6
|
+
= render partial: find_partial_for(relation, :actions), locals: { relation: relation, component: component, model: model, allow_adding_resource: allow_adding_resource }
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
.page-list-heading
|
|
9
|
+
.flextable
|
|
10
|
+
= render partial: find_partial_for(relation, :filters), locals: { attributes: attributes }
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
- if resources.length > 0
|
|
13
|
+
.page-entries-info
|
|
14
|
+
= page_entries(resources)
|
|
15
|
+
.page-list-body
|
|
16
|
+
= table_for(resources: resources, model: model, component: component, attributes: attributes)
|
|
15
17
|
|
|
16
|
-
- else
|
|
17
|
-
= alert class: 'no-results', dismissable: false do
|
|
18
|
-
- if filtered?(attributes)
|
|
19
|
-
= t('para.list.no_results')
|
|
20
18
|
- else
|
|
21
|
-
=
|
|
19
|
+
= alert class: 'no-results', dismissable: false do
|
|
20
|
+
- if filtered?(attributes)
|
|
21
|
+
= t('para.list.no_results')
|
|
22
|
+
- else
|
|
23
|
+
= t('para.list.empty')
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
= add_button_for(component, relation, model) if allow_adding_resource
|
|
24
26
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
- if resources.total_count > resources.length
|
|
28
|
+
.page-list-footer
|
|
29
|
+
= paginate resources, theme: Para.config.pagination_theme
|
|
30
|
+
|
|
31
|
+
-# Render history module if configured to be displayed
|
|
32
|
+
= history_for(@component, action: :index, resources: resources, relation: relation)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
= render partial: find_partial_for(model, :fields), locals: { form: form }
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
= page_top_bar(title: resource_title_for(resource), type: 'crud/edit')
|
|
2
2
|
|
|
3
|
-
.page-content-
|
|
4
|
-
|
|
3
|
+
.row.page-content-row
|
|
4
|
+
%div{ class: @component.page_container_class }
|
|
5
|
+
.page-content-wrap.form-fixed-actions
|
|
6
|
+
= render find_partial_for(resource, :form)
|
|
7
|
+
|
|
8
|
+
-# Render history module if configured to be displayed
|
|
9
|
+
= history_for(@component, action: :edit, resource: resource)
|
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
%span.icon-bar
|
|
7
7
|
%span.icon-bar
|
|
8
8
|
|
|
9
|
+
= render_breadcrumbs
|
|
10
|
+
|
|
9
11
|
%ul.nav.navbar-nav.pull-right
|
|
10
12
|
%li
|
|
11
13
|
= link_to root_path, target: "_blank", class: 'hint--bottom-left btn-home btn-shadow', aria: { label: t('para.admin.back_to_app') } do
|
|
12
14
|
%i.fa.fa-home
|
|
13
|
-
|
|
15
|
+
|
|
14
16
|
%li
|
|
15
17
|
= link_to destroy_admin_user_session_path, method: :delete, class: 'hint--bottom-left btn-power-off btn-shadow', aria: { label: t('para.admin.sign_out') } do
|
|
16
18
|
%i.fa.fa-sign-out
|
|
17
|
-
|
|
18
|
-
= render_breadcrumbs
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
.navmenu.navmenu-default.navmenu-fixed-left.offcanvas-sm{ class: ('dark-theme' if Para.config.dark_theme) }
|
|
1
|
+
.navmenu.navmenu-default.navmenu-fixed-left.offcanvas-sm{ class: ('dark-theme' if Para.config.dark_theme), data: { :'admin-left-sidebar' => true } }
|
|
2
2
|
= link_to admin_path, class: 'navmenu-brand' do
|
|
3
3
|
= t('para.admin.brand').html_safe
|
|
4
4
|
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
.form-tabs{ data: { :'form-tabs' => true } }
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
.form-tabs{ data: { :'form-tabs' => true, :'top-level-tabs' => !tabs_manager.builder.nested? } }
|
|
2
|
+
-# The .nav-tabs-wrapper is used to maintain the tabs height when the tabs
|
|
3
|
+
-# are fixed to top
|
|
4
|
+
.nav-tabs-wrapper{ data: { :'nav-tabs-wrapper' => true } }
|
|
5
|
+
%ul.nav.nav-tabs.affix-no-mobile{ role: 'tablist', data: { :'tabs-nav-affix' => tabs_manager.affix? } }
|
|
6
|
+
- tabs.each do |tab|
|
|
7
|
+
%li{ class: ('active' if tab.active?), role: 'presentation' }
|
|
8
|
+
= link_to "##{ tab.dom_id }", role: 'tab', aria: { controls: 'settings' }, data: { toggle: 'tab' } do
|
|
9
|
+
%i.fa{ class: "fa-#{ tab.icon }" }
|
|
10
|
+
= tab.title
|
|
8
11
|
|
|
9
12
|
.tab-content
|
|
10
|
-
- tabs.
|
|
11
|
-
.tab-pane{ id: tab.dom_id, class: ('active' if
|
|
13
|
+
- tabs.each do |tab|
|
|
14
|
+
.tab-pane{ id: tab.dom_id, class: ('active' if tab.active?), role: 'tabpanel' }
|
|
12
15
|
= tab.content
|
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
.row
|
|
5
5
|
.col-md-6
|
|
6
6
|
.input-group.filter-form
|
|
7
|
-
=
|
|
7
|
+
= text_field :q, search_param, class: 'string required form-control', autocomplete: 'off', data: { :'search-field' => true, :'search-field-input' => true }
|
|
8
|
+
|
|
9
|
+
-# Scope search results to the provided collection
|
|
10
|
+
= hidden_field_tag :'q[id_in]', collection.map(&:id).join(','), data: { :'search-field-input' => true } if collection
|
|
11
|
+
|
|
8
12
|
%span.input-group-btn
|
|
9
13
|
= button_tag type: 'button', class: 'btn' do
|
|
10
14
|
= fa_icon 'search'
|
|
@@ -20,7 +24,7 @@
|
|
|
20
24
|
= t('para.form.multi_select.available.add_all')
|
|
21
25
|
|
|
22
26
|
.panel-body
|
|
23
|
-
%table.table{
|
|
27
|
+
%table.table{ :'data-available-items' => true, :'data-no-available-items' => render(partial: 'para/inputs/multi_select/no_items', locals: { available: true }) }
|
|
24
28
|
%tbody
|
|
25
29
|
= render partial: 'para/admin/search/result', collection: option_resources
|
|
26
30
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
.nested-many-field{ class: [('orderable' if orderable), ('nested-many-field-inset' if inset)] }
|
|
2
2
|
.fields-list{ id: dom_identifier }
|
|
3
|
-
= form.simple_fields_for attribute_name, resources, nested_attribute_name: attribute_name, orderable: orderable do |nested_form|
|
|
4
|
-
= render partial: find_partial_for(model, 'nested_many/container', partial_dir: 'inputs'), locals: { form: nested_form, model: nested_form.object.class, subclass: subclass, nested_locals: nested_locals, inset: inset }
|
|
3
|
+
= form.simple_fields_for attribute_name, resources, nested_attribute_name: attribute_name, orderable: orderable, track_attribute_mappings: render_partial do |nested_form|
|
|
4
|
+
= render partial: find_partial_for(model, 'nested_many/container', partial_dir: 'inputs'), locals: { form: nested_form, model: nested_form.object.class, subclass: subclass, nested_locals: nested_locals, inset: inset, render_partial: render_partial, remote_partial_params: remote_partial_params }
|
|
5
5
|
-# Add button
|
|
6
6
|
- if add_button
|
|
7
7
|
- if subclass
|
|
8
|
-
= render partial: 'para/inputs/nested_many/add_with_subclasses', locals: { form: form, model: model, dom_identifier: dom_identifier, nested_locals: nested_locals, attribute_name: attribute_name, orderable: orderable, subclasses: subclasses, inset: inset }
|
|
8
|
+
= render partial: 'para/inputs/nested_many/add_with_subclasses', locals: { form: form, model: model, dom_identifier: dom_identifier, nested_locals: nested_locals, attribute_name: attribute_name, orderable: orderable, subclasses: subclasses, inset: inset, add_button_label: add_button_label, add_button_class: add_button_class, render_partial: render_partial, remote_partial_params: remote_partial_params }
|
|
9
9
|
- else
|
|
10
|
-
= render partial: 'para/inputs/nested_many/add', locals: { form: form, model: model, dom_identifier: dom_identifier, nested_locals: nested_locals, attribute_name: attribute_name, orderable: orderable, inset: inset }
|
|
10
|
+
= render partial: 'para/inputs/nested_many/add', locals: { form: form, model: model, dom_identifier: dom_identifier, nested_locals: nested_locals, attribute_name: attribute_name, orderable: orderable, inset: inset, add_button_label: add_button_label, add_button_class: add_button_class, render_partial: render_partial, remote_partial_params: remote_partial_params }
|
|
@@ -1,3 +1,14 @@
|
|
|
1
1
|
.nested-one-field
|
|
2
2
|
= form.simple_fields_for attribute_name, resource, nested_attribute_name: attribute_name do |nested_form|
|
|
3
|
-
|
|
3
|
+
- if defined?(collapsible) && collapsible
|
|
4
|
+
.panel.panel-default.form-fields
|
|
5
|
+
.panel-heading
|
|
6
|
+
%h3.panel-title
|
|
7
|
+
= link_to "##{ nested_form.nested_resource_dom_id }", class: "nested-input-title collapsed#{ (' has-error' if nested_form.object.errors.any?) }", data: { toggle: 'collapse', :'open-on-insert' => true } do
|
|
8
|
+
= nested_form.nested_resource_name
|
|
9
|
+
%i.fa.fa-angle-up
|
|
10
|
+
|
|
11
|
+
.panel-body.panel-collapse.form-inputs.collapse{ id: nested_form.nested_resource_dom_id }
|
|
12
|
+
= render partial: find_partial_for(model, :fields), locals: { form: nested_form, parent: form.object }.merge(nested_locals)
|
|
13
|
+
- else
|
|
14
|
+
= render partial: find_partial_for(model, :fields), locals: { form: nested_form, parent: form.object }.merge(nested_locals)
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
- if available
|
|
1
|
+
- if available
|
|
2
2
|
%tr
|
|
3
|
-
%td
|
|
4
|
-
|
|
3
|
+
%td
|
|
4
|
+
= t('para.form.multi_select.available.no_items')
|
|
5
|
+
|
|
6
|
+
- else
|
|
5
7
|
%tr
|
|
6
|
-
%td
|
|
8
|
+
%td
|
|
9
|
+
= t('para.form.multi_select.selected.no_items')
|
|
10
|
+
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
= link_to_add_association form, attribute_name, partial: find_partial_for(model, 'nested_many/container', partial_dir: 'inputs'), form_name: 'form', class:
|
|
2
|
-
|
|
1
|
+
= link_to_add_association form, attribute_name, partial: find_partial_for(model, 'nested_many/container', partial_dir: 'inputs'), form_name: 'form', class: "btn btn-shadow add-button nested-many-inset-add-button #{add_button_class}", data: { :'association-insertion-node' => "##{ dom_identifier }", :'association-insertion-method' => 'append' }, render_options: { nested_attribute_name: attribute_name, orderable: orderable, locals: { model: model, nested_locals: nested_locals, inset: inset, render_partial: render_partial, remote_partial_params: remote_partial_params } } do
|
|
2
|
+
= add_button_label
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
.add-button.nested-many-inset-add-button.dropdown
|
|
2
|
-
%button.btn.btn-shadow.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown' } }
|
|
3
|
-
=
|
|
2
|
+
%button.btn.btn-shadow.dropdown-toggle{ class: add_button_class, type: 'button', data: { toggle: 'dropdown' } }
|
|
3
|
+
= add_button_label
|
|
4
4
|
%i.fa.fa-angle-down
|
|
5
5
|
%ul.dropdown-menu
|
|
6
6
|
- subclasses.each do |submodel|
|
|
7
7
|
%li
|
|
8
|
-
= link_to_add_association form, attribute_name, wrap_object: proc { submodel.new }, partial: find_partial_for(submodel, 'nested_many/container', partial_dir: 'inputs'), form_name: 'form', class: 'dropdown-link', data: { :'association-insertion-node' => "##{ dom_identifier }", :'association-insertion-method' => 'append' }, render_options: { nested_attribute_name: attribute_name, orderable: orderable, locals: { model: submodel, nested_locals: nested_locals, inset: inset } } do
|
|
8
|
+
= link_to_add_association form, attribute_name, wrap_object: proc { submodel.new }, partial: find_partial_for(submodel, 'nested_many/container', partial_dir: 'inputs'), form_name: 'form', class: 'dropdown-link', data: { :'association-insertion-node' => "##{ dom_identifier }", :'association-insertion-method' => 'append' }, render_options: { nested_attribute_name: attribute_name, orderable: orderable, locals: { model: submodel, nested_locals: nested_locals, inset: inset, render_partial: render_partial, remote_partial_params: remote_partial_params } } do
|
|
9
9
|
= submodel.model_name.human
|
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
- nested_locals ||= {}
|
|
2
2
|
|
|
3
|
-
.panel.panel-default.form-fields{ class: ('nested-many-inset-panel' if inset), data: { :
|
|
3
|
+
.panel.panel-default.form-fields{ class: ('nested-many-inset-panel' if inset), data: { persisted: form.object.persisted? } }
|
|
4
4
|
.panel-heading
|
|
5
5
|
= form.reorder_anchor
|
|
6
6
|
|
|
7
7
|
%h3.panel-title
|
|
8
|
-
= link_to "##{ form.nested_resource_dom_id }", class:
|
|
8
|
+
= link_to "##{ form.nested_resource_dom_id }", class: "nested-input-title collapsed#{ (' has-error' if form.object.errors.any?) }", data: { toggle: 'collapse', :'open-on-insert' => true } do
|
|
9
9
|
= form.nested_resource_name
|
|
10
10
|
%i.fa.fa-angle-up
|
|
11
11
|
|
|
12
12
|
= form.remove_association_button
|
|
13
13
|
|
|
14
|
-
.panel-collapse.form-inputs.collapse{ id: form.nested_resource_dom_id, class: ('in' if inset) }
|
|
15
|
-
.panel-body
|
|
16
|
-
|
|
14
|
+
.panel-collapse.form-inputs.collapse{ id: form.nested_resource_dom_id, class: ('in' if inset && form.object.persisted?), data: { rendered: render_partial, render_path: @component.path(remote_partial_params), id: form.object.id, :"object-name" => form.object_name, :"model-name" => model.name } }
|
|
15
|
+
.panel-body{ data: { :"nested-form-container" => true } }
|
|
16
|
+
- if render_partial
|
|
17
|
+
= render partial: find_partial_for(model, :fields), locals: { form: form }.merge(nested_locals)
|
|
18
|
+
- else
|
|
19
|
+
= fa_icon "spinner spin"
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
-# Add inheritance column field if needed
|
|
22
|
+
= form.hidden_field form.object.class.inheritance_column if form.object.class.column_names.include?(form.object.class.inheritance_column)
|
|
20
23
|
|
|
21
24
|
.clearfix
|
|
@@ -27,6 +27,30 @@ class <%= exporter_class_name %> < Para::Exporter::Csv
|
|
|
27
27
|
# def row_for(resource)
|
|
28
28
|
# end
|
|
29
29
|
|
|
30
|
+
# Whitelist params to be fetched from the controller and passed down to the
|
|
31
|
+
# exporter.
|
|
32
|
+
#
|
|
33
|
+
# For example, if you want to export posts for a given category, you
|
|
34
|
+
# can add the `:category_id` param to your export link, and whitelist
|
|
35
|
+
# this param here with :
|
|
36
|
+
#
|
|
37
|
+
# def self.params_whitelist
|
|
38
|
+
# [:category_id]
|
|
39
|
+
# end
|
|
40
|
+
#
|
|
41
|
+
# It will be passed from the controller to the importer so it can be used
|
|
42
|
+
# to scope resources before exporting.
|
|
43
|
+
#
|
|
44
|
+
# Note that you'll manually need to scope the resources by overriding the
|
|
45
|
+
# #resources method.
|
|
46
|
+
#
|
|
47
|
+
# If you need automatic scoping, please use the `:q` param that accepts
|
|
48
|
+
# ransack search params and applies it to the resources.
|
|
49
|
+
#
|
|
50
|
+
# def self.params_whitelist
|
|
51
|
+
# []
|
|
52
|
+
# end
|
|
53
|
+
|
|
30
54
|
# If you need complete control over you CSV generation, use the following
|
|
31
55
|
# method instead of the #fields or #row_for methods, and return a valid CSV
|
|
32
56
|
# string
|
|
@@ -27,6 +27,30 @@ class <%= exporter_class_name %> < Para::Exporter::Xls
|
|
|
27
27
|
# def row_for(resource)
|
|
28
28
|
# end
|
|
29
29
|
|
|
30
|
+
# Whitelist params to be fetched from the controller and passed down to the
|
|
31
|
+
# exporter.
|
|
32
|
+
#
|
|
33
|
+
# For example, if you want to export posts for a given category, you
|
|
34
|
+
# can add the `:category_id` param to your export link, and whitelist
|
|
35
|
+
# this param here with :
|
|
36
|
+
#
|
|
37
|
+
# def self.params_whitelist
|
|
38
|
+
# [:category_id]
|
|
39
|
+
# end
|
|
40
|
+
#
|
|
41
|
+
# It will be passed from the controller to the importer so it can be used
|
|
42
|
+
# to scope resources before exporting.
|
|
43
|
+
#
|
|
44
|
+
# Note that you'll manually need to scope the resources by overriding the
|
|
45
|
+
# #resources method.
|
|
46
|
+
#
|
|
47
|
+
# If you need automatic scoping, please use the `:q` param that accepts
|
|
48
|
+
# ransack search params and applies it to the resources.
|
|
49
|
+
#
|
|
50
|
+
# def self.params_whitelist
|
|
51
|
+
# []
|
|
52
|
+
# end
|
|
53
|
+
|
|
30
54
|
# If you need complete control over you XLS generation, use the following
|
|
31
55
|
# method instead of the #fields or #row_for methods, and return a valid XLS
|
|
32
56
|
# StringIO object.
|
|
@@ -20,7 +20,10 @@ module Para
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def parse_input(params, resource)
|
|
23
|
-
if
|
|
23
|
+
if reflection &&
|
|
24
|
+
(id = params[reflection.foreign_key].presence) &&
|
|
25
|
+
!reflection.klass.exists?(id: id)
|
|
26
|
+
then
|
|
24
27
|
on_the_fly_creation(id) do |resource|
|
|
25
28
|
params[reflection.foreign_key] = resource.id
|
|
26
29
|
end
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
module Para
|
|
2
2
|
module AttributeField
|
|
3
3
|
module NestedField
|
|
4
|
-
def nested_model_mappings(nested_attributes)
|
|
5
|
-
model = if
|
|
4
|
+
def nested_model_mappings(nested_attributes, resource)
|
|
5
|
+
model = if resource
|
|
6
|
+
resource.class
|
|
7
|
+
elsif (type = nested_attributes[:type]).present?
|
|
6
8
|
nested_attributes[:type].try(:constantize)
|
|
7
9
|
else
|
|
8
10
|
reflection.klass
|
|
@@ -9,8 +9,8 @@ module Para
|
|
|
9
9
|
def parse_input(params, resource)
|
|
10
10
|
if (nested_attributes = params[nested_attributes_key])
|
|
11
11
|
nested_attributes.each do |index, attributes|
|
|
12
|
-
mappings = nested_model_mappings(attributes)
|
|
13
12
|
nested_resource = fetch_or_build_nested_resource_for(resource, index, attributes)
|
|
13
|
+
mappings = nested_model_mappings(attributes, nested_resource)
|
|
14
14
|
|
|
15
15
|
mappings.fields.each do |field|
|
|
16
16
|
field.parse_input(attributes, nested_resource)
|
|
@@ -8,8 +8,8 @@ module Para
|
|
|
8
8
|
|
|
9
9
|
def parse_input(params, resource)
|
|
10
10
|
if (nested_attributes = params[nested_attributes_key])
|
|
11
|
-
mappings = nested_model_mappings(nested_attributes)
|
|
12
11
|
nested_resource = fetch_or_build_nested_resource_for(resource, nested_attributes)
|
|
12
|
+
mappings = nested_model_mappings(nested_attributes, nested_resource)
|
|
13
13
|
|
|
14
14
|
mappings.fields.each do |field|
|
|
15
15
|
field.parse_input(nested_attributes, nested_resource)
|
|
@@ -36,7 +36,20 @@ module Para
|
|
|
36
36
|
|
|
37
37
|
item.value = entry.value
|
|
38
38
|
item.expires_at = options[:expires_in].try(:since)
|
|
39
|
-
|
|
39
|
+
|
|
40
|
+
# Save chace item in its own thread, to get a clean
|
|
41
|
+
# ActiveRecord::Base.connection.
|
|
42
|
+
#
|
|
43
|
+
# It allows cache to be saved inside transactions but without waiting
|
|
44
|
+
# transactions to be committed to commit chache changes.
|
|
45
|
+
#
|
|
46
|
+
# This is needed for checking progress of long running tasks, like
|
|
47
|
+
# imports, that use the cache inside transactions but need other
|
|
48
|
+
# processes to get their state in real time.
|
|
49
|
+
#
|
|
50
|
+
Thread.new do
|
|
51
|
+
item.save!
|
|
52
|
+
end.join
|
|
40
53
|
|
|
41
54
|
# Ensure cached item in RequestStore is up to date
|
|
42
55
|
RequestStore.store[cache_key_for(key)] = item
|