para 0.8.8 → 0.8.13

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: f7e6c8c33108e3898e0d032ff8c5e1910e4cffa4
4
- data.tar.gz: 21447be4a3cdae224fb4bce7b7632e6e2e8f42c4
2
+ SHA256:
3
+ metadata.gz: 162c6a81b2f57ab3565758832cacec3a10442bd52a520870d73c56745098914b
4
+ data.tar.gz: 73b84d05ccfd02ce566c90925edba4f4123cb5ed38cbc193e47c7e8dde5c2dee
5
5
  SHA512:
6
- metadata.gz: 99823f9bdf05353edbaea1dbb39fcc9b3dc7784aafa6c748333de2cd6659f41d7f1ffb42d847a1aa37f5ddecca5b8e1ca99590c06289e767fefedcba28196c01
7
- data.tar.gz: '0089801d66360f8a7aff0d6527a36d067c10e87d0ec8f5718bb7a4a0cdd8dae43c3425a6601d346062734aef8a11f50ae2753dc747e9d10bcdea86a9a208a07c'
6
+ metadata.gz: 3a23e25fab98168e0baeae6c5ef9623f580cb0a1ad4c2f6a7c7f78c2ed0f7ff0d35b5639c14e2deda65312f57f3fb9f6d2bf5e2dd1bd862d2e9a1db0a638b865
7
+ data.tar.gz: 42acaacd0c3dfeeee9af36a334229d8c1d9a36762fea88a57cb3dd4725296906347f348bc8916c857524ac2bd1c64fa5d7eb133e46aa5c146925ac1d0deb7ddd
@@ -9,7 +9,11 @@
9
9
  .actions-control
10
10
  list-style: none
11
11
  padding: 0
12
- margin: 0
12
+ margin: 0 15px 0 0
13
+
14
+ &:last-child
15
+ margin-right: 0
16
+
13
17
  li
14
18
  +inline-block
15
19
  padding: 0 5px
@@ -42,7 +46,7 @@
42
46
 
43
47
  .page-list-heading
44
48
  border-bottom: 1px solid $gray-light
45
-
49
+
46
50
  .page-list-body:last-child
47
51
  padding-bottom: 0
48
52
 
@@ -76,7 +80,7 @@
76
80
  color: $gray
77
81
  border-color: transparent
78
82
  &:hover
79
- color: $gray-dark
83
+ color: $gray-dark
80
84
 
81
85
 
82
86
  .page-entries-info
@@ -0,0 +1,17 @@
1
+ module Para
2
+ module Admin
3
+ module NestedInputsHelper
4
+ # Helper that allows filling a parent association for a given resource, based on the
5
+ # inverse_of option of the parent resource association.
6
+ #
7
+ def with_inverse_association_for(resource, attribute_name, parent_resource)
8
+ resource.tap do
9
+ association_name = parent_resource.association(attribute_name).options[:inverse_of]
10
+ return resource unless association_name
11
+
12
+ resource.association(association_name).replace(parent_resource)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -2,18 +2,25 @@ module Para
2
2
  module Admin
3
3
  module PageHelper
4
4
  def page_top_bar(options = {})
5
- content_tag(:div, class: 'page-title row') do
5
+ top_bar = content_tag(:div, class: 'page-title row') do
6
6
  content_tag(:h1, options[:title]) +
7
7
 
8
8
  if (actions = actions_for(options[:type]))
9
9
  actions.map(&method(:build_action)).join('').html_safe
10
10
  end
11
11
  end
12
+
13
+ # Return both top bar and component navigation to be displayed at the top of the
14
+ # page.
15
+ top_bar + component_navigation
12
16
  end
13
17
 
14
18
  def build_action(action)
