klastera 1.3.1 → 1.4.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a8ffbcdd772da4880e6fac3a2fa9165754a873673afb5f2108161d099e634622
4
- data.tar.gz: 8541bb3c9c26b8e0084e42b91ef9262b443dbaf1250e4bd209a2fde9db6d92a9
3
+ metadata.gz: 2c6ed8465fe9fcf78e96af1ad4d137ecdc867cc26242e0c4aada31557668b5db
4
+ data.tar.gz: 045d77f5c652c11aa1badd03c404c26b07a04ddd8d7bd7886b72ba47195d9339
5
5
  SHA512:
6
- metadata.gz: e43f85ef19e054e32274ad4c5c99db112333c76fa831383e72f3e0f7d7ad312d5b48519c33794a46ad3148dd4d50cca62117620e756b12933f0889a9891ad631
7
- data.tar.gz: dc98ab10fc1588bec841af4b1e3a4caee5b976986e8023b7528c70190ee2b27fb2e230855773aaa1fcf1a28392380b9f63d186b9165e4f5704acf2631cd3d40f
6
+ metadata.gz: 70bb42f25ded9aa221049e3d2fc25a88eb467514f562a99b5ffbc26e575a9bbf692e41af450201d82f00452f7d924e5b4f3a71409539d091ab60397de65fc379
7
+ data.tar.gz: 53e3902628bfac1f1353e0d6760483df4d82a51aa4ee32066c579e23b81bdb3832ab3ef9fc79ccff94d576971958ca4d00a8920ed7860ba7df1356c894b3c2c4
@@ -8,7 +8,9 @@ module Klastera::Concerns::Cluster
8
8
 
9
9
  attr_reader :last_record
10
10
 
11
- MODES = [ :required_suborganization, :optional_suborganization, :optional_filter ].freeze
11
+ REQUIRED_MODE = :required_suborganization.freeze
12
+ OPTIONAL_MODE = :optional_suborganization.freeze
13
+ MODES = [ REQUIRED_MODE, OPTIONAL_MODE ].freeze
12
14
 
13
15
  belongs_to :organization, class_name: Klastera.organization_class
14
16
  has_many :cluster_users
@@ -56,7 +58,7 @@ module Klastera::Concerns::Cluster
56
58
  end
57
59
 
58
60
  def display_name_nid
59
- "#{name} (#{nid})"
61
+ "#{name} (#{nid==Klastera::UNCLUSTERED_ENTITY ? I18n.t("klastera.#{ Klastera::UNCLUSTERED_ENTITY}") : nid})"
60
62
  end
61
63
  end
62
64
 
@@ -14,7 +14,7 @@ module Klastera::Concerns::ClusterUser
14
14
  #
15
15
  def clusters_of(organization,and_user=nil)
16
16
  and_user_id = and_user.present? ? { users: { id: and_user } } : {}
17
- ::Cluster.eager_load(cluster_users: :user).where({ organization_id: organization }.merge(and_user_id) )
17
+ ::Cluster.eager_load(cluster_users: :user).where({ organization_id: organization }.order(order: :asc).merge(and_user_id) )
18
18
  end
19
19
 
20
20
  ##
@@ -1,7 +1,6 @@
1
1
  module Klastera::Concerns::Clusterizable
2
2
  extend ActiveSupport::Concern
3
3
  included do
4
- attr_accessor :lonely_cluster
5
4
  belongs_to :cluster
6
5
 
7
6
  has_many :cluster_entities, as: :entity, class_name: Klastera::ClusterEntity
@@ -11,6 +10,8 @@ module Klastera::Concerns::Clusterizable
11
10
  validate :at_least_one_cluster_entity, if: proc { self.organization.required_suborganization_mode? }
12
11
  validate :uniqueness_of_cluster_entity_record
13
12
 
13
+ scope :includes_cluster, -> { includes(cluster_entities: :cluster) }
14
+
14
15
  def at_least_one_cluster_entity
15
16
  if cluster_entities.length == 0 || cluster_entities.reject{|cluster_entity| cluster_entity._destroy == true}.empty?
16
17
  return errors.add(:cluster_entities, I18n.t('klastera.messages.at_least_one_cluster_entity'))
@@ -43,7 +44,7 @@ module Klastera::Concerns::Clusterizable
43
44
 
