klastera 1.3.3.1 → 1.4.1

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: f45dc24f8870c8dc07dc2e7702f6021b66b5c71b97529ca4aece12ce47399348
4
+ data.tar.gz: 1c8673aaef5fb0d562376e80cdf787e302601672e4a4285066f3be4da04fb925
5
5
  SHA512:
6
- metadata.gz: 591f79de0e4332c44454b172eee9aea5491cfbee45ed2d942f67841a6918ff22323d0f0219e15ee60709672de8e190e9d466a822b96eb82b213dfecec5b22647
7
- data.tar.gz: bb1b6e5ec7c8c4795f1662f7d0d9e58a758f0594547bd2ac64408efcb77a02fef103a0f1c349a6243d0023620f2fc23c631761f78ea5ced6a2c9640428b215eb
6
+ metadata.gz: c59493dadf8ac3a4b52a235f3a4f96c52cef9668ec215bf9f857bad90c72241ad8564fe11c86f3a212587fba37440c87230487ce10da9bdd09519be907a433ca
7
+ data.tar.gz: 838347a85ffdfb1144dad6cf83094ff11fa9a8d78398e70f382a65e51df31d80bc557b3022175e7d13a8ddcbd88567f5c8af1b8a7ca6056194f475ed5d57b12d
@@ -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 }.merge(and_user_id) ).order(order: :asc)
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,154 +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 same_cluster_scope
12
- cluster_scope_through_of
13
- ]
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 cluster_scope_left_join ].freeze
14
11
 
15
12
  class << self
16
13
 
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
14
  ##
157
15
  # TODO:
158
16
  # Implement a validation to ensure that
@@ -165,19 +23,22 @@ module Klastera
165
23
  end
166
24
 
167
25
  ##
168
- # Returns a Cluster::ActiveRecord_Relation if the organization is using the cluster mode.
169
- # 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
170
27
  #
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
180
- 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
+ )
181
42
  end
182
43
  active_record_collection
183
44
  end
@@ -197,82 +58,163 @@ module Klastera
197
58
  end
198
59
 
199
60
  ##
200
- # 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
201
62
  #
202
63
  def user_clusters_string_list!(user,organization,cluster_entities,separator,attribute=:name)
203
- @clusters_session ||= Klastera.cluster_of!(organization,user)
64
+ @clusters_session ||= Klastera.cluster_list!(organization,user)
204
65
  self.entity_clusters_string_list!(cluster_entities, separator, attribute, @clusters_session.map(&:id))
205
66
  end
206
- end
207
67
 
208
- def cluster_user
209
- current_user
210
- 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
211
105
 
212
- def cluster_organization
213
- current_organization
214
- 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)
215
111
 
216
- def user_has_more_than_one_cluster
217
- @clusters_session ||= cluster_of_my_own
218
- @clusters_session.size > 1
219
- 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
220
115
 
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)
116
+ if scope_klass.respond_to?(:organization)
117
+ unclusterized_scope = unclusterized_scope.where(organization_id: organization)
118
+ end
119
+
120
+ unclusterized_scope.where("#{relation}_id" => cluster_scope!(cluster_entity_klass, user, organization, cluster_filter, force_cluster_clause))
227
121
  end
228
- end
229
122
 