15
- content_tag(:div, class: 'actions-control pull-right') do
16
- link_to(action[:url], class: 'btn btn-default btn-shadow') do
19
+ link_options = action.fetch(:link_options, {})
20
+ link_options[:class] ||= "btn btn-default btn-shadow"
21
+
22
+ content_tag(:div, class: 'actions-control pull-right') do
23
+ link_to(action[:url], link_options) do
17
24
  (
18
25
  (fa_icon(action[:icon], class: 'fa-fw') if action[:icon]) +
19
26
  action[:label]
@@ -27,6 +34,26 @@ module Para
27
34
  instance_eval(&action)
28
35
  end.compact
29
36
  end
37
+
38
+ def component_navigation
39
+ parent_component = @component && (
40
+ @component.parent_component ||
41
+ @component.child_components.any? && @component
42
+ )
43
+
44
+ return unless parent_component
45
+
46
+ # If the component has a `model_type` option, therefore, an associated model,
47
+ # we try to render the partial from the relative path of the model, else we
48
+ # use the component class as the base target path
49
+ partial_target = parent_component.try(:model_type) || parent_component
50
+
51
+ render partial: find_partial_for(partial_target, :navigation),
52
+ locals: {
53
+ parent_component: parent_component,
54
+ active_component: @component
55
+ }
56
+ end
30
57
  end
31
58
  end
32
59
  end
@@ -1,3 +1,6 @@
1
+ # Base class used for the `AdminUser` model class as parent but automatically overriden
2
+ # by application's own ApplicationRecord definition in Rails 5+
3
+ #
1
4
  class ApplicationRecord < ActiveRecord::Base
2
5
  self.abstract_class = true
3
6
  end
@@ -0,0 +1,20 @@
1
+ module Para
2
+ # Base class for all para-specific models.
3
+ #
4
+ class ApplicationRecord < ActiveRecord::Base
5
+ self.abstract_class = true
6
+
7
+ private
8
+
9
+ # Adds the `optional: true` option to the belongs_to calls inside the provided block,
10
+ # but only for Rails 5.1+
11
+ #
12
+ def self.with_belongs_to_optional_option_if_needed(&block)
13
+ if ActiveRecord::Associations::Builder::BelongsTo.valid_options({}).include?(:optional)
14
+ with_options(optional: true, &block)
15
+ else
16
+ block.call
17
+ end
18
+ end
19
+ end
20
+ end
@@ -3,7 +3,7 @@
3
3
  #
4
4
  module Para
5
5
  module Cache
6
- class Item < ActiveRecord::Base
6
+ class Item < Para::ApplicationRecord
7
7
  def value
8
8
  Marshal.load(::Base64.decode64(self[:value])) if self[:value].present?
9
9
  end
@@ -1,6 +1,6 @@
1
1
  module Para
2
2
  module Component
3
- class Base < ActiveRecord::Base
3
+ class Base < Para::ApplicationRecord
4
4
  self.table_name = 'para_components'
5
5
 
6
6
  class_attribute :component_name
@@ -16,7 +16,14 @@ module Para
16
16
 
17
17
  configurable_on :controller
18
18
 
19
- belongs_to :component_section, class_name: 'Para::ComponentSection'
19
+ with_belongs_to_optional_option_if_needed do
20
+ belongs_to :component_section, class_name: 'Para::ComponentSection'
21
+ belongs_to :parent_component, class_name: 'Para::Component::Base'
22
+ end
23
+
24
+ has_many :child_components, -> { ordered },
25
+ class_name: 'Para::Component::Base',
26
+ foreign_key: 'parent_component_id'
20
27
 
21
28
  validates :identifier, :type, presence: true
22
29
 
@@ -31,6 +38,13 @@ module Para
31
38
  )
32
39
  end
33
40
 
41
+ def main_navigation_name
42
+ ::I18n.t(
43
+ "components.main_navigation.#{ identifier }",
44
+ default: name
45
+ )
46
+ end
47
+
34
48
  def exportable?
35
49
  false
36
50
  end
@@ -1,5 +1,5 @@
1
1
  module Para
2
- class ComponentResource < ActiveRecord::Base
2
+ class ComponentResource < Para::ApplicationRecord
3
3
  belongs_to :component, class_name: 'Para::Component::Base'
4
4
  belongs_to :resource, polymorphic: true
5
5
  end
@@ -1,5 +1,5 @@
1
1
  module Para
2
- class ComponentSection < ActiveRecord::Base
2
+ class ComponentSection < Para::ApplicationRecord
3
3
  has_many :components, -> { ordered }, class_name: 'Para::Component::Base',
4
4
  autosave: true, foreign_key: :component_section_id,
5
5
  dependent: :destroy
@@ -1,6 +1,6 @@
1
1
  module Para
2
2
  module Library
3
- class File < ActiveRecord::Base
3
+ class File < Para::ApplicationRecord
4
4
  if defined?(ActiveStorage)
5
5
  has_one_attached :attachment
6
6
 
