klastera 1.5.3 → 1.5.5

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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +3 -0
  4. data/Rakefile +37 -0
  5. data/app/assets/javascripts/klastera/application.js +13 -0
  6. data/app/assets/javascripts/klastera/clusters.js +2 -0
  7. data/app/assets/stylesheets/klastera/clusters.scss +46 -0
  8. data/app/controllers/klastera/application_controller.rb +29 -0
  9. data/app/controllers/klastera/clusters_controller.rb +60 -0
  10. data/app/helpers/klastera/application_helper.rb +40 -0
  11. data/app/models/klastera/cluster.rb +5 -0
  12. data/app/models/klastera/cluster_entity.rb +5 -0
  13. data/app/models/klastera/cluster_filter.rb +5 -0
  14. data/app/models/klastera/cluster_user.rb +5 -0
  15. data/app/models/klastera/concerns/cluster.rb +67 -0
  16. data/app/models/klastera/concerns/cluster_entity.rb +18 -0
  17. data/app/models/klastera/concerns/cluster_filter.rb +22 -0
  18. data/app/models/klastera/concerns/cluster_user.rb +41 -0
  19. data/app/models/klastera/concerns/clusterizable.rb +115 -0
  20. data/app/models/klastera/concerns/organization.rb +49 -0
  21. data/app/models/klastera/concerns/transfer.rb +45 -0
  22. data/app/models/klastera/concerns/user.rb +77 -0
  23. data/app/models/klastera/transfer.rb +5 -0
  24. data/app/views/klastera/clusters/_filter.html.erb +0 -0
  25. data/app/views/klastera/clusters/_form.html.erb +22 -0
  26. data/app/views/klastera/clusters/_form_transfer.html.erb +34 -0
  27. data/app/views/klastera/clusters/_table.html.erb +33 -0
  28. data/app/views/klastera/clusters/create.js.erb +1 -0
  29. data/app/views/klastera/clusters/destroy.js.erb +6 -0
  30. data/app/views/klastera/clusters/edit.html.erb +10 -0
  31. data/app/views/klastera/clusters/index.html.erb +21 -0
  32. data/app/views/klastera/clusters/new.html.erb +10 -0
  33. data/app/views/klastera/clusters/transfer.html.erb +10 -0
  34. data/app/views/klastera/clusters/update.js.erb +1 -0
  35. data/app/views/layouts/klastera/_cluster_entity_fields.html.erb +10 -0
  36. data/app/views/layouts/klastera/_cluster_filter.html.erb +23 -0
  37. data/app/views/layouts/klastera/_cluster_role.html.erb +48 -0
  38. data/app/views/layouts/klastera/_cluster_selector.html.erb +21 -0
  39. data/app/views/layouts/klastera/_cluster_user_fields.html.erb +10 -0
  40. data/app/views/layouts/klastera/_nested_cluster_entity.html.erb +43 -0
  41. data/app/views/layouts/klastera/_nested_cluster_user.html.erb +22 -0
  42. data/app/views/layouts/klastera/_options.html.erb +21 -0
  43. data/config/locales/es.yml +96 -0
  44. data/config/routes.rb +7 -0
  45. data/db/migrate/20200324203929_create_klastera_clusters.rb +12 -0
  46. data/db/migrate/20200326111219_add_cluster_options_to_organizations.rb +6 -0
  47. data/db/migrate/20200330010551_create_klastera_cluster_users.rb +9 -0
  48. data/db/migrate/20200330221601_add_order_field_to_clusters.rb +5 -0
  49. data/db/migrate/20200518142609_create_klastera_cluster_entities.rb +8 -0
  50. data/db/migrate/20200908180057_add_cluster_config_to_organization.rb +5 -0
  51. data/db/migrate/20220602222332_add_unique_index_to_cluster_entities.rb +5 -0
  52. data/db/migrate/20250429110829_add_entity_id_index_to_cluster_entities.rb +6 -0
  53. data/db/migrate/20250429110830_add_entity_id_index_and_entity_type_index_to_cluster_entities.klastera.rb +7 -0
  54. data/lib/klastera/engine.rb +5 -0
  55. data/lib/klastera/version.rb +3 -0
  56. data/lib/klastera.rb +253 -0
  57. data/lib/tasks/klastera_tasks.rake +32 -0
  58. data/test/controllers/klastera/clusters_controller_test.rb +52 -0
  59. data/test/fixtures/klastera/cluster_users.yml +9 -0
  60. data/test/fixtures/klastera/clusters.yml +11 -0
  61. data/test/integration/navigation_test.rb +8 -0
  62. data/test/klastera_test.rb +7 -0
  63. data/test/models/klastera/cluster_test.rb +9 -0
  64. data/test/models/klastera/cluster_user_test.rb +9 -0
  65. data/test/test_helper.rb +21 -0
  66. metadata +79 -7