44
45
  module ClassMethods
45
46
  def cluster_entity_params
46
- [ :cluster_id, :lonely_cluster, { cluster_entities_attributes: [:id, :cluster_id, :_destroy] } ]
47
+ [ :cluster_id, { cluster_entities_attributes: [:id, :cluster_id, :_destroy] } ]
47
48
  end
48
49
  end
49
50
  end
@@ -22,19 +22,11 @@ module Klastera::Concerns::Organization
22
22
  end
23
23
 
24
24
  def required_suborganization_mode?
25
- cluster_mode == ::Cluster::MODES.first
25
+ cluster_mode == ::Cluster::REQUIRED_MODE
26
26
  end
27
27
 
28
28
  def optional_suborganization_mode?
29
- cluster_mode == ::Cluster::MODES.second
30
- end
31
-
32
- def optional_filter_mode?
33
- cluster_mode == ::Cluster::MODES.third
34
- end
35
-
36
- def optional_mode?
37
- optional_suborganization_mode? || optional_filter_mode?
29
+ cluster_mode == ::Cluster::OPTIONAL_MODE
38
30
  end
39
31
  end
40
32
 
@@ -21,10 +21,6 @@ module Klastera::Concerns::User
21
21
  def is_a_cluster_admin?; self.cluster_role == CLUSTER_ADMIN; end
22
22
  def is_a_cluster_user?; self.cluster_role == CLUSTER_USER; end
23
23
 
24
- def is_not_a_cluster_root?; ! self.is_a_cluster_root?; end
25
- def is_not_a_cluster_admin?; ! self.is_a_cluster_admin?; end
26
- def is_not_a_cluster_user?; ! self.is_a_cluster_user?; end
27
-
28
24
  def need_cluster_assignation?
29
25
  self.try(:organization).try(:required_suborganization_mode?) && self.is_a_cluster_user?
30
26
  end
@@ -33,6 +29,13 @@ module Klastera::Concerns::User
33
29
  self.organization.is_in_cluster_mode? && ( self.is_a_cluster_admin? || self.is_a_cluster_root? )
34
30
  end
35
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
+ ! ( self.is_a_cluster_admin? || self.is_a_cluster_root? || ( self.organization.optional_suborganization_mode? && self.cluster_users.blank? ) )
37
+ end
38
+
36
39
  #
37
40
  # We will try to create a cluster_user record whether
38
41
  # the organization is using force mode (if cluster_id isn't present, it will raise a validation error)
@@ -1,4 +1,4 @@
1
- <%= simple_form_for(@filter, url: url, remote: true) do |f| %>
1
+ <%= simple_form_for(@cluster_filter, url: url, remote: true) do |f| %>
2
2
 
3
3
  <div class="inline-buttons-block top">
4
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>" }) %>
@@ -7,7 +7,7 @@
7
7
  <%=yield(f) if block_given? %>
8
8
 
9
9
  <% if cluster_organization.is_in_cluster_mode? %>
10
- <% cluster_collection = cluster_of_my_own.map{|c|[c.name,(c.id||c.nid)]} %>
10
+ <% cluster_collection = cluster_list.map{|c|[c.name,(c.id||c.nid)]} %>
11
11
  <% if cluster_collection.size > 1 %>
12
12
  <div class="inline-label-control-block">
13
13
  <%= f.input :cluster_id, collection: cluster_collection, prompt: t('klastera.clusters.all'), label: false, wrapper: false %>
@@ -1,8 +1,27 @@
1
- <% user_cluster_roles = user_cluster_roles.nil? ? User::CLUSTER_ROLES : user_cluster_roles %>
2
- <% other_visibility_reason = other_visibility_reason.nil? ? false : other_visibility_reason %>
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
+ %>
3
10
  <% if @user.try(:organization).try(:is_in_cluster_mode?) || other_visibility_reason %>
4
11
  <div class="form-group">
5
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>
6
25
  <%= f.select :cluster_role, user_cluster_roles.map{|r|[t("klastera.roles.#{r}"),r]}, { include_blank: true }, { class: 'form-control' } %>
7
26
  </div>
8
27
 
@@ -23,5 +42,7 @@
23
42
  });
24
43
  }
25
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', });
26
47
  </script>
27
48
  <% end %>
@@ -4,26 +4,18 @@
4
4
  %>