@@ -1,21 +1,14 @@
1
1
  module Para
2
2
  module Page
3
- class Section < ActiveRecord::Base
3
+ class Section < Para::ApplicationRecord
4
4
  self.table_name = 'para_page_sections'
5
5
 
6
6
  acts_as_orderable parent: :page, as: :sections
7
7
 
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
8
+ with_belongs_to_optional_option_if_needed do
9
+ belongs_to :page, polymorphic: true
15
10
  end
16
11
 
17
- belongs_to :page, page_relation_options
18
-
19
12
  def css_class
20
13
  @css_class ||= self.class.name.demodulize.underscore.gsub(/_/, '-')
21
14
  end
@@ -1,6 +1,6 @@
1
1
  module Para
2
2
  module Page
3
- class SectionResource < ActiveRecord::Base
3
+ class SectionResource < Para::ApplicationRecord
4
4
  self.table_name = 'para_page_section_resources'
5
5
 
6
6
  acts_as_orderable parent: :section, as: :section_resources
@@ -0,0 +1,10 @@
1
+ .top-nav-tabs-affix-placeholder
2
+ %ul.top-nav-tabs-affix.nav.nav-tabs{ role: "tablist", "data-top-level-affix": true }
3
+ %li{ class: ("active" if active_component == parent_component) }
4
+ = link_to parent_component.path do
5
+ = parent_component.name
6
+
7
+ - parent_component.child_components.each do |child_component|
8
+ %li{ class: ("active" if active_component == child_component) }
9
+ = link_to child_component.path do
10
+ = child_component.name
@@ -16,7 +16,7 @@
16
16
  - component_section.components.each do |component|
17
17
  - if can?(:manage, component) && show_component?(component)
18
18
  %li.component-item{ class: (@component == component) && 'active' }
19
- = link_to component.name, component.path
19
+ = link_to component.main_navigation_name, component.path
20
20
 
21
21
  - else
22
22
  %li.component-section-item
@@ -5,5 +5,5 @@
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, uncollapsed: uncollapsed, render_partial: render_partial, remote_partial_params: remote_partial_params } } do
8
+ = link_to_add_association form, attribute_name, wrap_object: proc { with_inverse_association_for(submodel.new, attribute_name, form.object) }, 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, uncollapsed: uncollapsed, render_partial: render_partial, remote_partial_params: remote_partial_params } } do
9
9
  = submodel.model_name.human
@@ -6,5 +6,5 @@
6
6
  %ul.dropdown-menu
7
7
  - subclasses.each do |submodel|
8
8
  %li
