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 +4 -4
- data/app/models/klastera/concerns/cluster.rb +4 -2
- data/app/models/klastera/concerns/cluster_user.rb +1 -1
- data/app/models/klastera/concerns/clusterizable.rb +3 -2
- data/app/models/klastera/concerns/organization.rb +2 -10
- data/app/models/klastera/concerns/user.rb +7 -4
- data/app/views/layouts/klastera/_cluster_filter.html.erb +2 -2
- data/app/views/layouts/klastera/_cluster_role.html.erb +23 -2
- data/app/views/layouts/klastera/_cluster_selector.html.erb +6 -14
- data/app/views/layouts/klastera/_nested_cluster_entity.html.erb +1 -1
- data/app/views/layouts/klastera/_options.html.erb +2 -3
- data/config/locales/es.yml +21 -14
- data/lib/klastera.rb +125 -200
- data/lib/klastera/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2c6ed8465fe9fcf78e96af1ad4d137ecdc867cc26242e0c4aada31557668b5db
|
|
4
|
+
data.tar.gz: 045d77f5c652c11aa1badd03c404c26b07a04ddd8d7bd7886b72ba47195d9339
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
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,
|
|
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::
|
|
25
|
+
cluster_mode == ::Cluster::REQUIRED_MODE
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def optional_suborganization_mode?
|
|
29
|
-
cluster_mode == ::Cluster::
|
|
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(@
|
|
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 =
|
|
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
|
-
<%
|
|
2
|
-
|
|
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 ||
|
|
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
|
-
|
|
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
|
-
|
|
21
|
-
<%= f.
|
|
22
|
-
|
|
23
|
-
|
|
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 =
|
|
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' %>
|
data/config/locales/es.yml
CHANGED
|
@@ -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
|
|
13
|
-
text: Este rol es sólo para usuarios
|
|
12
|
+
title: Asignación Super Admin
|
|
13
|
+
text: Este rol es sólo para usuarios super admin
|
|
14
14
|
roles:
|
|
15
|
-
root:
|
|
16
|
-
admin: Admin
|
|
17
|
-
user: Usuario
|
|
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:
|
|
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
|
|
27
|
-
required_suborganization:
|
|
28
|
-
optional_suborganization:
|
|
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:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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':
|
data/lib/klastera.rb
CHANGED
|
@@ -5,152 +5,12 @@ module Klastera
|
|
|
5
5
|
|
|
6
6
|
extend ActiveSupport::Concern
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
|
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
|
|
170
|
-
#
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
#
|
|
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 ||=
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
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
|
-
|
|
227
|
-
|
|
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
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
157
|
+
##################################################################################################################################################
|
|
158
|
+
|
|
159
|
+
def cluster_user
|
|
160
|
+
current_user
|
|
234
161
|
end
|
|
235
162
|
|
|
236
|
-
def
|
|
237
|
-
|
|
163
|
+
def cluster_organization
|
|
164
|
+
current_organization
|
|
238
165
|
end
|
|
239
166
|
|
|
240
|
-
def
|
|
241
|
-
|
|
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
|
|
245
|
-
Klastera.
|
|
176
|
+
def cluster_list(include_unclustered=true)
|
|
177
|
+
Klastera.cluster_list!(cluster_organization, cluster_user, include_unclustered)
|
|
246
178
|
end
|
|
247
179
|
|
|
248
|
-
def
|
|
249
|
-
Klastera.
|
|
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
|
|
253
|
-
Klastera.
|
|
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
|
|
257
|
-
Klastera.
|
|
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
|
|
267
|
-
Klastera.
|
|
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
|
data/lib/klastera/version.rb
CHANGED
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.
|
|
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-
|
|
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.
|
|
130
|
+
rubygems_version: 3.0.8
|
|
131
131
|
signing_key:
|
|
132
132
|
specification_version: 4
|
|
133
133
|
summary: Clusterization Engine
|