@@ -0,0 +1,77 @@
1
+ module Klastera::Concerns::User
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+
6
+ CLUSTER_ROOT = 'root'.freeze
7
+ CLUSTER_ADMIN = 'admin'.freeze
8
+ CLUSTER_USER = 'user'.freeze
9
+
10
+ CLUSTER_ROLES = [ CLUSTER_ROOT, CLUSTER_ADMIN, CLUSTER_USER ].freeze
11
+
12
+ has_many :cluster_users, inverse_of: :user, class_name: ::ClusterUser.to_s
13
+ accepts_nested_attributes_for :cluster_users, reject_if: :all_blank, allow_destroy: true
14
+ validates :cluster_role, presence: true, if: -> { organization.present? && organization.is_in_cluster_mode? }
15
+ validates :cluster_role, inclusion: { in: CLUSTER_ROLES , message: I18n.t('klastera.clusters.wrong_option') }, if: -> { cluster_role.present? }
16
+ validate :at_least_one_role, if: -> { use_cluster? && try(:need_cluster_assignation?) }
17
+
18
+ def use_cluster?; cluster_role.present?; end
19
+
20
+ def is_a_cluster_root?; cluster_role == CLUSTER_ROOT; end
21
+ def is_a_cluster_admin?; cluster_role == CLUSTER_ADMIN; end
22
+ def is_a_cluster_user?; cluster_role == CLUSTER_USER; end
23
+
24
+ def need_cluster_assignation?
25
+ organization.present? && organization.required_suborganization_mode? && is_a_cluster_user?
26
+ end
27
+
28
+ def can_admin_clusters?
29
+ organization.present? && organization.is_in_cluster_mode? && ( is_a_cluster_admin? || is_a_cluster_root? )
30
+ end
31
+
32
+ #
33
+ # It tells if this user should see what they cluster role or organization dictates
34
+ # Adminers and users from show cluster modes skip cluster clause
35
+ def cannot_skip_cluster_clause?
36
+ ! ( is_a_cluster_admin? || is_a_cluster_root? || ( organization.present? && organization.optional_suborganization_mode? && cluster_users.blank? ) )
37
+ end
38
+
39
+ #
40
+ # We will try to create a cluster_user record whether
41
+ # the organization is using force mode (if cluster_id isn't present, it will raise a validation error)
42
+ # or
43
+ # cluster_id is present (on show/use mode organizations).
44
+ #
45
+ def try_to_clusterify!(role,cluster_ids)
46
+ if organization.present? && organization.is_in_cluster_mode?
47
+ self.cluster_role = role
48
+ if ( organization.present? && organization.required_suborganization_mode? ) || cluster_ids.present?
49
+ timestamp = DateTime.now.to_i
50
+ nested_attributes = {}
51
+ cluster_ids.each do |cluster_id|
52
+ nested_attributes["#{cluster_id}_#{timestamp}"] = {
53
+ cluster_id: cluster_id,
54
+ _destroy: false
55
+ }
56
+ end
57
+ self.cluster_users_attributes = nested_attributes
58
+ end
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def at_least_one_role
65
+ if cluster_users.none?
66
+ errors.add(:base, I18n.t('klastera.user.you_need_add_at_least_one_cluster'))
67
+ end
68
+ end
69
+
70
+ end
71
+
72
+ module ClassMethods
73
+ def cluster_params
74
+ [ :cluster_role, { cluster_users_attributes: [:id, :cluster_id, :_destroy] } ]
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,5 @@
1
+ module Klastera
2
+ class Transfer
3
+ include Klastera::Concerns::Transfer
4
+ end
5
+ end
File without changes
@@ -0,0 +1,22 @@
1
+ <%= simple_form_for( @cluster, remote: true, html: { autocomplete: :off, class: 'slim-form-field' } ) do |f| %>
2
+ <%=render 'layouts/remote_form/header', o: @cluster, title: title, fa: :qrcode %>
3
+
4
+ <div class="<%=classes_for_remote_modal_body()%>">
5
+ <div class="row">
6
+ <div class="col-xs-6">
7
+ <%= f.input :name, as: :string, label: t('klastera.cluster_name') %>
8
+ </div>
9
+ <div class="col-xs-6">
10
+ <%= f.input :nid, as: :string, label: t('klastera.cluster_nid') %>
11
+ </div>
12
+ <div class="col-xs-6">
13
+ <%= f.input :color, as: :string, label: t('klastera.cluster_color'), input_html: { class: 'color-picker'} %>
14
+ </div>
15
+ <div class="col-xs-6">
16
+ <%= f.input :order, as: :string, label: t('klastera.cluster_order') %>
17
+ </div>
18
+ </div>
19
+ </div>
20
+
21
+ <%=render 'layouts/remote_form/footer', o: @cluster %>
22
+ <% end %>
@@ -0,0 +1,34 @@
1
+ <% can_transfer_and_destroy = @cluster.can_transfer_and_destroy? %>
2
+ <%= simple_form_for( @transfer, url: cluster_path(@cluster), method: :delete, remote: true, html: { autocomplete: :off, class: 'destroy-form slim-form-field' } ) do |f| %>
3
+ <%=render 'layouts/remote_form/header', o: @cluster, title: title, fa: :qrcode %>
4
+ <div class="<%=classes_for_remote_modal_body()%>">
5
+ <h4 class="blank-modal-title text-center">Esta acción es irreversible<br/>¿Está seguro?</h4>
6
+ <% if @cluster.cluster_entities.size > 0 %>
7
+ <br />
8
+ <div class="row">
9
+ <% if can_transfer_and_destroy %>
10
+ <div class="col-xs-12">
11
+ <div class="alert alert-<%=cluster_organization.required_suborganization_mode? ? :warning : :info %> text-center">
12
+ <i class="fa fa-2x fa-<%=cluster_organization.required_suborganization_mode? ? :warning : 'info-circle' %> float-none"></i>
13
+ <p class="margin-top-5 fa-1-2x"><%=t("klastera.clusters.transfer.#{cluster_organization.required_suborganization_mode? ? :required : :optional}")%></p>
14
+ </div>
15
+ </div>
16
+ <div class="col-xs-12">
17
+ <%= f.input :new_cluster_id, collection: @cluster.siblings.map{|s|[s.name,s.id]}, label: t('klastera.clusters.siblings') %>
18
+ </div>
19
+ <% else %>
20
+ <div class="col-xs-12">
21
+ <div class="alert alert-danger text-center no-margin">
22
+ <i class="fa fa-2x fa-times-circle float-none"></i>
23
+ <br />
24
+ <div class="fa-1-2x">
25
+ <%=ac_errors_as_html_list(@cluster.errors)%>
26
+ </div>
27
+ </div>
28
+ </div>
29
+ <% end %>
30
+ </div>
31
+ <% end %>
32
+ </div>
33
+ <%=render 'layouts/remote_form/footer', o: @cluster, btn_type: :danger, hide_submit_button: (! can_transfer_and_destroy ) %>
34
+ <% end %>
@@ -0,0 +1,33 @@
1
+ <table class="table table-striped table-hover table-condensed table-bordered datatable table">
2
+ <thead>
3
+ <tr>
4
+ <th class="text-center cluster-id"><%=t('klastera.cluster_id')%></th>
5
+ <th class="text-center cluster-order"><%=t('klastera.cluster_order')%></th>
6
+ <th class="text-center cluster-color"><%=t('klastera.cluster_color')%></th>
7
+ <th class="text-center cluster-name"><%=t('klastera.cluster_name')%></th>
8
+ <th class="text-center cluster-nid"><%=t('klastera.cluster_nid')%></th>
9
+ <th class="cogs-actions two-icons"><span class="fa fa-cogs"></span></th>
10
+ </tr>
11
+ </thead>
12
+ <tbody>
13
+ <% @clusters.each do |cluster| %>
14
+ <tr class="middle-align-rows">
15
+ <td class="text-center cluster-id"><%= cluster.id %></td>
16
+ <td class="text-center cluster-order"><%= cluster.order %></td>
17
+ <td class="text-center cluster-color">
18
+ <i style='<%="color:#{cluster.color}" if cluster.color.present?%>' class="fa fa-square fa-2x<%=' colorless' if cluster.color.blank?%>" aria-hidden="true"></i>
19
+ </td>
20
+ <td class="text-center cluster-name"><%= cluster.name %></td>
21
+ <td class="text-center cluster-nid"><%= cluster.nid %></td>
22
+ <td class="cogs-actions two-icons">
23
+ <%= link_to edit_cluster_path(cluster), class:'btn btn-primary btn-xs', title: t('shared.actions.edit'), "data-toggle": "modal", "data-target": "#remote-modal-block" do %>
24
+ <span class="fa fa-pencil-square-o"></span>
25
+ <% end %>
26
+ <%= link_to transfer_cluster_path(cluster), class:'btn btn-danger btn-xs', title: t('shared.actions.archive'), "data-toggle": "modal", "data-target": "#remote-modal-block" do %>
27
+ <span class="fa fa-trash"></span>
28
+ <% end %>
29
+ </td>
30
+ </tr>
31
+ <% end %>
32
+ </tbody>
33
+ </table>
@@ -0,0 +1 @@
1
+ <%= render 'layouts/remote_form/common', title: t('klastera.clusters.new.title'), action: :create, id: :cluster, o: @cluster %>
@@ -0,0 +1,6 @@
1
+ <% if @transfer.errors.any? %>
2
+ <%= render 'layouts/remote_form/common', title: t('klastera.clusters.destroy.title', cluster_nid: @cluster.nid), action: :update, id: :cluster, o: @transfer, form_partial: 'form_transfer' %>
3
+ <% else %>
4
+ $('#cluster-remote-form').html("<%= j render 'layouts/remote_form/blank_modal', title: "Eliminado", o: nil, fa: :qrcode, type: :success, message: t('klastera.messages.record_action_successfully',a: t('klastera.actions.deleted').downcase)%>")
5
+ <%= render 'layouts/remote_form/common', action: :destroy, id: :cluster %>
6
+ <% end %>
@@ -0,0 +1,10 @@
1
+ <%= render layout: 'layouts/remote_form/wrapper', locals: { title: t('.title', cluster_nid: @cluster.nid), id: :cluster, fa: :qrcode } do %>
2
+ <div class="pull-right">
3
+ <%= link_to clusters_path(@cluster), class: 'btn btn-primary btn-sm', title: t('shared.actions.show') do %>
4
+ <span class="fa fa-eye"></span>
5
+ <% end %>
6
+ <%= link_to clusters_path, class: 'btn btn-default btn-sm', title: t('shared.actions.back') do %>
7
+ <span class="fa fa-list"></span>
8
+ <% end %>
9
+ </div>
10
+ <% end %>
@@ -0,0 +1,21 @@
1
+ <div class="container" id="gb-content-wrapper">
2
+ <div class="page-header">
3
+ <div class="pull-right">
4
+ <%= link_to(new_cluster_path, class: 'btn btn-success btn-xs action-control', "data-toggle": "modal", "data-target": "#remote-modal-block", title: t('shared.actions.new')) do %>
5
+ <i class="fa fa-plus"></i>
6
+ <% end %>
7
+ </div>
8
+ <h1><span class="fa fa-qrcode"></span> <%= t '.title' %></h1>
9
+ </div>
10
+
11
+ <div id="cluster-filter-remote-form">
12
+ <%= render 'filter' %>
13
+ </div>
14
+
15
+ <div id="cluster-remote-table" class="datatable-without-search">
16
+ <%= render 'table' %>
17
+ </div>
18
+
19
+ </div>
20
+
21
+ <%= render 'layouts/remote_form/modal', width: 'md' %>
@@ -0,0 +1,10 @@
1
+ <%= render layout: 'layouts/remote_form/wrapper', locals: { title: t('.title'), id: :cluster, fa: :qrcode } do %>
2
+ <div class="pull-right">
3
+ <%= link_to clusters_path(@cluster), class: 'btn btn-primary btn-sm', title: t('shared.actions.show') do %>
4
+ <span class="fa fa-eye"></span>
5
+ <% end %>
6
+ <%= link_to clusters_path, class: 'btn btn-default btn-sm', title: t('shared.actions.back') do %>
7
+ <span class="fa fa-list"></span>
8
+ <% end %>
9
+ </div>
10
+ <% end %>
@@ -0,0 +1,10 @@
1
+ <%= render layout: 'layouts/remote_form/wrapper', locals: { title: t('.title', cluster_nid: @cluster.nid), id: :cluster, fa: :qrcode, form: 'form_transfer' } do %>
2
+ <div class="pull-right">
3
+ <%= link_to clusters_path(@cluster), class: 'btn btn-primary btn-sm', title: t('shared.actions.show') do %>
4
+ <span class="fa fa-eye"></span>
5
+ <% end %>
6
+ <%= link_to clusters_path, class: 'btn btn-default btn-sm', title: t('shared.actions.back') do %>
7
+ <span class="fa fa-list"></span>
8
+ <% end %>
9
+ </div>
10
+ <% end %>
@@ -0,0 +1 @@
1
+ <%= render 'layouts/remote_form/common', title: t('klastera.clusters.edit.title', cluster_nid: @cluster.nid), action: :update, id: :cluster, o: @cluster %>
@@ -0,0 +1,10 @@
1
+ <tr class="nested-fields">
2
+ <td class="field">
3
+ <%= f.select :cluster_id, @clusters_session.order(:order,:name).map{|c|[c.name,c.id]}, { include_blank: true }, { class: 'form-control' } %>
4
+ </td>
5
+ <td class="action vertical-align-middle text-center" width="44">
6
+ <%= link_to_remove_association f, class: 'btn btn-danger btn-xs text-danger' do %>
7
+ <i class="fa fa-trash"></i>
8
+ <% end %>
9
+ </td>
10
+ </tr>
@@ -0,0 +1,23 @@
1
+ <%= simple_form_for(@cluster_filter, url: url, remote: true) do |f| %>
2
+
3
+ <div class="inline-buttons-block top">
4
+ <%= button_tag(t('klastera.actions.filter'), class: 'btn btn-primary btn-sm float-right', data: { disable_with: "<i class='fa fa-spinner spin'></i>" }) %>
5
+ </div>
6
+
7
+ <%=yield(f) if block_given? %>
8
+
9
+ <% if cluster_organization.is_in_cluster_mode? %>
10
+ <% cluster_collection = cluster_list.order(:order, :name).map{|c|[c.name,(c.id||c.nid)]} %>
11
+ <% if cluster_collection.size > 1 %>
12
+ <div class="inline-label-control-block">
13
+ <%= f.input :cluster_id, collection: cluster_collection, prompt: t('klastera.clusters.all'), label: false, wrapper: false %>
14
+ <label class="control-label"><%=t('klastera.cluster.title')%>:</label>
15
+ </div>
16
+ <% end %>
17
+ <% end %>
18
+
19
+ <div class="inline-buttons-block bottom">
20
+ <%= button_tag(t('klastera.actions.filter'), class: 'btn btn-primary btn-sm float-right', data: { disable_with: "<i class='fa fa-spinner spin'></i>" }) %>
21
+ </div>
22
+
23
+ <% end %>
@@ -0,0 +1,48 @@
1
+ <%
2
+ user_cluster_roles = user_cluster_roles.nil? ? User::CLUSTER_ROLES : user_cluster_roles
3
+ other_visibility_reason = other_visibility_reason.nil? ? false : other_visibility_reason
4
+ if @user.try(:organization).present?
5
+ normal_user_help = @user.try(:organization).optional_suborganization_mode? ? :optional : :required
6
+ else
7
+ normal_user_help = :new
8
+ end
9
+ %>
10
+ <% if @user.try(:organization).try(:is_in_cluster_mode?) || other_visibility_reason %>
11
+ <div class="form-group">
12
+ <%= f.label t('klastera.cluster_role') %>
13
+ <span
14
+ class="klastera-user-cluster-rol"
15
+ data-title="<%= t('klastera.help.popover.title')%>"
16
+ data-toggle="popover"
17
+ data-content="<%=
18
+ t('klastera.help.popover.content.cluster_role',
19
+ m1: t('klastera.help.roles.description', r: t('klastera.roles.admin'), d: t('klastera.help.roles.admin')),
20
+ m2: t('klastera.help.roles.description', r: t('klastera.roles.user'), d: t("klastera.help.roles.user.#{normal_user_help}"))
21
+ )%>"
22
+ >
23
+ <%= fa_icon 'info-circle', class: 'btn-link' %>
24
+ </span>
25
+ <%= f.select :cluster_role, user_cluster_roles.map{|r|[t("klastera.roles.#{r}"),r]}, { include_blank: true }, { class: 'form-control' } %>
26
+ </div>
27
+
28
+ <script type="text/javascript">
29
+ $('[name="user[cluster_role]"]').change(function(){
30
+ if ( $(this).val() == "<%=User::CLUSTER_ROOT%>" ) {
31
+ swal({
32
+ title: "<%= t('klastera.user.cluster_root_asignation.title') %>",
33
+ text: "<%= t('klastera.user.cluster_root_asignation.text') %>",
34
+ type: "warning",
35
+ showCancelButton: true,
36
+ confirmButtonText: "<%= t('klastera.user.assign_cluster_role') %>",
37
+ cancelButtonText: "<%= t('klastera.actions.cancel') %>",
38
+ }, function (isConfirm) {
39
+ if ( ! isConfirm ) {
40
+ $('[name="user[cluster_role]"]').val("");
41
+ }
42
+ });
43
+ }
44
+ });
45
+ var popoverTemplate = '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
46
+ $('.klastera-user-cluster-rol').popover({ template: popoverTemplate, html: true, trigger: 'click', });
47
+ </script>
48
+ <% end %>
@@ -0,0 +1,21 @@
1
+ <%
2
+ f = f.nil? ? nil : f
3
+ other_visibility_reason = other_visibility_reason.nil? ? false : other_visibility_reason
4
+ %>
5
+ <% if cluster_organization.is_in_cluster_mode? || other_visibility_reason %>
6
+ <%
7
+ cluster_collection = @clusters_session || cluster_list
8
+ label = t('klastera.cluster.title')
9
+ %>
10
+ <% if f.nil? %>
11
+ <%= label_tag(:cluster_id, label, class: 'control-label') %>
12
+ <%= select_tag(:cluster_id, options_from_collection_for_select(cluster_collection, 'id', 'display_name_nid'), class: 'form-control', prompt: 'Seleccione') %>
13
+ <% elsif f.is_a?(SimpleForm::FormBuilder) %>
14
+ <%= f.input :cluster_id, collection: cluster_collection.map{|c|[c.name,c.id]}, label: label %>
15
+ <% else %>
16
+ <div class="form-group">
17
+ <%= f.label label %>
18
+ <%= f.select :cluster_id, cluster_collection.map{|c|[c.name,c.id]}, { include_blank: true }, { class: 'form-control' } %>
19
+ </div>
20
+ <% end %>
21
+ <% end %>
@@ -0,0 +1,10 @@
1
+ <tr class="nested-fields">
2
+ <td class="field">
3
+ <%= f.select :cluster_id, @user.organization.clusters.order(:order, :name).map{|c|[c.name,c.id]}, { include_blank: true }, { class: 'form-control' } %>
4
+ </td>
5
+ <td class="action vertical-align-middle text-center" width="44">
6
+ <%= link_to_remove_association f, class: 'btn btn-danger btn-xs text-danger' do %>
7
+ <i class="fa fa-trash"></i>
8
+ <% end %>
9
+ </td>
10
+ </tr>
@@ -0,0 +1,43 @@
1
+ <% if cluster_organization.is_in_cluster_mode? %>
2
+ <% @clusters_session = cluster_list(false) %>
3
+ <div class="col-xs-12">
4
+ <% if hide_title||=false %>
5
+ <div class="form-group file required <%=f.object.class.name.parameterize%>_cluster_entity<%=' has-error' if f.object.errors.has_key?(:cluster_entities)%>">
6
+ <label class="text" for="<%=f.object.class.name.parameterize%>_cluster_entity">
7
+ <%=t('klastera.clusters.title')%>
8
+ <% if cluster_organization.required_suborganization_mode? %>
9
+ <abbr title="required">*</abbr>
10
+ <% end %>
11
+ </label>
12
+ <% end %>
13
+
14
+ <div class="nested-cluster-entity">
15
+ <div class="panel panel-default">
16
+ <div class="panel-heading<%=' custom-required' if cluster_organization.required_suborganization_mode? %>">
17
+ <h4 class="panel-title">
18
+ <% unless hide_title||=false %>
19
+ <div class="float-left" style="line-height: 20px"><%=t('klastera.clusters.title')%></div>
20
+ <% end %>
21
+ <%= link_to_add_association f, :cluster_entities, partial: 'layouts/klastera/cluster_entity_fields', data: { 'association-insertion-node': :'tbody.cluster-entity-rows', 'association-insertion-method': :append }, class: 'btn btn-success btn-xs float-right color-white' do %>
22
+ <i class="fa fa-plus"></i>
23
+ <% end %>
24
+ <div class="clearfix"></div>
25
+ </h4>
26
+ </div>
27
+ <table id="cluster-entities" class="table table-striped">
28
+ <tbody class="cluster-entity-rows">
29
+ <%= f.fields_for :cluster_entities do |cluster_entity|%>
30
+ <% next unless @clusters_session.map(&:id).include?(cluster_entity.try(:object).try(:cluster_id)) %>
31
+ <%= render 'layouts/klastera/cluster_entity_fields', f: cluster_entity %>
32
+ <% end %>
33
+ </body>
34
+ </table>
35
+ </div>
36
+ </div>
37
+
38
+ <% if hide_title||=false %>
39
+ <div class="invalid-feedback"><%=f.object.try(:errors)&.[](:cluster_entities).try(:first)%></div>
40
+ </div>
41
+ <% end %>
42
+ </div>
43
+ <% end %>
@@ -0,0 +1,22 @@
1
+ <% if @user.try(:is_a_cluster_user?) %>
2
+ <div class="nested-cluster-user">
3
+ <div class="panel panel-default">
4
+ <div class="panel-heading">
5
+ <h4 class="panel-title">
6
+ <div class="float-left" style="line-height: 20px"><%=t('klastera.clusters.title')%></div>
7
+ <%= link_to_add_association f, :cluster_users, partial: 'layouts/klastera/cluster_user_fields', data: { 'association-insertion-node': :'tbody.cluster-user-rows', 'association-insertion-method': :append }, class: 'btn btn-success btn-xs float-right color-white' do %>
8
+ <i class="fa fa-plus"></i>
9
+ <% end %>
10
+ <div class="clearfix"></div>
11
+ </h4>
12
+ </div>
13
+ <table id="cluster-users" class="table table-striped">
14
+ <tbody class="cluster-user-rows">
15
+ <%= f.fields_for :cluster_users do |cluster_user|%>
16
+ <%= render 'layouts/klastera/cluster_user_fields', f: cluster_user %>
17
+ <% end %>
18
+ </body>
19
+ </table>
20
+ </div>
21
+ </div>
22
+ <% end %>
@@ -0,0 +1,21 @@
1
+ <div class="form-group">
2
+ <%= f.label t('klastera.use_cluster_as') %>
3
+ <span
4
+ class="klastera-cluster-option"
5
+ data-title="<%= t('klastera.help.popover.title')%>"
6
+ data-toggle="popover"
7
+ data-content="<%=
8
+ t('klastera.help.popover.content.user_as_cluster_as',
9
+ m1:t('klastera.help.required_suborganization', t: t('klastera.required_suborganization'), e: t('klastera.clusters.entities')),
10
+ m2:t('klastera.help.optional_suborganization', t: t('klastera.optional_suborganization'), e: t('klastera.clusters.entities'))
11
+ )%>"
12
+ >
13
+ <%= fa_icon 'info-circle', class: 'btn-link' %>
14
+ </span>
15
+ <%= f.select :use_cluster_as, ::Cluster::MODES.map{|cm|[t("klastera.#{cm}"),cm]}, { include_blank: true }, { class: 'form-control' } %>
16
+ </div>
17
+
18
+ <script type="text/javascript">
19
+ var popoverTemplate = '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
20
+ $('.klastera-cluster-option').popover({ template: popoverTemplate, html: true, trigger: 'click', });
21
+ </script>
@@ -0,0 +1,96 @@
1
+ es:
2
+ klastera:
3
+ without_cluster: Sin cluster
4
+ cluster:
5
+ title: Cluster
6
+ id: Cluster ID
7
+ user:
8
+ add_cluster: Agregar cluster
9
+ you_need_add_at_least_one_cluster: Esta organización usa clusters en modo obligatorio. Asigne un cluster al usuario por favor.
10
+ assign_cluster_role: Asignar role
11
+ cluster_root_asignation:
12
+ title: Asignación Super Admin
13
+ text: Este rol es sólo para usuarios super admin
14
+ roles:
15
+ root: Super Admin
16
+ admin: Admin Cluster
17
+ user: Usuario Cluster
18
+ using_cluster_as: Usando cluster como
19
+ cluster_order: Orden
20
+ cluster_id: ID
21
+ cluster_role: Rol de clusters
22
+ cluster_name: Nombre
23
+ cluster_nid: Código
24
+ cluster_color: Color
25
+ organization_name: Organización
26
+ use_cluster_as: Activar y usar cluster en modo
27
+ required_suborganization: Obligatorio
28
+ optional_suborganization: Opcional
29
+ clusters:
30
+ all: Todos
31
+ title: Clusters
32
+ siblings: Seleccione un cluster
33
+ cant_delete: No se puede eliminar
34
+ wrong_option: "%{value} es una opción inválida"
35
+ entities: "[agregar entidades]"
36
+ index:
37
+ title: Gestión de clusters
38
+ new:
39
+ title: Nuevo Cluster
40
+ edit:
41
+ title: Editar Cluster %{cluster_nid}
42
+ destroy:
43
+ title: Eliminar Cluster %{cluster_nid}
44
+ transfer:
45
+ title: Eliminar Cluster %{cluster_nid}
46
+ required: Para eliminar el Cluster es necesario asignar las entidades asociadas a un nuevo Cluster
47
+ optional: Entidades asociadas al Cluster pueden quedar sin asignar o ser asignadas a un nuevo Cluster
48
+ actions:
49
+ filter: Filtrar
50
+ cancel: Cancelar
51
+ add: Agregar
52
+ delete: Eliminar
53
+ create: Crear
54
+ update: Actualizar
55
+ deleted: Eliminado
56
+ created: Creado
57
+ updated: Actualizado
58
+ messages:
59
+ has_one_cluster_entity: Está permitido agregar un sólo cluster
60
+ at_least_one_cluster_entity: Debe agregar al menos un cluster
61
+ record_action_successfully: Registro %{a} exitosamente
62
+ cant_delete_the_last_record_in_required_suborganization_mode: No se puede eliminar el único cluster de esta organización
63
+ duplicated_cluster_entity: Hay un cluster duplicado
64
+ new_cluster_id:
65
+ nil: Cluster no existe
66
+ same: Cluster no puede ser el mismo
67
+ current_cluster:
68
+ cant_transfer: No se puede eliminar ni mover el cluster
69
+ access_cluster_admin: No puede acceder a esta página
70
+ help:
71
+ popover:
72
+ title: Descripción de cada opción
73
+ content:
74
+ user_as_cluster_as: "<ul class='klastera-option-help'><li>%{m1}</li><li>%{m2}</li></ul>"
75
+ required_suborganization: "<b>%{t}</b>: Obliga asignar un cluster a las entidades %{e} ."
76
+ optional_suborganization: "<b>%{t}</b>: Permite asociar un cluster a las entidades %{e}."
77
+ roles:
78
+ description: "<b>%{r}</b>: %{d}."
79
+ root: Ve todos los clusters y no necesita asignación
80
+ admin: Ve todos los clusters y no necesita asignación
81
+ user:
82
+ new: Usuario al que se le puede restringir la cosas que puede ver a través de su asignación con los clusters
83
+ required: Necesita que se le asigne uno o más clusters, de lo contrario no verá las entidades clusterizadas.
84
+ optional: Si no tiene clusters asignados verá todas la entidades CON y SIN clusters. Si se le asigna un cluster, verá las entidades de ese cluster + las entidades SIN cluster.
85
+ activemodel:
86
+ attributes:
87
+ 'klastera/transfer':
88
+ new_cluster_id: Cluster a transferir
89
+ activerecord:
90
+ models:
91
+ 'klastera/cluster':
92
+ one: Cluster
93
+ other: Clusters
94
+ attributes:
95
+ organization:
96
+ use_cluster_as: Activar y usar cluster como
data/config/routes.rb ADDED
@@ -0,0 +1,7 @@
1
+ Klastera::Engine.routes.draw do
2
+ resources :clusters, except: [:show] do
3
+ member do
4
+ get :transfer
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,12 @@
1
+ class CreateKlasteraClusters < ActiveRecord::Migration
2
+ def change
3
+ create_table :clusters do |t|
4
+ t.references :organization
5
+ t.text :name
6
+ t.string :nid
7
+ t.string :color
8
+
9
+ t.timestamps null: false
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,6 @@
1
+ class AddClusterOptionsToOrganizations < ActiveRecord::Migration
2
+ def change
3
+ add_column :organizations, :use_cluster_as, :string
4
+ add_index :organizations, :use_cluster_as, using: :btree
5
+ end
6
+ end
@@ -0,0 +1,9 @@
1
+ class CreateKlasteraClusterUsers < ActiveRecord::Migration
2
+ def change
3
+ create_table :cluster_users do |t|
4
+ t.integer :user_id, null: false
5
+ t.integer :cluster_id, null: false
6
+ end
7
+ add_column :users, :cluster_role, :string
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ class AddOrderFieldToClusters < ActiveRecord::Migration
2
+ def change
3
+ add_column :clusters, :order, :integer
4
+ end
5
+ end
@@ -0,0 +1,8 @@
1
+ class CreateKlasteraClusterEntities < ActiveRecord::Migration
2
+ def change
3
+ create_table :cluster_entities do |t|
4
+ t.integer :cluster_id, index: true
5
+ t.references :entity, polymorphic: true, index: true
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ class AddClusterConfigToOrganization < ActiveRecord::Migration
2
+ def change
3
+ add_column :organizations, :cluster_config, :text
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddUniqueIndexToClusterEntities < ActiveRecord::Migration[5.2]
2
+ def change
3
+ add_index :cluster_entities, %i[cluster_id entity_id entity_type], unique: true, name: :index_on_cluster_id_and_entity_id_and_entity_type
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ class AddEntityIdIndexToClusterEntities < ActiveRecord::Migration[5.2]
2
+ disable_ddl_transaction!
3
+ def change
4
+ add_index :cluster_entities, :entity_id, algorithm: :concurrently
5
+ end
6
+ end