5
5
  <% if cluster_organization.is_in_cluster_mode? || other_visibility_reason %>
6
6
  <%
7
- cluster_collection = @clusters_session || cluster_of_my_own
7
+ cluster_collection = @clusters_session || cluster_list
8
8
  label = t('klastera.cluster.title')
9
9
  %>
10
10
  <% if f.nil? %>
11
11
  <%= label_tag(:cluster_id, label, class: 'control-label') %>
12
12
  <%= select_tag(:cluster_id, options_from_collection_for_select(cluster_collection, 'id', 'display_name_nid'), class: 'form-control', prompt: 'Seleccione') %>
13
13
  <% elsif f.is_a?(SimpleForm::FormBuilder) %>
14
- <% if cluster_collection.size == 1 %>
15
- <%= f.input :lonely_cluster, as: :hidden, html_input: { value: true } %>
16
- <% else %>
17
- <%= f.input :cluster_id, collection: cluster_collection.map{|c|[c.name,c.id]}, label: label %>
18
- <% end %>
14
+ <%= f.input :cluster_id, collection: cluster_collection.map{|c|[c.name,c.id]}, label: label %>
19
15
  <% else %>
20
- <% if cluster_collection.size == 1 %>
21
- <%= f.hidden_field :lonely_cluster, value: true %>
22
- <% else %>
23
- <div class="form-group">
24
- <%= f.label label %>
25
- <%= f.select :cluster_id, cluster_collection.map{|c|[c.name,c.id]}, { include_blank: true }, { class: 'form-control' } %>
26
- </div>
27
- <% end %>
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>
28
20
  <% end %>
29
21
  <% end %>
@@ -1,5 +1,5 @@
1
1
  <% if cluster_organization.is_in_cluster_mode? %>
2
- <% @clusters_session = cluster_of_my_own %>
2
+ <% @clusters_session = cluster_list(false) %>
3
3
  <div class="col-xs-12">
4
4
  <% if hide_title||=false %>
5
5
  <div class="form-group file required <%=f.object.class.name.parameterize%>_cluster_entity<%=' has-error' if f.object.errors.has_key?(:cluster_entities)%>">
@@ -5,10 +5,9 @@
5
5
  data-title="<%= t('klastera.help.popover.title')%>"
6
6
  data-toggle="popover"
7
7
  data-content="<%=
