klastera 1.3.3.1 → 1.4

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: 7fae7d83f8b939309532cb345a38bc9c4f76bfca455b16ccda6ad17283f6851e
4
- data.tar.gz: f62a4b00a2c526c47b95464263dcca6de69d880b6095fdba41204fd14eff66b2
3
+ metadata.gz: ef7788eb1826bb5f7ee15fe495ee69f181d3c028b4e51250bee6adfe8be4acbc
4
+ data.tar.gz: d703b16d95ef2704ad7fcc01ed7d578f957744e362c2c968edea434eb97c167e
5
5
  SHA512:
6
- metadata.gz: 591f79de0e4332c44454b172eee9aea5491cfbee45ed2d942f67841a6918ff22323d0f0219e15ee60709672de8e190e9d466a822b96eb82b213dfecec5b22647
7
- data.tar.gz: bb1b6e5ec7c8c4795f1662f7d0d9e58a758f0594547bd2ac64408efcb77a02fef103a0f1c349a6243d0023620f2fc23c631761f78ea5ced6a2c9640428b215eb
6
+ metadata.gz: 3927e49fb28dceb261c9400a214f7a84a074e2330425eada935086023e235c25c8c6b530c109a46b6c934a22183200669c2afd7ac481a7090c5249b9580b55d1
7
+ data.tar.gz: 8d812d8f464d26c10c4b97dfcbbcecf03d0ec62171269da18452e75aa91b88d71224300065b5b8b93a5c0c1af319ba008a9f3e600639bb881df42d4b2cb9c4f5
@@ -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
 
@@ -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 %>
@@ -3,6 +3,18 @@
3
3
  <% if @user.try(:organization).try(:is_in_cluster_mode?) || other_visibility_reason %>
4
4
  <div class="form-group">
5
5
  <%= f.label t('klastera.cluster_role') %>
6
+ <span
7
+ class="klastera-user-cluster-rol"
8
+ data-title="<%= t('klastera.help.popover.title')%>"
9
+ data-toggle="popover"
10
+ data-content="<%=
11
+ t('klastera.help.popover.content.cluster_role',
12
+ m1: t('klastera.help.roles.description', r: t('klastera.roles.admin'), d: t('klastera.help.roles.admin')),
13
+ m2: t('klastera.help.roles.description', r: t('klastera.roles.user'), d: t("klastera.help.roles.user.#{@user.organization.optional_suborganization_mode? ? :optional : :required}"))
14
+ )%>"
15
+ >
16
+ <%= fa_icon 'info-circle', class: 'btn-link' %>
17
+ </span>
6
18
  <%= f.select :cluster_role, user_cluster_roles.map{|r|[t("klastera.roles.#{r}"),r]}, { include_blank: true }, { class: 'form-control' } %>
7
19
  </div>
8
20
 
@@ -23,5 +35,7 @@
23
35
  });
24
36
  }
25
37
  });
38
+ var popoverTemplate = '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
39
+ $('.klastera-user-cluster-rol').popover({ template: popoverTemplate, html: true, trigger: 'click', });
26
40
  </script>
27
41
  <% 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,17 @@ 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
+ required: Necesita que se le asigne uno o más clusters, de lo contrario no verá las entidades clusterizadas.
83
+ 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
84
  activemodel:
79
85
  attributes:
80
86
  'klastera/transfer':
@@ -5,154 +5,11 @@ 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 same_cluster_scope
12
- cluster_scope_through_of
13
- ]
8
+ UNCLUSTERED_ENTITY = 'without_cluster'.freeze
9
+ KLSTR_HELPERS = %i[ cluster_user cluster_organization cluster_list cluster_scope cluster_scope_through_of user_clusters_string_list ].freeze
14
10
 
15
11
  class << self
16
12
 
