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 +4 -4
- data/app/models/klastera/concerns/cluster.rb +4 -2
- 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 +14 -0
- 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 +20 -14
- data/lib/klastera.rb +105 -206
- 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: ef7788eb1826bb5f7ee15fe495ee69f181d3c028b4e51250bee6adfe8be4acbc
|
4
|
+
data.tar.gz: d703b16d95ef2704ad7fcc01ed7d578f957744e362c2c968edea434eb97c167e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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,
|
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 %>
|
@@ -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 ||
|
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,17 @@ 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
|
+
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':
|
data/lib/klastera.rb
CHANGED
@@ -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
|
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
|
172
|
-
#
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
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
|
-
#
|
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.
|
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
|
-
|
209
|
-
|
210
|
-
|
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
|
-
|
213
|
-
|
214
|
-
|
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
|
-
|
217
|
-
|
218
|
-
|
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
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
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
|
-
|
231
|
-
|
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
|
-
|
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
|
241
|
-
|
141
|
+
def cluster_user
|
142
|
+
current_user
|
242
143
|
end
|
243
144
|
|
244
|
-
def
|
245
|
-
|
145
|
+
def cluster_organization
|
146
|
+
current_organization
|
246
147
|
end
|
247
148
|
|
248
|
-
def
|
249
|
-
|
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
|
253
|
-
Klastera.
|
158
|
+
def cluster_list(include_unclustered=true)
|
159
|
+
Klastera.cluster_list!(cluster_organization, cluster_user, include_unclustered)
|
254
160
|
end
|
255
161
|
|
256
|
-
def
|
257
|
-
Klastera.
|
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
|
261
|
-
Klastera.
|
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
|
265
|
-
Klastera.
|
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
|
275
|
-
Klastera.
|
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
|
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'
|
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-
|
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.
|
130
|
+
rubygems_version: 3.1.2
|
131
131
|
signing_key:
|
132
132
|
specification_version: 4
|
133
133
|
summary: Clusterization Engine
|