9
- = link_to_add_association form, attribute_name, wrap_object: proc { submodel.new }, partial: find_partial_for(submodel, 'nested_one/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, locals: { form: form, model: submodel, subclass: subclass, nested_locals: nested_locals } } do
9
+ = link_to_add_association form, attribute_name, wrap_object: proc { with_inverse_association_for(submodel.new, attribute_name, form.object) }, partial: find_partial_for(submodel, 'nested_one/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, locals: { form: form, model: submodel, subclass: subclass, nested_locals: nested_locals } } do
10
10
  = submodel.model_name.human
@@ -13,14 +13,14 @@ en:
13
13
  error: "Could not delete %{model}"
14
14
  clone:
15
15
  success: "%{model} cloned"
16
- error: "Could not clone %{model}"
17
-
16
+ error: "Could not clone %{model}"
17
+
18
18
  jobs:
19
19
  para/importer/base:
20
20
  progressing: "The file is being imported, please wait a few moments ..."
21
21
  success: "The import of the file was successfully completed"
22
22
  success_with_errors: |
23
- The import of the file was done, but some lines
23
+ The import of the file was done, but some lines
24
24
  were not taken into account due to errors :
25
25
  other_errors: "<br>And <b>%{count}</b> others errors ..."
26
26
  error: "The selected file contains errors and could not be imported"
@@ -40,9 +40,6 @@ en:
40
40
  confirmation:
41
41
  shared:
42
42
  destroy: 'Are you sure that you want to delete %{model} ?'
43
-
44
- component:
45
- none_created: "No components yet ..."
46
43
 
47
44
  component:
48
45
  create: "Create a component"
@@ -146,7 +143,7 @@ en:
146
143
  type: "Type"
147
144
  model_type: "Resource"
148
145
  namespaced: "Only list component's resources"
149
-
146
+
150
147
  activemodel:
151
148
  models:
152
149
  settings_rails/form:
@@ -0,0 +1,6 @@
1
+ class AddParentComponentToParaComponents < ActiveRecord::Migration[5.0]
2
+ def change
3
+ add_reference :para_components, :parent_component, foreign_key: false
4
+ add_foreign_key :para_components, :para_components, column: :parent_component_id
5
+ end
6
+ end
@@ -1,7 +1,7 @@
1
1
  module Para
2
2
  class ComponentsConfiguration
3
- class UndefinedComponentTypeError < StandardError
4
- end
3
+ class UndefinedComponentTypeError < StandardError; end
4
+ class ComponentTooDeepError < StandardError; end
5
5
 
6
6
  def draw(&block)
7
7
  return unless components_installed?
@@ -48,7 +48,24 @@ module Para
48
48
  end
49
49
 
50
50
  def component_configuration_for(identifier)
51
- sections.map(&:components).flatten.find { |c| c.identifier.to_s == identifier.to_s }
51
+ sections.each do |section|
52
+ section.components.each do |component|
53
+ # If one of the section component has the searched identifier return it
54
+ if component.identifier.to_s == identifier.to_s
55
+ return component
56
+ else
57
+ component.child_components.each do |child_component|
58
+ # If one of the component children has the searched identifier return it
59
+ if child_component.identifier.to_s == identifier.to_s
60
+ return child_component
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ # Return nil if the identifier was not found
68
+ nil
52
69
  end
53
70
 
54
71
  private
@@ -60,6 +77,10 @@ module Para
60
77
 
61
78
  section.components.each do |component|
62
79
  components_ids_hash[component.identifier] = component.model.id
80
+
81
+ component.child_components.each do |child_component|
82
+ components_ids_hash[child_component.identifier] = child_component.model.id
83
+ end
63
84
  end
64
85
  end
65
86
  end
@@ -129,8 +150,8 @@ module Para
129
150
  instance_eval(&block)
130
151
  end
131
152
 
132
- def component(*args)
133
- components << Component.new(*args)
153
+ def component(*args, &block)
154
+ components << Component.new(*args, &block)
134
155
  end
135
156
 
136
157
  def components
@@ -149,13 +170,17 @@ module Para
149
170
  end
150
171
 
151
172
  class Component
152
- attr_accessor :identifier, :type, :shown_if, :options, :model
173
+ attr_accessor :identifier, :type, :shown_if, :options, :model, :parent
153
174
 
154
- def initialize(identifier, type_identifier, shown_if: nil, **options)
155
- self.identifier = identifier.to_s
156
- self.type = Para::Component.registered_components[type_identifier]
157
- self.options = options
158
- self.shown_if = shown_if
175
+ def initialize(identifier, type_identifier, shown_if: nil, **options, &block)
176
+ @identifier = identifier.to_s
177
+ @type = Para::Component.registered_components[type_identifier]
178
+ @options = options
179
+ @shown_if = shown_if
180
+ @parent = options.delete(:parent)
181
+
182
+ # Build child components if a block is provided
183
+ instance_eval(&block) if block
159
184
 
160
185
  unless type
161
186
  raise UndefinedComponentTypeError.new(
@@ -165,10 +190,29 @@ module Para
165
190
  end
166
191
  end
167
192
 
193
+ def component(*args, **child_options, &block)
194
+ # Do not allow nesting components more than one level as the display of illimited
195
+ # child nesting deepness is not implemented
196
+ if parent
197
+ raise ComponentTooDeepError, "Cannot nest components more than one level"
198
+ end
199
+
200
+ child_component_options = child_options.merge(parent: self)
201
+ child_components << Component.new(*args, **child_component_options, &block)
202
+ end
203
+
204
+ def child_components
205
+ @child_components ||= []
206
+ end
207
+
168
208
  def refresh(attributes = {})
169
- self.model = type.where(identifier: identifier).first_or_initialize
209
+ @model = type.where(identifier: identifier).first_or_initialize
170
210
  model.update_with(attributes.merge(options_with_defaults))
171
211
  model.save!
212
+
213
+ child_components.each_with_index do |child_component, child_index|
214
+ child_component.refresh(component_section: nil, position: child_index)
215
+ end
172
216
  end
173
217
 
174
218
  # Ensures unset :configuration store options are set to nil to allow
@@ -179,7 +223,14 @@ module Para
179
223
  configurable_keys += options.keys
180
224
  configurable_keys.uniq!
181
225
 
182
- configurable_keys.each_with_object({}) do |key, hash|
226
+ options_with_defaults = {}
227
+
228
+ # Assign parent component resource to the final attribute options, assigning nil
229
+ # if the `:parent` option is empty, to allow extracting a component from its
230
+ # parent by just moving the component call outside of its parent block.
231
+ options_with_defaults[:parent_component] = parent&.model
232
+
233
+ configurable_keys.each_with_object(options_with_defaults) do |key, hash|
183
234
  hash[key] = options[key]
184
235
  end
185
236
  end
@@ -33,9 +33,9 @@ module Para
33
33
  end
34
34
 
35
35
  def ensure_total_progress
36
- return if @total_progress
36
+ return if @_total_progress
37
37
 
38
- @total_progress ||= if respond_to?(:total_progress, true)
38
+ @_total_progress ||= if respond_to?(:total_progress, true)
39
39
  progress.total = total_progress
40
40
  else
41
41
  progress[:total]
@@ -92,9 +92,9 @@ module Para
92
92
  end
93
93
 
94
94
  content_tag(:th, options) do
95
- if (sort = options.delete(:sort))
95
+ if search && (sort = options.delete(:sort))
96
96
  view.sort_link(search, *sort, label, hide_indicator: true)
97
- elsif searchable?(field_name)
97
+ elsif search && searchable?(field_name)
98
98
  view.sort_link(search, field_name, label, hide_indicator: true)
99
99
  else
100
100
  label
@@ -1,3 +1,3 @@
1
1
  module Para
2
- VERSION = '0.8.8'
2
+ VERSION = '0.8.13'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: para
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.8
4
+ version: 0.8.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Valentin Ballestrino
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-20 00:00:00.000000000 Z
11
+ date: 2020-12-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -577,6 +577,7 @@ files:
577
577
  - app/helpers/para/admin/components_helper.rb
578
578
  - app/helpers/para/admin/decorators_helper.rb
579
579
  - app/helpers/para/admin/history_helper.rb
580
+ - app/helpers/para/admin/nested_inputs_helper.rb
580
581
  - app/helpers/para/admin/page_helper.rb
581
582
  - app/helpers/para/admin/pagination_helper.rb
582
583
  - app/helpers/para/admin/resources_helper.rb
@@ -594,6 +595,7 @@ files:
594
595
  - app/helpers/para/tree_helper.rb
595
596
  - app/models/application_record.rb
596
597
  - app/models/para/ability.rb
598
+ - app/models/para/application_record.rb
597
599
  - app/models/para/cache/item.rb
598
600
  - app/models/para/component/base.rb
599
601
  - app/models/para/component/crud.rb
@@ -628,6 +630,7 @@ files:
628
630
  - app/views/para/admin/resources/_form.html.haml
629
631
  - app/views/para/admin/resources/_imports_menu.html.haml
630
632
  - app/views/para/admin/resources/_list.html.haml
633
+ - app/views/para/admin/resources/_navigation.html.haml
631
634
  - app/views/para/admin/resources/_per_page_select.html.haml
632
635
  - app/views/para/admin/resources/_remote_nested_form.html.haml
633
636
  - app/views/para/admin/resources/_subclassable_add_button.html.haml
@@ -669,6 +672,7 @@ files:
669
672
  - db/migrate/20160905134106_create_para_library_files.rb
670
673
  - db/migrate/20161006105728_create_para_cache_items.rb
671
674
  - db/migrate/20170324125547_create_para_page_section_resources.rb
675
+ - db/migrate/20201210152223_add_parent_component_to_para_components.rb
672
676
  - lib/generators/para/admin_user/admin_user_generator.rb
673
677
  - lib/generators/para/component/component_generator.rb
674
678
  - lib/generators/para/component/crud/crud_generator.rb
@@ -850,8 +854,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
850
854
  - !ruby/object:Gem::Version
851
855
  version: '0'
852
856
  requirements: []
853
- rubyforge_project:
854
- rubygems_version: 2.6.11
857
+ rubygems_version: 3.1.4
855
858
  signing_key:
856
859
  specification_version: 4
857
860
  summary: Rails admin engine