klastera 1.2.4.2 → 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/assets/stylesheets/klastera/clusters.css.scss +36 -1
- data/app/controllers/klastera/clusters_controller.rb +4 -6
- data/app/models/klastera/concerns/cluster.rb +7 -22
- data/app/models/klastera/concerns/cluster_filter.rb +1 -4
- data/app/models/klastera/concerns/cluster_user.rb +20 -36
- data/app/models/klastera/concerns/clusterizable.rb +2 -5
- data/app/models/klastera/concerns/organization.rb +3 -11
- data/app/models/klastera/concerns/transfer.rb +13 -22
- data/app/models/klastera/concerns/user.rb +7 -4
- data/app/views/klastera/clusters/_form.html.erb +4 -4
- data/app/views/klastera/clusters/_form_transfer.html.erb +2 -2
- data/app/views/klastera/clusters/_table.html.erb +4 -12
- data/app/views/klastera/clusters/index.html.erb +1 -1
- data/app/views/layouts/klastera/_cluster_entity_fields.html.erb +1 -1
- 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 +2 -2
- data/app/views/layouts/klastera/_options.html.erb +3 -20
- data/config/locales/es.yml +20 -14
- data/lib/klastera.rb +108 -199
- data/lib/klastera/version.rb +1 -1
- data/lib/tasks/klastera_tasks.rake +5 -0
- metadata +2 -2
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,4 +8,39 @@
|
|
8
8
|
background: none;
|
9
9
|
}
|
10
10
|
}
|
11
|
-
}
|
11
|
+
}
|
12
|
+
|
13
|
+
#cluster-remote-table {
|
14
|
+
.cluster-id { width: 70px; }
|
15
|
+
.cluster-order { width: 70px; }
|
16
|
+
.cluster-color { width: 70px; }
|
17
|
+
.odd .colorless { color: #F9F9F9 }
|
18
|
+
.even .colorless { color: #FFF }
|
19
|
+
tr:hover .colorless { color: #444; opacity: 0.2;}
|
20
|
+
}
|
21
|
+
|
22
|
+
#cluster-remote-form {
|
23
|
+
h4.blank-modal-title {
|
24
|
+
margin: 0;
|
25
|
+
line-height: 28px;
|
26
|
+
letter-spacing: 0.05rem;
|
27
|
+
font-size: 21px;
|
28
|
+
font-weight: 400;
|
29
|
+
color: #666;
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
|
34
|
+
ul.klastera-option-help {
|
35
|
+
padding-left: 20px;
|
36
|
+
list-style-type: disc;
|
37
|
+
}
|
38
|
+
|
39
|
+
ul.klastera-option-help li {
|
40
|
+
margin-bottom: 10px;
|
41
|
+
padding-bottom: 5px;
|
42
|
+
border-bottom: 1px solid #DDD;
|
43
|
+
}
|
44
|
+
|
45
|
+
ul.klastera-option-help li b { color: #8A8A8A; font-weight: 400; }
|
46
|
+
ul.klastera-option-help li b:first-child { color: #2E5F9B; font-weight: bold; }
|
@@ -33,13 +33,11 @@ module Klastera
|
|
33
33
|
|
34
34
|
def destroy
|
35
35
|
new_cluster_id = params.require(:transfer).permit(:new_cluster_id).values.first rescue nil
|
36
|
-
@transfer = Transfer.new(
|
37
|
-
current_cluster: @cluster,
|
38
|
-
new_cluster_id: new_cluster_id
|
39
|
-
)
|
36
|
+
@transfer = Transfer.new( current_cluster: @cluster, new_cluster_id: new_cluster_id )
|
40
37
|
if @transfer.valid?
|
41
|
-
@transfer.
|
42
|
-
|
38
|
+
if @transfer.apply!
|
39
|
+
@cluster.destroy
|
40
|
+
end
|
43
41
|
set_clusters
|
44
42
|
end
|
45
43
|
end
|
@@ -8,9 +8,13 @@ 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
|
16
|
+
has_many :cluster_users
|
17
|
+
has_many :cluster_entities, dependent: :destroy
|
14
18
|
|
15
19
|
scope :of, -> (organization,except_ids=[]) {
|
16
20
|
_scope = where(organization: organization)
|
@@ -41,7 +45,7 @@ module Klastera::Concerns::Cluster
|
|
41
45
|
end
|
42
46
|
|
43
47
|
def has_related_entities_using_it?
|
44
|
-
|
48
|
+
self.cluster_entities.size > 0
|
45
49
|
end
|
46
50
|
|
47
51
|
def can_transfer_and_destroy?
|
@@ -54,29 +58,10 @@ module Klastera::Concerns::Cluster
|
|
54
58
|
end
|
55
59
|
|
56
60
|
def display_name_nid
|
57
|
-
"#{name} (#{nid})"
|
61
|
+
"#{name} (#{nid==Klastera::UNCLUSTERED_ENTITY ? I18n.t("klastera.#{ Klastera::UNCLUSTERED_ENTITY}") : nid})"
|
58
62
|
end
|
59
63
|
end
|
60
64
|
|
61
65
|
module ClassMethods
|
62
|
-
def related_entities(attr_needed: :class_name, macro: :has_many)
|
63
|
-
::Cluster.reflections.map do |association_name, reflection|
|
64
|
-
reflection.send(attr_needed) if reflection.macro == macro
|
65
|
-
end.compact
|
66
|
-
end
|
67
|
-
|
68
|
-
def total_records_assign_to(cluster_instance)
|
69
|
-
related_entities(attr_needed: :name).inject(0) do |total, association_name|
|
70
|
-
entity = cluster_instance.send(association_name)
|
71
|
-
if entity.respond_to?(:cluster_id)
|
72
|
-
total+=entity.where(cluster_id: cluster_instance.id).count
|
73
|
-
end
|
74
|
-
total
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def modes_as_strings
|
79
|
-
::Cluster::MODES.map{|m|m.to_s}
|
80
|
-
end
|
81
66
|
end
|
82
67
|
end
|
@@ -4,11 +4,8 @@ module Klastera::Concerns::ClusterFilter
|
|
4
4
|
included do
|
5
5
|
include ActiveModel::Model
|
6
6
|
include ActiveModel::Validations::Callbacks
|
7
|
-
attr_accessor :cluster_id
|
8
7
|
|
9
|
-
|
10
|
-
self.cluster_id == 'without_cluster' ? nil : self.cluster_id
|
11
|
-
end
|
8
|
+
attr_accessor :cluster_id
|
12
9
|
end
|
13
10
|
|
14
11
|
module ClassMethods
|
@@ -3,55 +3,39 @@ module Klastera::Concerns::ClusterUser
|
|
3
3
|
|
4
4
|
included do
|
5
5
|
self.table_name = 'cluster_users'
|
6
|
-
|
7
6
|
belongs_to :user
|
8
7
|
belongs_to :cluster
|
9
|
-
|
10
8
|
validates :cluster_id, presence: true
|
11
|
-
|
12
|
-
scope :of, -> (user,organization) {
|
13
|
-
Rails.logger.warn("DON'T USE THIS SCOPE DIRECTLY: Use ::ClusterUser.clusters_from class method instead!")
|
14
|
-
includes(:cluster).where(user_id: user, "clusters.organization_id": organization)
|
15
|
-
}
|
16
9
|
end
|
17
10
|
|
18
11
|
module ClassMethods
|
12
|
+
##
|
13
|
+
# Return a Cluster::ActiveRecord_Relation of organization (and) user
|
14
|
+
#
|
15
|
+
def clusters_of(organization,and_user=nil)
|
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) )
|
18
|
+
end
|
19
19
|
|
20
20
|
##
|
21
|
-
|
22
|
-
|
23
|
-
def
|
24
|
-
|
25
|
-
organization_cluster_ids = ::Cluster.of(organization).map(&:id)
|
26
|
-
::ClusterUser.includes(:user).where(cluster_id: organization_cluster_ids).each do |cluster_user|
|
27
|
-
user_id = cluster_user.user_id || :uncluster
|
28
|
-
users_hash[user_id] ||= []
|
29
|
-
users_hash[user_id] << cluster_user.cluster_id
|
30
|
-
end
|
31
|
-
users_hash
|
21
|
+
# Return a User::ActiveRecord_Relation of organization (and) user
|
22
|
+
#
|
23
|
+
def users_of(organization)
|
24
|
+
::User.eager_load(:cluster_users).where(users: { organization_id: organization } )
|
32
25
|
end
|
33
26
|
|
34
27
|
##
|
35
|
-
#
|
28
|
+
# Return a hash of users and its clusters
|
36
29
|
#
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
# TODO: resolve it without quering twice
|
45
|
-
clusters_id = ::ClusterUser.of(user,organization).map(&:cluster_id)
|
46
|
-
clusters = ::Cluster.where(id: clusters_id)
|
47
|
-
# Add a empty cluster instance to handle models without a cluster assignation. Only for use and show modes
|
48
|
-
if organization.optional_mode?
|
49
|
-
clusters << ::Cluster.new({nid: :without_cluster, name: I18n.t('klastera.without_cluster')})
|
50
|
-
end
|
51
|
-
else
|
52
|
-
clusters = ::Cluster.of(organization)
|
30
|
+
def users_hash_of(organization)
|
31
|
+
users = {}
|
32
|
+
rows = self.users_of(organization).pluck("users.id AS user_id","cluster_users.cluster_id").uniq
|
33
|
+
rows.each do |row|
|
34
|
+
user_id = row.first
|
35
|
+
users[user_id] ||= []
|
36
|
+
users[user_id] << row.last
|
53
37
|
end
|
54
|
-
|
38
|
+
users
|
55
39
|
end
|
56
40
|
end
|
57
41
|
end
|
@@ -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,9 +10,7 @@ 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
|
|
14
|
-
scope :
|
15
|
-
joins(:cluster_entities).where("cluster_entities.entity_id = #{self.table_name}.id")
|
16
|
-
}
|
13
|
+
scope :includes_cluster, -> { includes(cluster_entities: :cluster) }
|
17
14
|
|
18
15
|
def at_least_one_cluster_entity
|
19
16
|
if cluster_entities.length == 0 || cluster_entities.reject{|cluster_entity| cluster_entity._destroy == true}.empty?
|
@@ -47,7 +44,7 @@ module Klastera::Concerns::Clusterizable
|
|
47
44
|
|
48
45
|
module ClassMethods
|
49
46
|
def cluster_entity_params
|
50
|
-
[ :cluster_id,
|
47
|
+
[ :cluster_id, { cluster_entities_attributes: [:id, :cluster_id, :_destroy] } ]
|
51
48
|
end
|
52
49
|
end
|
53
50
|
end
|
@@ -4,7 +4,7 @@ module Klastera::Concerns::Organization
|
|
4
4
|
|
5
5
|
has_many :clusters
|
6
6
|
|
7
|
-
validates :use_cluster_as, inclusion: { in: ::Cluster.
|
7
|
+
validates :use_cluster_as, inclusion: { in: ::Cluster::MODES.map{|m|m.to_s}, message: I18n.t('klastera.clusters.wrong_option') }, if: proc{ use_cluster_as.present? }
|
8
8
|
|
9
9
|
# Return a symbol version of use_cluster_as value
|
10
10
|
def cluster_mode
|
@@ -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
|
|
@@ -7,20 +7,14 @@ module Klastera::Concerns::Transfer
|
|
7
7
|
include ActiveModel::Model
|
8
8
|
include ActiveModel::Validations::Callbacks
|
9
9
|
|
10
|
-
attr_accessor :current_cluster, :new_cluster_id
|
10
|
+
attr_accessor :current_cluster, :new_cluster_id
|
11
11
|
|
12
12
|
validates :current_cluster, presence: true
|
13
13
|
validates :new_cluster_id, presence: true, if: proc { self.required_transfer? }
|
14
14
|
|
15
15
|
validate do
|
16
16
|
new_cluster = ::Cluster.find(self.new_cluster_id.to_i) rescue nil
|
17
|
-
|
18
|
-
# In my time, to_a? didnt work
|
19
|
-
# current_cluster.class returned:
|
20
|
-
# Cluster(id: integer, name: string, nid: text, organization_id: integer, created_at: datetime, updated_at: datetime, color: string)
|
21
|
-
#
|
22
|
-
# If you see this, please fixe it. Thanks
|
23
|
-
#
|
17
|
+
|
24
18
|
if current_cluster.class.name != 'Cluster' || current_cluster.try(:is_the_last_record_in_required_suborganization_mode?)
|
25
19
|
errors.add(:current_cluster, I18n.t('klastera.messages.current_cluster.cant_transfer'))
|
26
20
|
elsif self.required_transfer? && new_cluster_id.present? && new_cluster.nil?
|
@@ -33,22 +27,19 @@ module Klastera::Concerns::Transfer
|
|
33
27
|
end
|
34
28
|
end
|
35
29
|
|
36
|
-
##
|
37
|
-
#
|
38
|
-
#
|
39
|
-
def to!(related_entities)
|
40
|
-
self.entities_transfered = 0
|
41
|
-
related_entities.each do |entity|
|
42
|
-
if entity.is_a?(Class) && entity.respond_to?(:cluster_id)
|
43
|
-
self.entities_transfered += entity.where(
|
44
|
-
cluster_id: self.current_cluster.id
|
45
|
-
).update_all(cluster_id: self.new_cluster_id)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
30
|
def required_transfer?
|
51
31
|
self.current_cluster.required_transfer? && self.current_cluster.has_related_entities_using_it?
|
52
32
|
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# A returned boolean is expected. It should always be true even nothing is
|
36
|
+
# transfered, and it only will return false if creation fails.
|
37
|
+
#
|
38
|
+
def apply!
|
39
|
+
Klastera::ClusterEntity.create(current_cluster.cluster_entities.map{ |relation|
|
40
|
+
next if self.new_cluster_id.blank?
|
41
|
+
{ cluster_id: self.new_cluster_id, entity_id: relation.entity_id, entity_type: relation.entity_type }
|
42
|
+
}.compact)
|
43
|
+
end
|
53
44
|
end
|
54
45
|
end
|
@@ -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)
|
@@ -3,16 +3,16 @@
|
|
3
3
|
|
4
4
|
<div class="<%=classes_for_remote_modal_body()%>">
|
5
5
|
<div class="row">
|
6
|
-
<div class="col-xs-
|
6
|
+
<div class="col-xs-6">
|
7
7
|
<%= f.input :name, as: :string, label: t('klastera.cluster_name') %>
|
8
8
|
</div>
|
9
|
-
<div class="col-xs-
|
9
|
+
<div class="col-xs-6">
|
10
10
|
<%= f.input :nid, as: :string, label: t('klastera.cluster_nid') %>
|
11
11
|
</div>
|
12
|
-
<div class="col-xs-
|
12
|
+
<div class="col-xs-6">
|
13
13
|
<%= f.input :color, as: :string, label: t('klastera.cluster_color'), input_html: { class: 'color-picker'} %>
|
14
14
|
</div>
|
15
|
-
<div class="col-xs-
|
15
|
+
<div class="col-xs-6">
|
16
16
|
<%= f.input :order, as: :string, label: t('klastera.cluster_order') %>
|
17
17
|
</div>
|
18
18
|
</div>
|
@@ -2,8 +2,8 @@
|
|
2
2
|
<%= simple_form_for( @transfer, url: cluster_path(@cluster), method: :delete, remote: true, html: { autocomplete: :off, class: 'destroy-form slim-form-field' } ) do |f| %>
|
3
3
|
<%=render 'layouts/remote_form/header', o: @cluster, title: title, fa: :qrcode %>
|
4
4
|
<div class="<%=classes_for_remote_modal_body()%>">
|
5
|
-
<h4 class="text-center">Esta acción es irreversible
|
6
|
-
<% if
|
5
|
+
<h4 class="blank-modal-title text-center">Esta acción es irreversible<br/>¿Está seguro?</h4>
|
6
|
+
<% if @cluster.cluster_entities.size > 0 %>
|
7
7
|
<br />
|
8
8
|
<div class="row">
|
9
9
|
<% if can_transfer_and_destroy %>
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<table class="table table-striped table-hover table-condensed table-bordered datatable">
|
1
|
+
<table class="table table-striped table-hover table-condensed table-bordered datatable table">
|
2
2
|
<thead>
|
3
3
|
<tr>
|
4
4
|
<th class="text-center cluster-id"><%=t('klastera.cluster_id')%></th>
|
@@ -6,7 +6,7 @@
|
|
6
6
|
<th class="text-center cluster-color"><%=t('klastera.cluster_color')%></th>
|
7
7
|
<th class="text-center cluster-name"><%=t('klastera.cluster_name')%></th>
|
8
8
|
<th class="text-center cluster-nid"><%=t('klastera.cluster_nid')%></th>
|
9
|
-
<th class="cogs-actions
|
9
|
+
<th class="cogs-actions two-icons"><span class="fa fa-cogs"></span></th>
|
10
10
|
</tr>
|
11
11
|
</thead>
|
12
12
|
<tbody>
|
@@ -19,7 +19,7 @@
|
|
19
19
|
</td>
|
20
20
|
<td class="text-center cluster-name"><%= cluster.name %></td>
|
21
21
|
<td class="text-center cluster-nid"><%= cluster.nid %></td>
|
22
|
-
<td class="cogs-actions
|
22
|
+
<td class="cogs-actions two-icons">
|
23
23
|
<%= link_to edit_cluster_path(cluster), class:'btn btn-primary btn-xs', title: t('shared.actions.edit'), "data-toggle": "modal", "data-target": "#remote-modal-block" do %>
|
24
24
|
<span class="fa fa-pencil-square-o"></span>
|
25
25
|
<% end %>
|
@@ -30,12 +30,4 @@
|
|
30
30
|
</tr>
|
31
31
|
<% end %>
|
32
32
|
</tbody>
|
33
|
-
</table>
|
34
|
-
<style type="text/css">
|
35
|
-
.cluster-id { width: 70px; }
|
36
|
-
.cluster-order { width: 70px; }
|
37
|
-
.cluster-color { width: 70px; }
|
38
|
-
.odd .colorless { color: #F9F9F9 }
|
39
|
-
.even .colorless { color: #FFF }
|
40
|
-
tr:hover .colorless { color: #F5F5F5 }
|
41
|
-
</style>
|
33
|
+
</table>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<tr class="nested-fields">
|
2
2
|
<td class="field">
|
3
|
-
<%= f.select :cluster_id, @
|
3
|
+
<%= f.select :cluster_id, @clusters_session.map{|c|[c.name,c.id]}, { include_blank: true }, { class: 'form-control' } %>
|
4
4
|
</td>
|
5
5
|
<td class="action vertical-align-middle text-center" width="44">
|
6
6
|
<%= link_to_remove_association f, class: 'btn btn-danger btn-xs text-danger' do %>
|
@@ -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 = @
|
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
|
-
<% @
|
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)%>">
|
@@ -27,7 +27,7 @@
|
|
27
27
|
<table id="cluster-entities" class="table table-striped">
|
28
28
|
<tbody class="cluster-entity-rows">
|
29
29
|
<%= f.fields_for :cluster_entities do |cluster_entity|%>
|
30
|
-
<% next unless @
|
30
|
+
<% next unless @clusters_session.map(&:id).include?(cluster_entity.try(:object).try(:cluster_id)) %>
|
31
31
|
<%= render 'layouts/klastera/cluster_entity_fields', f: cluster_entity %>
|
32
32
|
<% end %>
|
33
33
|
</body>
|
@@ -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' %>
|
@@ -19,20 +18,4 @@
|
|
19
18
|
<script type="text/javascript">
|
20
19
|
var popoverTemplate = '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
|
21
20
|
$('.klastera-cluster-option').popover({ template: popoverTemplate, html: true, trigger: 'click', });
|
22
|
-
</script>
|
23
|
-
|
24
|
-
<style type="text/css">
|
25
|
-
ul.klastera-option-help {
|
26
|
-
padding-left: 20px;
|
27
|
-
list-style-type: disc;
|
28
|
-
}
|
29
|
-
|
30
|
-
ul.klastera-option-help li {
|
31
|
-
margin-bottom: 10px;
|
32
|
-
padding-bottom: 5px;
|
33
|
-
border-bottom: 1px solid #DDD;
|
34
|
-
}
|
35
|
-
|
36
|
-
ul.klastera-option-help li b { color: #8A8A8A; font-weight: 400; }
|
37
|
-
ul.klastera-option-help li b:first-child { color: #2E5F9B; font-weight: bold; }
|
38
|
-
</style>
|
21
|
+
</script>
|
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,140 +5,11 @@ module Klastera
|
|
5
5
|
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
cluster_scope cluster_clusters cluster_scope_filtered clusters_from
|
11
|
-
user_clusters_string_list set_collection_before_group_by_entity
|
12
|
-
]
|
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
|
13
10
|
|
14
11
|
class << self
|
15
12
|
|
16
|
-
def set_cluster_entities_attributes!(entity,array_cluster_ids)
|
17
|
-
cluster_entities_attributes = {}
|
18
|
-
entity_cluster_entities = entity.try(:cluster_entities) || []
|
19
|
-
entity_clusters = entity_cluster_entities.map(&:cluster_id).sort
|
20
|
-
array_cluster_ids = array_cluster_ids.map(&:id).sort
|
21
|
-
if array_cluster_ids != entity_clusters
|
22
|
-
now = DateTime.now
|
23
|
-
entity_cluster_entities.each_with_index do |ec,index|
|
24
|
-
remove_cluster = true
|
25
|
-
if array_cluster_ids.include?(ec.cluster_id)
|
26
|
-
remove_cluster = false
|
27
|
-
array_cluster_ids.delete(ec.cluster_id)
|
28
|
-
end
|
29
|
-
cluster_entities_attributes[index] = { id: ec.id, cluster_id: ec.cluster_id, _destroy: remove_cluster }
|
30
|
-
end
|
31
|
-
array_cluster_ids.each_with_index do |cluster_id,index|
|
32
|
-
cluster_entities_attributes[now.to_i+index] = { cluster_id: cluster_id, _destroy: false }
|
33
|
-
end
|
34
|
-
end
|
35
|
-
cluster_entities_attributes
|
36
|
-
end
|
37
|
-
|
38
|
-
#
|
39
|
-
# In cases you don't have the active_record_collection object to filter, this method helps you
|
40
|
-
# calling cluster_scope! before and pass the collction to filter_clusterized_collection_with!,
|
41
|
-
# but at the end of the day, I just like to wrap methods.
|
42
|
-
#
|
43
|
-
def cluster_scope_filtered!(scope,cluster_id,user,organization)
|
44
|
-
self.filter_clusterized_collection_with!(
|
45
|
-
cluster_id,
|
46
|
-
self.cluster_scope!(scope,user,organization),
|
47
|
-
organization
|
48
|
-
)
|
49
|
-
end
|
50
|
-
|
51
|
-
#
|
52
|
-
# In order to this works, active_record_collection argument
|
53
|
-
# should be passed through cluster_scope! method before.
|
54
|
-
#
|
55
|
-
def filter_clusterized_collection_with!(cluster_id,active_record_collection,cluster_organization)
|
56
|
-
if cluster_organization.is_in_cluster_mode?
|
57
|
-
|
58
|
-
# IMPORTANT
|
59
|
-
# The next block was commented on because cluster_scope! method is not returning
|
60
|
-
# the cluster_entities within the scope anymore and it doesn't make any sense trying
|
61
|
-
# to filter based on force/use/show logic. Nevertheless, someday we will need it again.
|
62
|
-
|
63
|
-
# # If cluster_id is nil we will try to filter including unclustered entities active_record_collection wsas
|
64
|
-
# unless cluster_id.present?
|
65
|
-
# # Based on force/use/show definition we don't really need this validation. A force cluster organization won't return an entity that
|
66
|
-
# # doesn't belong to a cluster. Nevertheless we don't know if active_record_collection argument is fully definition-compliant.
|
67
|
-
# if cluster_organization.optional_suborganization_mode?
|
68
|
-
# cluster_array << nil
|
69
|
-
# end
|
70
|
-
# end
|
71
|
-
|
72
|
-
if cluster_id.present?
|
73
|
-
cluster_array = [cluster_id] unless cluster_id.is_a?(Array)
|
74
|
-
# The ActiveRecordCollection argument should have previously eager-loaded the cluster entities, thus the join statement is unnecessary.
|
75
|
-
active_record_collection = active_record_collection.joins(:cluster_entities).where("cluster_entities.cluster_id": cluster_array)
|
76
|
-
end
|
77
|
-
|
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
|
-
#
|
86
|
-
#
|
87
|
-
##
|
88
|
-
def set_collection_before_group_by_entity!(active_record_collection,entity_params,cluster_organization)
|
89
|
-
entity_params_keys = [:entity_name,:entity_attribute,:entity_id,:entity_id_attribute,:unamed]
|
90
|
-
entity_params[:entity_id] ||= nil #Ensures the entity_id attribute presence even if there is no filter
|
91
|
-
entity_params = entity_params.slice(*entity_params_keys).values
|
92
|
-
model_class = active_record_collection.model.base_class
|
93
|
-
model_relations = model_class.reflections.keys
|
94
|
-
if model_relations.include?(entity_params[0])
|
95
|
-
entity_params << "#{entity_params[0]}_id".to_sym
|
96
|
-
if entity_params[0] == 'cluster'
|
97
|
-
entity_params << :unclustered if cluster_organization.is_in_cluster_mode?
|
98
|
-
active_record_collection = Klastera.filter_clusterized_collection_with!(
|
99
|
-
entity_params[2],
|
100
|
-
active_record_collection,
|
101
|
-
cluster_organization
|
102
|
-
)
|
103
|
-
end
|
104
|
-
yield( active_record_collection, entity_params_keys.zip(entity_params).to_h )
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
##
|
109
|
-
# Returns a ::Cluster::ActiveRecord_Relation from a given scope
|
110
|
-
#
|
111
|
-
def clusters_from!(scope,user,organization)
|
112
|
-
cluster_scope!(scope,user,organization).related_clusters
|
113
|
-
end
|
114
|
-
|
115
|
-
##
|
116
|
-
# Returns a scope filtered by clusters or its
|
117
|
-
# organization if the cluster mode is not active.
|
118
|
-
#
|
119
|
-
def cluster_scope!(scope,user,organization)
|
120
|
-
scope_klass = scope_class(scope).where(organization_id: organization)
|
121
|
-
session_clusters(user,organization) do |clusters|
|
122
|
-
if organization.is_in_cluster_mode?
|
123
|
-
scope_klass = scope_klass.select("DISTINCT ON (#{scope.table_name}.id) #{scope.table_name}.id, #{scope.table_name}.*")
|
124
|
-
cluster_ids = clusters.map(&:id)
|
125
|
-
if organization.required_suborganization_mode?
|
126
|
-
scope_klass = scope_klass.joins(:cluster_entities).where( cluster_entities: { cluster_id: cluster_ids } )
|
127
|
-
else
|
128
|
-
or_these_cluster_ids = cluster_ids.present? ? " OR cluster_entities.cluster_id IN (#{cluster_ids.join(",")})" : ""
|
129
|
-
scope_klass = scope_klass.joins("
|
130
|
-
LEFT OUTER JOIN cluster_entities
|
131
|
-
ON entity_id = #{scope.table_name}.id
|
132
|
-
AND entity_type = '#{scope}'
|
133
|
-
").where("cluster_entities.id IS NULL#{or_these_cluster_ids}")
|
134
|
-
end
|
135
|
-
# Provisional fix to avoid SQL clashes due to DISTINCT ON clause
|
136
|
-
scope_klass = scope_class(scope).eager_load(:cluster_entities).where(id: scope_klass.map(&:id), organization_id: organization)
|
137
|
-
end
|
138
|
-
end
|
139
|
-
scope_klass
|
140
|
-
end
|
141
|
-
|
142
13
|
##
|
143
14
|
# TODO:
|
144
15
|
# Implement a validation to ensure that
|
@@ -151,19 +22,22 @@ module Klastera
|
|
151
22
|
end
|
152
23
|
|
153
24
|
##
|
154
|
-
# Returns a
|
155
|
-
#
|
156
|
-
# Use this only to get current_user/organization clusters
|
157
|
-
# understanding this wont be useful out of a cluster mode context.
|
25
|
+
# Returns which clusters a user can see avoiding unnecessary queries if the cluster restraint doesn't apply
|
158
26
|
#
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
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 })
|
165
33
|
end
|
166
|
-
|
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
|
38
|
+
active_record_collection << ::Cluster.new({nid: UNCLUSTERED_ENTITY, name: I18n.t("klastera.#{UNCLUSTERED_ENTITY}")})
|
39
|
+
end
|
40
|
+
active_record_collection
|
167
41
|
end
|
168
42
|
|
169
43
|
##
|
@@ -181,88 +55,124 @@ module Klastera
|
|
181
55
|
end
|
182
56
|
|
183
57
|
##
|
184
|
-
#
|
185
|
-
# that why, we perfomed this logic here
|
58
|
+
# cluster_list! needs a user and a organization. that why we perfomed this logic here
|
186
59
|
#
|
187
60
|
def user_clusters_string_list!(user,organization,cluster_entities,separator,attribute=:name)
|
188
|
-
@
|
189
|
-
self.entity_clusters_string_list!(cluster_entities, separator, attribute, @
|
61
|
+
@clusters_session ||= Klastera.cluster_list!(organization,user)
|
62
|
+
self.entity_clusters_string_list!(cluster_entities, separator, attribute, @clusters_session.map(&:id))
|
190
63
|
end
|
191
|
-
end
|
192
64
|
|
193
|
-
|
194
|
-
|
195
|
-
|
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
|
196
102
|
|
197
|
-
|
198
|
-
|
199
|
-
|
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)
|
200
108
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
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
|
112
|
+
|
113
|
+
if scope_klass.respond_to?(:organization)
|
114
|
+
unclusterized_scope = unclusterized_scope.where(organization_id: organization)
|
115
|
+
end
|
205
116
|
|
206
|
-
|
207
|
-
form_model = @form_record ? model_name_from_record_or_class(@form_record).param_key : params[:controller].singularize
|
208
|
-
parameters = params.require( form_model ) rescue nil
|
209
|
-
lonely_cluster = parameters.blank? ? false : parameters.permit( :lonely_cluster ).present?
|
210
|
-
if lonely_cluster
|
211
|
-
params[form_model][:cluster_id] = cluster_clusters.first.try(:id)
|
117
|
+
unclusterized_scope.where("#{relation}_id" => cluster_scope!(cluster_entity_klass, user, organization, cluster_filter, force_cluster_clause))
|
212
118
|
end
|
213
|
-
end
|
214
119
|
|
215
|
-
|
216
|
-
|
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
|
217
137
|
end
|
218
138
|
|
219
|
-
|
220
|
-
parameters = params.require(:cluster_filter) rescue nil
|
221
|
-
return {} if parameters.blank?
|
222
|
-
parameters.permit(*cluster_filter_permit_params)
|
223
|
-
end
|
139
|
+
##################################################################################################################################################
|
224
140
|
|
225
|
-
def
|
226
|
-
|
141
|
+
def cluster_user
|
142
|
+
current_user
|
227
143
|
end
|
228
144
|
|
229
|
-
def
|
230
|
-
|
145
|
+
def cluster_organization
|
146
|
+
current_organization
|
231
147
|
end
|
232
148
|
|
233
|
-
def
|
234
|
-
|
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
|
+
)
|
235
156
|
end
|
236
157
|
|
237
|
-
def
|
238
|
-
Klastera.
|
158
|
+
def cluster_list(include_unclustered=true)
|
159
|
+
Klastera.cluster_list!(cluster_organization, cluster_user, include_unclustered)
|
239
160
|
end
|
240
161
|
|
241
|
-
def
|
242
|
-
Klastera.
|
243
|
-
scope,
|
244
|
-
cluster_id,
|
245
|
-
cluster_user,
|
246
|
-
cluster_organization
|
247
|
-
)
|
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)
|
248
164
|
end
|
249
165
|
|
250
|
-
def
|
251
|
-
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)
|
252
168
|
end
|
253
169
|
|
254
|
-
def
|
255
|
-
Klastera.
|
256
|
-
cluster_user,
|
257
|
-
cluster_organization,
|
258
|
-
object_entity.try(:cluster_entities),
|
259
|
-
separator,
|
260
|
-
attribute
|
261
|
-
)
|
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)
|
262
172
|
end
|
263
173
|
|
264
|
-
def
|
265
|
-
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)
|
266
176
|
end
|
267
177
|
|
268
178
|
included do
|
@@ -275,6 +185,5 @@ module Klastera
|
|
275
185
|
hide_action("#{helper}=")
|
276
186
|
end
|
277
187
|
end
|
278
|
-
before_action :set_the_lonely_cluster, only: %i[ create update ]
|
279
188
|
end
|
280
189
|
end
|
data/lib/klastera/version.rb
CHANGED
@@ -13,6 +13,11 @@ namespace :klastera do
|
|
13
13
|
klass = args.entity.constantize
|
14
14
|
ActiveRecord::Base.transaction do
|
15
15
|
klass.where.not(cluster_id: nil).each do |entity|
|
16
|
+
if entity.cluster.blank?
|
17
|
+
puts "Cluster ID #{entity.cluster_id} was not found!"
|
18
|
+
puts "skip..."
|
19
|
+
next
|
20
|
+
end
|
16
21
|
Klastera::ClusterEntity.create(entity: entity, cluster: entity.cluster)
|
17
22
|
end
|
18
23
|
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.
|
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-
|
11
|
+
date: 2020-07-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|