8
- t('klastera.help.popover.content',
8
+ t('klastera.help.popover.content.user_as_cluster_as',
9
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
- m3:t('klastera.help.optional_filter', t: t('klastera.optional_filter'), e: t('klastera.clusters.entities'))
10
+ m2:t('klastera.help.optional_suborganization', t: t('klastera.optional_suborganization'), e: t('klastera.clusters.entities'))
12
11
  )%>"
13
12
  >
14
13
  <%= fa_icon 'info-circle', class: 'btn-link' %>
@@ -9,24 +9,23 @@ es:
9
9
  you_need_add_at_least_one_cluster: Esta organización usa clusters en modo obligatorio. Asigne un cluster al usuario por favor.
10
10
  assign_cluster_role: Asignar role
11
11
  cluster_root_asignation:
12
- title: Asignación Routing Super Admin
13
- text: Este rol es sólo para usuarios Routing
12
+ title: Asignación Super Admin
13
+ text: Este rol es sólo para usuarios super admin
14
14
  roles:
15
- root: Routing Super Admin
16
- admin: Admin Cliente [ Ignora asignación, ve todos los clusters ]
17
- user: Usuario normal [ Ve sólo los clusters asignados ]
15
+ root: Super Admin
16
+ admin: Admin Cluster
17
+ user: Usuario Cluster
18
18
  using_cluster_as: Usando cluster como
19
19
  cluster_order: Orden
20
20
  cluster_id: ID
21
- cluster_role: Cluster rol
21
+ cluster_role: Rol de clusters
22
22
  cluster_name: Nombre
23
23
  cluster_nid: Código
24
24
  cluster_color: Color
25
25
  organization_name: Organización
26
- use_cluster_as: Activar y usar cluster como
27
- required_suborganization: Sub-organización Obligatorio
28
- optional_suborganization: Sub-organización Opcional
29
- optional_filter: Filtro opcional
26
+ use_cluster_as: Activar y usar cluster en modo
27
+ required_suborganization: Obligatorio
28
+ optional_suborganization: Opcional
30
29
  clusters:
31
30
  all: Todos
32
31
  title: Clusters
@@ -71,10 +70,18 @@ es:
71
70
  help:
72
71
  popover:
73
72
  title: Descripción de cada opción
74
- content: "<ul class='klastera-option-help'><li>%{m1}</li><li>%{m2}</li><li>%{m3}</li></ul>"
75
- required_suborganization: "<b>%{t}</b>: Obliga definir un cluster en las entidades %{e} ."
76
- optional_suborganization: "<b>%{t}</b>: Permite definir un cluster en las entidades %{e}."
77
- optional_filter: "<b>%{t}</b>: Permite definir un cluster en las entidades %{e} de forma opcional."
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.
78
85
  activemodel:
79
86
  attributes:
80
87
  'klastera/transfer':
@@ -5,152 +5,12 @@ module Klastera
5
5
 
6
6
  extend ActiveSupport::Concern
7
7
 
8
- UNCLUSTERED_ENTITY = 'without_cluster'
9
- KLSTR_HELPERS = %i[
10
- cluster_user cluster_organization user_has_more_than_one_cluster cluster_scope cluster_of_my_own
11
- user_clusters_string_list set_collection_before_group_by_entity __cluster_scope
12
- ]
8
+ UNCLUSTERED_POSITION = 9999
9
+ UNCLUSTERED_ENTITY = 'without_cluster'.freeze
10
+ KLSTR_HELPERS = %i[ cluster_user cluster_organization cluster_list cluster_scope cluster_scope_through_of user_clusters_string_list ].freeze
13
11
 
14
12
  class << self
15
13
 
16
- ##
17
- #
18
- #
19
- def set_cluster_entities_attributes!(entity,array_cluster_ids)
20
- cluster_entities_attributes = {}
21
- entity_cluster_entities = entity.try(:cluster_entities) || []
22
- entity_clusters = entity_cluster_entities.map(&:cluster_id).sort
23
- array_cluster_ids = array_cluster_ids.map(&:id).sort
24
- if array_cluster_ids != entity_clusters
25
- now = DateTime.now
26
- entity_cluster_entities.each_with_index do |ec,index|
27
- remove_cluster = true
28
- if array_cluster_ids.include?(ec.cluster_id)
29
- remove_cluster = false
30
- array_cluster_ids.delete(ec.cluster_id)
31
- end
32
- cluster_entities_attributes[index] = { id: ec.id, cluster_id: ec.cluster_id, _destroy: remove_cluster }
33
- end
34
- array_cluster_ids.each_with_index do |cluster_id,index|
35
- cluster_entities_attributes[now.to_i+index] = { cluster_id: cluster_id, _destroy: false }
36
- end
37
- end
38
- cluster_entities_attributes
39
- end
40
-
41
- ##
42
- #
43
- #
44
- def set_collection_before_group_by_entity!(active_record_collection,entity_params,organization)
45
- entity_params_keys = [:entity_name,:entity_attribute,:entity_id,:entity_id_attribute,:unamed]
46
- entity_params[:entity_id] ||= nil #Ensures the entity_id attribute presence even if there is no filter
47
- entity_params = entity_params.slice(*entity_params_keys).values
48
- model_class = active_record_collection.model.base_class
49
- model_relations = model_class.reflections.keys
50
- if model_relations.include?(entity_params[0])
51
- entity_params << "#{entity_params[0]}_id".to_sym
52
- if entity_params[0] == 'cluster'
53
- entity_params << I18n.t("klastera.#{UNCLUSTERED_ENTITY}") if organization.is_in_cluster_mode?
54
- active_record_collection = Klastera.filter_clusterized!(
55
- active_record_collection,
56
- entity_params[2],
57
- organization
58
- )
59
- end
60
- yield( active_record_collection, entity_params_keys.zip(entity_params).to_h )
61
- end
62
- end
63
-
64
- #
65
- # In order to this work, the active_record_collection argument should be a cluster_scope! return.
66
- #
67
- def filter_clusterized!(active_record_collection,cluster_id,organization)
68
- if organization.is_in_cluster_mode? && cluster_id.present?
69
- # Ensures that an array of ids is used in the statement
70
- cluster_array = cluster_id.is_a?(Array) ? cluster_id : [cluster_id]
71
- # If we receive a UNCLUSTERED_ENTITY request, it means that we should filter by entities without clusters.
72
- # The optional_mode?(show/use) condition add to this method an ambivalent use,
73
- # where you can filter a unique value or multiple including NULL ones.
74
- cluster_array << nil if cluster_array.delete(UNCLUSTERED_ENTITY) || organization.optional_mode?
75
- active_record_collection = active_record_collection.where(cluster_entities: { cluster_id: cluster_array.uniq })
76
- # You should use a block with clusterable data only
77
- yield(active_record_collection) if block_given?
78
- end
79
- active_record_collection
80
- end
81
-
82
- ##
83
- # If the cluster mode is not active, this returns a scope filtered by clusters of its organization/users/cluster
84
- #
85
- def cluster_scope!(scope,user,organization,cluster_id=nil)
86
- scope_klass = scope_class(scope).where(organization_id: organization)
87
- if organization.is_in_cluster_mode?
88
-
89
- if cluster_id.present?
90
- # Ensures that an array of id is used in the statement
91
- cluster_ids = cluster_id.is_a?(Array) ? cluster_id : [cluster_id]
92
- else
93
- clusters = cluster_of!(user,organization)
94
- cluster_ids = clusters.map(&:id).compact
95
- end
96
-
97
- scope_klass = scope_klass.select("DISTINCT ON (#{scope.table_name}.id) #{scope.table_name}.id, #{scope.table_name}.*")
98
-
99
- if organization.required_suborganization_mode?
100
- scope_klass = scope_klass.joins(:cluster_entities).where( cluster_entities: { cluster_id: cluster_ids } )
101
- else
102
- or_these_cluster_ids = cluster_ids.present? ? " OR cluster_entities.cluster_id IN (#{cluster_ids.join(",")})" : ""
103
- scope_klass = scope_klass.joins("
104
- LEFT OUTER JOIN cluster_entities
105
- ON entity_id = #{scope.table_name}.id
106
- AND entity_type = '#{scope}'
107
- ").where("cluster_entities.id IS NULL#{or_these_cluster_ids}")
108
- end
109
-
110
- # Provisional fix to avoid unresolved SQL clashes with the main application due to DISTINCT ON clause
111
- scope_klass = scope_class(scope).eager_load(:cluster_entities).where(id: scope_klass.map(&:id), organization_id: organization)
112
- end
113
- scope_klass
114
- end
115
-
116
-
117
- # Untested version for more optimized queries
118
- #
119
- def __cluster_scope!(scope,user,organization,cluster_id=nil,same_scope:false)
120
- scope_klass = scope_class(scope)
121
- if organization.is_in_cluster_mode?
122
- scope_klass = scope_klass.includes(:organization)
123
-
124
- if cluster_id.present?
125
- cluster_ids = cluster_id.is_a?(Array) ? cluster_id : [cluster_id]
126
- else
127
- clusters = cluster_of!(user,organization)
128
- cluster_ids = clusters.map(&:id).compact
129
- end
130
-
131
- scope_klass = scope_klass.select("DISTINCT ON (#{scope.table_name}.id) #{scope.table_name}.id, #{scope.table_name}.*, clusters.*")
132
-
133
- if organization.required_suborganization_mode?
134
- scope_klass = scope_klass.includes(cluster_entities: :cluster).where( cluster_entities: { cluster_id: cluster_ids } )
135
- else
136
- or_these_cluster_ids = cluster_ids.present? ? " OR cluster_entities.cluster_id IN (#{cluster_ids.join(",")})" : ""
137
- scope_klass = scope_klass.joins("
138
- LEFT OUTER JOIN cluster_entities
139
- ON entity_id = #{scope.table_name}.id
140
- AND entity_type = '#{scope}'
141
- ").joins("
142
- LEFT OUTER JOIN clusters
143
- ON clusters.id = cluster_entities.cluster_id
144
- ").where("cluster_entities.id IS NULL#{or_these_cluster_ids}")
145
- end
146
- # Provisional fix to avoid unresolved SQL clashes with the main application due to DISTINCT ON clause
147
- unless same_scope
148
- scope_klass = scope_class(scope).eager_load(:cluster_entities).where(id: scope_klass.map(&:id))
149
- end
150
- end
151
- scope_klass.where(organization_id: organization)
152
- end
153
-
154
14
  ##
155
15
  # TODO:
156
16
  # Implement a validation to ensure that
@@ -163,17 +23,22 @@ module Klastera
163
23
  end
164
24
 
165
25
  ##
166
- # Returns a Cluster::ActiveRecord_Relation if the organization is using the cluster mode.
167
- # Use this only to get current_user/organization clusters
26
+ # Returns which clusters a user can see avoiding unnecessary queries if the cluster restraint doesn't apply
168
27
  #
169
- def cluster_of!(user,organization)
170
- # A cluster user with role Root or Admin retrieve every cluster of its organizations
171
- and_this_user = user.can_admin_clusters? ? nil : user
172
- # We weill return a cluster active record collection that can be filtered
173
- active_record_collection = ::ClusterUser.clusters_of(organization,and_this_user)
174
- # Add a empty cluster instance to handle models without a cluster assignation.
175
- if organization.optional_mode? # For use and show modes only
176
- active_record_collection << ::Cluster.new({nid: UNCLUSTERED_ENTITY, name: I18n.t("klastera.#{UNCLUSTERED_ENTITY}")})
28
+ def cluster_list!(organization,user,include_unclustered=true)
29
+ # Only the cluster mode on and the mandatory user-cluster relation will use a join clause to get the clusters list
30
+ if organization.is_in_cluster_mode? && user.cannot_skip_cluster_clause?
31
+ active_record_collection = ::ClusterUser.clusters_of(organization,user)
32
+ else
33
+ active_record_collection = ::Cluster.where({ organization_id: organization }).order(order: :asc)
34
+ end
35
+
36
+ active_record_collection = active_record_collection.order(order: :asc)
37
+
38
+ if include_unclustered && organization.optional_suborganization_mode? # For show and use modes only
39
+ active_record_collection.append(
40
+ ::Cluster.new({nid: UNCLUSTERED_ENTITY, name: I18n.t("klastera.#{UNCLUSTERED_ENTITY}")}, order: UNCLUSTERED_POSITION )
41
+ )
177
42
  end
178
43
  active_record_collection
179
44
  end
@@ -193,78 +58,139 @@ module Klastera
193
58
  end
194
59
 
195
60
  ##
196
- # cluster_of! needs a user and a organization. that why we perfomed this logic here
61
+ # cluster_list! needs a user and a organization. that why we perfomed this logic here
197
62
  #
198
63
  def user_clusters_string_list!(user,organization,cluster_entities,separator,attribute=:name)
199
- @clusters_session ||= self.cluster_of!(user,organization)
64
+ @clusters_session ||= Klastera.cluster_list!(organization,user)
200
65
  self.entity_clusters_string_list!(cluster_entities, separator, attribute, @clusters_session.map(&:id))
201
66
  end
202
- end
203
67
 
204
- def cluster_user
205
- current_user
206
- end
68
+ #
69
+ # We will try to avoid cluster clause except when:
70
+ # 1.- cluster mode is active
71
+ # AND
72
+ # 2a.- cluster_filter is present (someone wants to filter by cluster)
73
+ # OR
74
+ # 2b.- the current user has some limitations and must checks they cluster relation
75
+ # - User is having clusters in optional_suborganization mode
76
+ # - User IS NOT having clusters in required_suborganization mode
77
+ #
78
+ # For the other hand, with force_cluster_clause we can skip the previous logic if
79
+ # cluster_filter_id is present when the optional_suborganization mode is on. BUT!
80
+ # Be aware that if the cluster_filter is not present, the value of force_cluster_clause
81
+ # will be overridden by the returned value of cannot_skip_cluster_clause? method.
82
+ #
83
+ def cluster_scope!(scope_klass, user, organization, cluster_filter=nil, force_cluster_clause=false)
84
+ scope_klass = scope_class(scope_klass)
85
+ if organization.is_in_cluster_mode? && ( cluster_filter.present? || force_cluster_clause = user.cannot_skip_cluster_clause? ) # yes, this is an assignation
86
+ cluster_ids = []
87
+ # Set another variable as array to get the cluster id(s)
88
+ if cluster_filter.present?
89
+ cluster_ids = cluster_filter.is_a?(Array) ? cluster_filter : [cluster_filter]
90
+ elsif force_cluster_clause
91
+ cluster_ids = ::ClusterUser.clusters_of(organization,user).map(&:id)
92
+ end
93
+ # We will avoid the query unless cluster_ids is having values OR force_cluster_clause is set (see method description)
94
+ if cluster_ids.present? || force_cluster_clause
95
+ scope_klass = scope_klass.eager_load(:organization,cluster_entities: :cluster)
96
+ # We add the unclustered if the value of cluster_filter have the special without_cluster string or as method description says
97
+ if cluster_ids.delete(UNCLUSTERED_ENTITY) || ( force_cluster_clause && organization.optional_suborganization_mode? )
98
+ cluster_ids << nil
99
+ end
100
+ scope_klass = scope_klass.where( cluster_entities: { cluster_id: cluster_ids } )
101
+ end
102
+ end
103
+ scope_klass.where(organization_id: organization)
104
+ end
207
105
 
208
- def cluster_organization
209
- current_organization
210
- end
106
+
107
+ # Filter non-clustered entity through a clusterized one
108
+ #
109
+ def cluster_scope_through_of!(relation, cluster_entity_klass, scope_klass, user, organization, cluster_filter=nil, force_cluster_clause=false)
110
+ unclusterized_scope = scope_class(scope_klass)
211
111
 
212
- def user_has_more_than_one_cluster
213
- @clusters_session ||= cluster_of_my_own
214
- @clusters_session.size > 1
215
- end
112
+ if organization.is_in_cluster_mode? && ( force_cluster_clause || user.cannot_skip_cluster_clause? )
113
+ unclusterized_scope = unclusterized_scope.joins(relation => :cluster_entities)
114
+ end
115
+
116
+ if scope_klass.respond_to?(:organization)
117
+ unclusterized_scope = unclusterized_scope.where(organization_id: organization)
118
+ end
216
119
 
217
- def set_the_lonely_cluster
218
- form_model = @form_record ? model_name_from_record_or_class(@form_record).param_key : params[:controller].singularize
219
- parameters = params.require( form_model ) rescue nil
220
- lonely_cluster = parameters.blank? ? false : parameters.permit( :lonely_cluster ).present?
221
- if lonely_cluster
222
- params[form_model][:cluster_id] = cluster_of_my_own.first.try(:id)
120
+ unclusterized_scope.where("#{relation}_id" => cluster_scope!(cluster_entity_klass, user, organization, cluster_filter, force_cluster_clause))
223
121
  end
224
- end
225
122
 
226
- def set_cluster_filter
227
- @filter = ::ClusterFilter.new(cluster_filter_params)
123
+ #
124
+ # Returns an array with a clusterized scoped result and its grouped version
125
+ #
126
+ def group_by_cluster_scope!(scope_klass, user, organization, cluster_filter=[], scope_scopes=[])
127
+ cluster_ids = cluster_filter.is_a?(Array) ? cluster_filter : [cluster_filter]
128
+ kluster_scope = cluster_scope!(scope_klass, user, organization, cluster_ids, organization.is_in_cluster_mode? )
129
+
130
+ scope_scopes.each do |tuple_scope|
131
+ scope_name, scope_arg = tuple_scope
132
+ kluster_scope = scope_arg.present? ? kluster_scope.send(scope_name,scope_arg) : kluster_scope.send(scope_name)
133
+ end
134
+
135
+ group_by_block = ->(o) {
136
+ if organization.is_in_cluster_mode?
137
+ o.cluster.present? ? o.cluster.name : UNCLUSTERED_POSITION
138
+ else
139
+ I18n.t("klastera.group_by_cluster_scope.#{scope_klass.model_name.plural}")
140
+ end
141
+ }
142
+
143
+ grouped_cluster_scope = kluster_scope.group_by(&group_by_block).sort_by{|k,v|k.to_s}
144
+
145
+ grouped_cluster_scope.dup.each do |group|
146
+ if group.first == UNCLUSTERED_POSITION
147
+ grouped_cluster_scope.delete(group)
148
+ group[0] = I18n.t("klastera.#{UNCLUSTERED_ENTITY}")
149
+ grouped_cluster_scope.append(group)
150
+ end
151
+ end
152
+
153
+ [ kluster_scope, grouped_cluster_scope ]
154
+ end
228
155
  end
229
156
 
230
- def cluster_filter_params
231
- parameters = params.require(:cluster_filter) rescue nil
232
- return {} if parameters.blank?
233
- parameters.permit(*cluster_filter_permit_params)
157
+ ##################################################################################################################################################
158
+
159
+ def cluster_user
160
+ current_user
234
161
  end
235
162
 
236
- def cluster_filter_permit_params
237
- [ :cluster_id ].concat( ::ClusterFilter.attributes )
163
+ def cluster_organization
164
+ current_organization
238
165
  end
239
166
 
240
- def filter_clusterized(active_record_collection,cluster_id)
241
- Klastera.filter_clusterized!(active_record_collection, cluster_id, cluster_organization)
167
+ def set_cluster_filter
168
+ cluster_filter_params = params.require(:cluster_filter) rescue {}
169
+ @cluster_filter = ::ClusterFilter.new(
170
+ cluster_filter_params.present? ? cluster_filter_params.permit(
171
+ [ :cluster_id ].concat( ::ClusterFilter.attributes )
172
+ ) : {}
173
+ )
242
174
  end
243
175
 
244
- def cluster_scope(scope,cluster_id=nil)
245
- Klastera.cluster_scope!(scope, cluster_user, cluster_organization, cluster_id)
176
+ def cluster_list(include_unclustered=true)
177
+ Klastera.cluster_list!(cluster_organization, cluster_user, include_unclustered)
246
178
  end
247
179
 
248
- def __cluster_scope(scope,cluster_id=nil,same_scope:false)
249
- Klastera.__cluster_scope!(scope, cluster_user, cluster_organization, cluster_id, same_scope: same_scope)
180
+ def cluster_scope(scope_klass, cluster_filter=nil, force_cluster_clause=false)
181
+ Klastera.cluster_scope!(scope_klass, cluster_user, cluster_organization, cluster_filter, force_cluster_clause)
250
182
  end
251
183
 
252
- def cluster_of_my_own
253
- Klastera.cluster_of!(cluster_user, cluster_organization)
184
+ def cluster_scope_through_of(relation, cluster_entity_klass, scope_klass, cluster_filter=nil, force_cluster_clause=false)
185
+ Klastera.cluster_scope_through_of!(relation, cluster_entity_klass, scope_klass, cluster_user, cluster_organization, cluster_filter, force_cluster_clause)
254
186
  end
255
187
 
256
- def user_clusters_string_list(object_entity,separator,attribute=:name)
257
- Klastera.user_clusters_string_list!(
258
- cluster_user,
259
- cluster_organization,
260
- object_entity.try(:cluster_entities),
261
- separator,
262
- attribute
263
- )
188
+ def group_by_cluster_scope(scope_klass, cluster_filter=[], scope_scopes=[])
189
+ Klastera.group_by_cluster_scope!(scope_klass, cluster_user, cluster_organization, cluster_filter, scope_scopes)
264
190
  end
265
191
 
266
- def set_collection_before_group_by_entity(active_record_collection,entity_params,&block)
267
- Klastera.set_collection_before_group_by_entity!(active_record_collection, params, cluster_organization, &block)
192
+ def user_clusters_string_list(object_entity, separator, attribute=:name)
193
+ Klastera.user_clusters_string_list!(cluster_user, cluster_organization, object_entity.try(:cluster_entities), separator, attribute)
268
194
  end
269
195
 
270
196
  included do
@@ -277,6 +203,5 @@ module Klastera
277
203
  hide_action("#{helper}=")
278
204
  end
279
205
  end
280
- before_action :set_the_lonely_cluster, only: %i[ create update ]
281
206
  end
282
207
  end
@@ -1,3 +1,3 @@
1
1
  module Klastera
2
- VERSION = "1.3.1"
2
+ VERSION = "1.4.0.2"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: klastera
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.4.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gino Barahona
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-26 00:00:00.000000000 Z
11
+ date: 2020-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -127,7 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
127
127
  - !ruby/object:Gem::Version
128
128
  version: '0'
129
129
  requirements: []
130
- rubygems_version: 3.1.2
130
+ rubygems_version: 3.0.8
131
131
  signing_key:
132
132
  specification_version: 4
133
133
  summary: Clusterization Engine