230
- def set_cluster_filter
231
- @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
155
+
156
+ #
157
+ # A helper that returns a CLUSTER SCOPE to build queries that need explicit LEFT OUTER JOIN clause,
158
+ # instead of the default INNER JOIN provide by ActiveRecord's joins method
159
+ #
160
+ def cluster_scope_left_join!(scope_klass,organization)
161
+ scope_klass_arel_table = scope_klass.arel_table
162
+ cluster_entities_arel_table = Klastera::ClusterEntity.arel_table
163
+
164
+ scope_klass_cluster_entities = scope_klass_arel_table.join(cluster_entities_arel_table, Arel::Nodes::OuterJoin).on(
165
+ scope_klass_arel_table[:id].eq(cluster_entities_arel_table[:entity_id]), cluster_entities_arel_table[:entity_type].eq(scope_klass.name)
166
+ ).join_sources
167
+
168
+ cluster_arel_table = ::Cluster.arel_table
169
+ cluster_entities_cluster = cluster_entities_arel_table.join(cluster_arel_table, Arel::Nodes::OuterJoin).on(
170
+ cluster_entities_arel_table[:cluster_id].eq(cluster_arel_table[:id]),
171
+ ).join_sources
172
+
173
+ scope_class(scope_klass).where(organization_id: organization).joins(scope_klass_cluster_entities).joins(cluster_entities_cluster)
174
+ end
232
175
  end
233
176
 
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)
177
+ ##################################################################################################################################################
178
+
179
+ def cluster_user
180
+ current_user
238
181
  end
239
182
 
240
- def cluster_filter_permit_params
241
- [ :cluster_id ].concat( ::ClusterFilter.attributes )
183
+ def cluster_organization
184
+ current_organization
242
185
  end
243
186
 
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)
187
+ def set_cluster_filter
188
+ cluster_filter_params = params.require(:cluster_filter) rescue {}
189
+ @cluster_filter = ::ClusterFilter.new(
190
+ cluster_filter_params.present? ? cluster_filter_params.permit(
191
+ [ :cluster_id ].concat( ::ClusterFilter.attributes )
192
+ ) : {}
193
+ )
246
194
  end
247
195
 
248
- def cluster_scope(scope,cluster_id=nil)
249
- Klastera.cluster_scope!(scope, cluster_user, cluster_organization, cluster_id)
196
+ def cluster_list(include_unclustered=true)
197
+ Klastera.cluster_list!(cluster_organization, cluster_user, include_unclustered)
250
198
  end
251
199
 
252
- def same_cluster_scope(scope,cluster_id=nil)
253
- Klastera.same_cluster_scope!(scope, cluster_user, cluster_organization, cluster_id)
200
+ def cluster_scope(scope_klass, cluster_filter=nil, force_cluster_clause=false)
201
+ Klastera.cluster_scope!(scope_klass, cluster_user, cluster_organization, cluster_filter, force_cluster_clause)
254
202
  end
255
203
 
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 )
204
+ def cluster_scope_through_of(relation, cluster_entity_klass, scope_klass, cluster_filter=nil, force_cluster_clause=false)
205
+ Klastera.cluster_scope_through_of!(relation, cluster_entity_klass, scope_klass, cluster_user, cluster_organization, cluster_filter, force_cluster_clause)
258
206
  end
259
207
 
260
- def cluster_of_my_own
261
- Klastera.cluster_of!(cluster_organization,cluster_user)
208
+ def group_by_cluster_scope(scope_klass, cluster_filter=[], scope_scopes=[])
209
+ Klastera.group_by_cluster_scope!(scope_klass, cluster_user, cluster_organization, cluster_filter, scope_scopes)
262
210
  end
263
211
 
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
- )
212
+ def user_clusters_string_list(object_entity, separator, attribute=:name)
213
+ Klastera.user_clusters_string_list!(cluster_user, cluster_organization, object_entity.try(:cluster_entities), separator, attribute)
272
214
  end
273
215
 
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)
216
+ def cluster_scope_left_join(scope_klass)
217
+ Klastera.cluster_scope_left_join!(scope_klas,cluster_organization)
276
218
  end
277
219
 
278
220
  included do
@@ -285,6 +227,5 @@ module Klastera
285
227
  hide_action("#{helper}=")
286
228
  end
287
229
  end
288
- before_action :set_the_lonely_cluster, only: %i[ create update ]
289
230
  end
290
231
  end
@@ -1,3 +1,3 @@
1
1
  module Klastera
2
- VERSION = "1.3.3.1"
2
+ VERSION = "1.4.1"
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.1
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-08-05 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