17
- ##
18
- #
19
- #
20
- def set_cluster_entities_attributes!(entity,array_cluster_ids)
21
- cluster_entities_attributes = {}
22
- entity_cluster_entities = entity.try(:cluster_entities) || []
23
- entity_clusters = entity_cluster_entities.map(&:cluster_id).sort
24
- array_cluster_ids = array_cluster_ids.map(&:id).sort
25
- if array_cluster_ids != entity_clusters
26
- now = DateTime.now
27
- entity_cluster_entities.each_with_index do |ec,index|
28
- remove_cluster = true
29
- if array_cluster_ids.include?(ec.cluster_id)
30
- remove_cluster = false
31
- array_cluster_ids.delete(ec.cluster_id)
32
- end
33
- cluster_entities_attributes[index] = { id: ec.id, cluster_id: ec.cluster_id, _destroy: remove_cluster }
34
- end
35
- array_cluster_ids.each_with_index do |cluster_id,index|
36
- cluster_entities_attributes[now.to_i+index] = { cluster_id: cluster_id, _destroy: false }
37
- end
38
- end
39
- cluster_entities_attributes
40
- end
41
-
42
- ##
43
- #
44
- #
45
- def set_collection_before_group_by_entity!(active_record_collection,entity_params,organization,include_nil_by_rule=true)
46
- entity_params_keys = [:entity_name,:entity_attribute,:entity_id,:entity_id_attribute,:unamed]
47
- entity_params[:entity_id] ||= nil #Ensures the entity_id attribute presence even if there is no filter
48
- entity_params = entity_params.slice(*entity_params_keys).values
49
- model_class = active_record_collection.model.base_class
50
- model_relations = model_class.reflections.keys
51
- if model_relations.include?(entity_params[0])
52
- entity_params << "#{entity_params[0]}_id".to_sym
53
- if entity_params[0] == 'cluster'
54
- entity_params << I18n.t("klastera.#{UNCLUSTERED_ENTITY}") if organization.is_in_cluster_mode?
55
- active_record_collection = Klastera.filter_clusterized!(
56
- active_record_collection,
57
- entity_params[2],
58
- organization,
59
- include_nil_by_rule
60
- )
61
- end
62
- yield( active_record_collection, entity_params_keys.zip(entity_params).to_h )
63
- end
64
- end
65
-
66
- #
67
- # In order to this work, the active_record_collection argument should be a cluster_scope! return.
68
- #
69
- def filter_clusterized!(active_record_collection,cluster_id,organization,include_nil_by_rule=true)
70
- if organization.is_in_cluster_mode? && cluster_id.present?
71
- # Ensures that an array of ids is used in the statement
72
- cluster_array = cluster_id.is_a?(Array) ? cluster_id.dup : [cluster_id]
73
- # If we receive a UNCLUSTERED_ENTITY request, it means that we should filter by entities without clusters.
74
- # The optional_mode?(show/use) condition add to this method an ambivalent use,
75
- # where you can filter a unique value or multiple including NULL ones.
76
- cluster_array << nil if include_nil_by_rule || cluster_id == UNCLUSTERED_ENTITY && ( cluster_array.delete(UNCLUSTERED_ENTITY) || organization.optional_mode? )
77
- active_record_collection = active_record_collection.where(cluster_entities: { cluster_id: cluster_array.uniq })
78
- # You should use a block with clusterable data only
79
- yield(active_record_collection) if block_given?
80
- end
81
- active_record_collection
82
- end
83
-
84
- ##
85
- # If the cluster mode is not active, this returns a scope filtered by clusters of its organization/users/cluster
86
- #
87
- def cluster_scope!(scope,user,organization,cluster_id=nil)
88
- scope_klass = scope_class(scope).where(organization_id: organization)
89
- if organization.is_in_cluster_mode?
90
-
91
- if cluster_id.present?
92
- # Ensures that an array of id is used in the statement
93
- cluster_ids = cluster_id.is_a?(Array) ? cluster_id : [cluster_id]
94
- else
95
- clusters = cluster_of!(organization,user)
96
- cluster_ids = clusters.map(&:id).compact
97
- end
98
-
99
- scope_klass = scope_klass.select("DISTINCT ON (#{scope.table_name}.id) #{scope.table_name}.id, #{scope.table_name}.*")
100
-
101
- if organization.required_suborganization_mode?
102
- scope_klass = scope_klass.joins(:cluster_entities).where( cluster_entities: { cluster_id: cluster_ids } )
103
- else
104
- or_these_cluster_ids = cluster_ids.present? ? " OR cluster_entities.cluster_id IN (#{cluster_ids.join(",")})" : ""
105
- scope_klass = scope_klass.joins("
106
- LEFT OUTER JOIN cluster_entities
107
- ON entity_id = #{scope.table_name}.id
108
- AND entity_type = '#{scope}'
109
- ").where("cluster_entities.id IS NULL#{or_these_cluster_ids}")
110
- end
111
-
112
- # Provisional fix to avoid unresolved SQL clashes with the main application due to DISTINCT ON clause
113
- scope_klass = scope_class(scope).eager_load(:cluster_entities).where(id: scope_klass.map(&:id), organization_id: organization)
114
- end
115
- scope_klass
116
- end
117
-
118
-
119
- # Optimized version of cluster_scope!
120
- #
121
- def same_cluster_scope!(scope,user,organization,cluster_id=nil)
122
- scope_klass = scope_class(scope)
123
- if organization.is_in_cluster_mode?
124
- scope_klass = scope_klass.eager_load(:organization,cluster_entities: :cluster)
125
-
126
- if cluster_id.present?
127
- cluster_ids = cluster_id.is_a?(Array) ? cluster_id : [cluster_id]
128
- else
129
- clusters = cluster_of!(organization,user)
130
- cluster_ids = clusters.map(&:id).compact.sort
131
- end
132
-
133
- if organization.required_suborganization_mode?# || cluster_id # If cluster_id is passed NULL clusters are expected, rigth?
134
- scope_klass = scope_klass.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.where("cluster_entities.id IS NULL#{or_these_cluster_ids}")
138
- end
139
- end
140
-
141
- scope_klass.where(organization_id: organization)
142
- end
143
-
144
-
145
- # Filter non-clustered entity through a clusterized one
146
- #
147
- def cluster_scope_through_of!(relation,cluster_entity_klass,scope_klass,user,organization,cluster_id=nil)
148
- scope_class(scope_klass).joins(relation => :cluster_entities).where(
149
- organization_id: organization,
150
- "#{relation}_id" => same_cluster_scope!(
151
- cluster_entity_klass, user, organization, cluster_id
152
- )
153
- )
154
- end
155
-
156
13
  ##
157
14
  # TODO:
158
15
  # Implement a validation to ensure that
@@ -165,18 +22,19 @@ module Klastera
165
22
  end
166
23
 
167
24
  ##
168
- # Returns a Cluster::ActiveRecord_Relation if the organization is using the cluster mode.
169
- # Use this only to get current_user/organization clusters
25
+ # Returns which clusters a user can see avoiding unnecessary queries if the cluster restraint doesn't apply
170
26
  #
171
- def cluster_of!(organization,user)
172
- # A cluster user with role Root or Admin retrieve every cluster of its organizations
173
- # Thats why we pass a nil object to avoid user filter, but if you pass a nil user as argument
174
- # The result is the same as if the user was a admin, BE CAREFUL!
175
- and_this_user = user.try(:can_admin_clusters?) ? nil : user
176
- # We weill return a cluster active record collection that can be filtered
177
- active_record_collection = ::ClusterUser.clusters_of(organization,and_this_user)
178
- # Add a empty cluster instance to handle models without a cluster assignation.
179
- if organization.optional_mode? # For use and show modes only
27
+ def cluster_list!(organization,user,include_unclustered=true)
28
+ # Only the cluster mode on and the mandatory user-cluster relation will use a join clause to get the clusters list
29
+ if organization.is_in_cluster_mode? && user.cannot_skip_cluster_clause?
30
+ active_record_collection = ::ClusterUser.clusters_of(organization,user)
31
+ else
32
+ active_record_collection = ::Cluster.where({ organization_id: organization })
33
+ end
34
+
35
+ active_record_collection = active_record_collection.order(order: :asc)
36
+
37
+ if include_unclustered && organization.optional_suborganization_mode? # For show and use modes only
180
38
  active_record_collection << ::Cluster.new({nid: UNCLUSTERED_ENTITY, name: I18n.t("klastera.#{UNCLUSTERED_ENTITY}")})
181
39
  end
182
40
  active_record_collection
@@ -197,82 +55,124 @@ module Klastera
197
55
  end
198
56
 
199
57
  ##
200
- # cluster_of! needs a user and a organization. that why we perfomed this logic here
58
+ # cluster_list! needs a user and a organization. that why we perfomed this logic here
201
59
  #
202
60
  def user_clusters_string_list!(user,organization,cluster_entities,separator,attribute=:name)
203
- @clusters_session ||= Klastera.cluster_of!(organization,user)
61
+ @clusters_session ||= Klastera.cluster_list!(organization,user)
204
62
  self.entity_clusters_string_list!(cluster_entities, separator, attribute, @clusters_session.map(&:id))
205
63
  end
206
- end
207
64
 
208
- def cluster_user
209
- current_user
210
- end
65
+ #
66
+ # We will try to avoid cluster clause except when:
67
+ # 1.- cluster mode is active
68
+ # AND
69
+ # 2a.- cluster_filter is present (someone wants to filter by cluster)
70
+ # OR
71
+ # 2b.- the current user has some limitations and must checks they cluster relation
72
+ # - User is having clusters in optional_suborganization mode
73
+ # - User IS NOT having clusters in required_suborganization mode
74
+ #
75
+ # For the other hand, with force_cluster_clause we can skip the previous logic if
76
+ # cluster_filter_id is present when the optional_suborganization mode is on. BUT!
77
+ # Be aware that if the cluster_filter is not present, the value of force_cluster_clause
78
+ # will be overridden by the returned value of cannot_skip_cluster_clause? method.
79
+ #
80
+ def cluster_scope!(scope_klass, user, organization, cluster_filter=nil, force_cluster_clause=false)
81
+ scope_klass = scope_class(scope_klass)
82
+ if organization.is_in_cluster_mode? && ( cluster_filter.present? || force_cluster_clause = user.cannot_skip_cluster_clause? ) # yes, this is an assignation
83
+ cluster_ids = []
84
+ # Set another variable as array to get the cluster id(s)
85
+ if cluster_filter.present?
86
+ cluster_ids = cluster_filter.is_a?(Array) ? cluster_filter : [cluster_filter]
87
+ elsif force_cluster_clause
88
+ cluster_ids = ::ClusterUser.clusters_of(organization,user).map(&:id).sort
89
+ end
90
+ # We will avoid the query unless cluster_ids is having values OR force_cluster_clause is set (see method description)
91
+ if cluster_ids.present? || force_cluster_clause
92
+ scope_klass = scope_klass.eager_load(:organization,cluster_entities: :cluster)
93
+ # We add the unclustered if the value of cluster_filter have the special without_cluster string or as method description says
94
+ if cluster_ids.delete(UNCLUSTERED_ENTITY) || ( force_cluster_clause && organization.optional_suborganization_mode? )
95
+ cluster_ids << nil
96
+ end
97
+ scope_klass = scope_klass.where( cluster_entities: { cluster_id: cluster_ids } )
98
+ end
99
+ end
100
+ scope_klass.where(organization_id: organization)
101
+ end
211
102
 
212
- def cluster_organization
213
- current_organization
214
- end
103
+
104
+ # Filter non-clustered entity through a clusterized one
105
+ #
106
+ def cluster_scope_through_of!(relation, cluster_entity_klass, scope_klass, user, organization, cluster_filter=nil, force_cluster_clause=false)
107
+ unclusterized_scope = scope_class(scope_klass)
215
108
 
216
- def user_has_more_than_one_cluster
217
- @clusters_session ||= cluster_of_my_own
218
- @clusters_session.size > 1
219
- end
109
+ if organization.is_in_cluster_mode? && ( force_cluster_clause || user.cannot_skip_cluster_clause? )
110
+ unclusterized_scope = unclusterized_scope.joins(relation => :cluster_entities)
111
+ end
220
112
 
221
- def set_the_lonely_cluster
222
- form_model = @form_record ? model_name_from_record_or_class(@form_record).param_key : params[:controller].singularize
223
- parameters = params.require( form_model ) rescue nil
224
- lonely_cluster = parameters.blank? ? false : parameters.permit( :lonely_cluster ).present?
225
- if lonely_cluster
226
- params[form_model][:cluster_id] = cluster_of_my_own.first.try(:id)
113
+ if scope_klass.respond_to?(:organization)
114
+ unclusterized_scope = unclusterized_scope.where(organization_id: organization)
115
+ end
116
+
117
+ unclusterized_scope.where("#{relation}_id" => cluster_scope!(cluster_entity_klass, user, organization, cluster_filter, force_cluster_clause))
227
118
  end
228
- end
229
119
 
230
- def set_cluster_filter
231
- @filter = ::ClusterFilter.new(cluster_filter_params)
120
+
121
+ # Returns an array with a clusterized scoped result and its grouped version
122
+ #
123
+ def group_by_cluster_scope!(scope_klass, user, organization, cluster_filter=[], scope_scopes=[])
124
+ cluster_ids = cluster_filter.is_a?(Array) ? cluster_filter : [cluster_filter]
125
+ kluster_scope = cluster_scope!(scope_klass, user, organization, cluster_ids, organization.optional_suborganization_mode? )
126
+ scope_scopes.each do |tuple_scope|
127
+ scope_name, scope_arg = tuple_scope
128
+ kluster_scope = scope_arg.present? ? kluster_scope.send(scope_name,scope_arg) : kluster_scope.send(scope_name)
129
+ end
130
+ [
131
+ kluster_scope,
132
+ kluster_scope.order(:cluster_id).group_by do |e|
133
+ e.cluster.present? ? e.cluster.name : I18n.t("klastera.#{UNCLUSTERED_ENTITY}")
134
+ end
135
+ ]
136
+ end
232
137
  end
233
138
 
234
- def cluster_filter_params
235
- parameters = params.require(:cluster_filter) rescue nil
236
- return {} if parameters.blank?
237
- parameters.permit(*cluster_filter_permit_params)
238
- end
139
+ ##################################################################################################################################################
239
140
 
240
- def cluster_filter_permit_params
241
- [ :cluster_id ].concat( ::ClusterFilter.attributes )
141
+ def cluster_user
142
+ current_user
242
143
  end
243
144
 
244
- def filter_clusterized(active_record_collection,cluster_id,include_nil_by_rule=true)
245
- Klastera.filter_clusterized!(active_record_collection, cluster_id, cluster_organization, include_nil_by_rule)
145
+ def cluster_organization
146
+ current_organization
246
147
  end
247
148
 
248
- def cluster_scope(scope,cluster_id=nil)
249
- Klastera.cluster_scope!(scope, cluster_user, cluster_organization, cluster_id)
149
+ def set_cluster_filter
150
+ cluster_filter_params = params.require(:cluster_filter) rescue {}
151
+ @cluster_filter = ::ClusterFilter.new(
152
+ cluster_filter_params.present? ? cluster_filter_params.permit(
153
+ [ :cluster_id ].concat( ::ClusterFilter.attributes )
154
+ ) : {}
155
+ )
250
156
  end
251
157
 
252
- def same_cluster_scope(scope,cluster_id=nil)
253
- Klastera.same_cluster_scope!(scope, cluster_user, cluster_organization, cluster_id)
158
+ def cluster_list(include_unclustered=true)
159
+ Klastera.cluster_list!(cluster_organization, cluster_user, include_unclustered)
254
160
  end
255
161
 
256
- def cluster_scope_through_of(relation,cluster_entity_klass,scope_klass,cluster_id=nil)
257
- Klastera.cluster_scope_through_of!(relation, cluster_entity_klass, scope_klass, cluster_user, cluster_organization, cluster_id )
162
+ def cluster_scope(scope_klass, cluster_filter=nil, force_cluster_clause=false)
163
+ Klastera.cluster_scope!(scope_klass, cluster_user, cluster_organization, cluster_filter, force_cluster_clause)
258
164
  end
259
165
 
260
- def cluster_of_my_own
261
- Klastera.cluster_of!(cluster_organization,cluster_user)
166
+ def cluster_scope_through_of(relation, cluster_entity_klass, scope_klass, cluster_filter=nil, force_cluster_clause=false)
167
+ Klastera.cluster_scope_through_of!(relation, cluster_entity_klass, scope_klass, cluster_user, cluster_organization, cluster_filter, force_cluster_clause)
262
168
  end
263
169
 
264
- def user_clusters_string_list(object_entity,separator,attribute=:name)
265
- Klastera.user_clusters_string_list!(
266
- cluster_user,
267
- cluster_organization,
268
- object_entity.try(:cluster_entities),
269
- separator,
270
- attribute
271
- )
170
+ def group_by_cluster_scope(scope_klass, cluster_filter=[], scope_scopes=[])
171
+ Klastera.group_by_cluster_scope!(scope_klass, cluster_user, cluster_organization, cluster_filter, scope_scopes)
272
172
  end
273
173
 
274
- def set_collection_before_group_by_entity(active_record_collection,entity_params,include_nil_by_rule=true,&block)
275
- Klastera.set_collection_before_group_by_entity!(active_record_collection, params, cluster_organization, include_nil_by_rule, &block)
174
+ def user_clusters_string_list(object_entity, separator, attribute=:name)
175
+ Klastera.user_clusters_string_list!(cluster_user, cluster_organization, object_entity.try(:cluster_entities), separator, attribute)
276
176
  end
277
177
 
278
178
  included do
@@ -285,6 +185,5 @@ module Klastera
285
185
  hide_action("#{helper}=")
286
186
  end
287
187
  end
288
- before_action :set_the_lonely_cluster, only: %i[ create update ]
289
188
  end
290
189
  end
@@ -1,3 +1,3 @@
1
1
  module Klastera
2
- VERSION = "1.3.3.1"
2
+ VERSION = "1.4"
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.3.1
4
+ version: '1.4'
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-07-22 00:00:00.000000000 Z
11
+ date: 2020-07-29 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.0.8
130
+ rubygems_version: 3.1.2
131
131
  signing_key:
132
132
  specification_version: 4
133
133
  summary: Clusterization Engine