klastera 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +3 -0
- data/Rakefile +37 -0
- data/app/assets/javascripts/klastera/application.js +13 -0
- data/app/assets/javascripts/klastera/clusters.js +2 -0
- data/app/assets/stylesheets/klastera/application.css +15 -0
- data/app/assets/stylesheets/klastera/clusters.css +4 -0
- data/app/assets/stylesheets/scaffold.css +56 -0
- data/app/controllers/klastera/application_controller.rb +33 -0
- data/app/controllers/klastera/clusters_controller.rb +66 -0
- data/app/helpers/klastera/application_helper.rb +40 -0
- data/app/helpers/klastera/clusters_helper.rb +4 -0
- data/app/models/klastera/cluster.rb +5 -0
- data/app/models/klastera/cluster_user.rb +5 -0
- data/app/models/klastera/concerns/cluster.rb +78 -0
- data/app/models/klastera/concerns/cluster_user.rb +20 -0
- data/app/models/klastera/concerns/organization.rb +46 -0
- data/app/models/klastera/concerns/transfer.rb +54 -0
- data/app/models/klastera/concerns/user.rb +62 -0
- data/app/models/klastera/transfer.rb +5 -0
- data/app/views/klastera/clusters/_filter.html.erb +0 -0
- data/app/views/klastera/clusters/_form.html.erb +22 -0
- data/app/views/klastera/clusters/_form_transfer.html.erb +34 -0
- data/app/views/klastera/clusters/_table.html.erb +35 -0
- data/app/views/klastera/clusters/create.js.erb +1 -0
- data/app/views/klastera/clusters/destroy.js.erb +6 -0
- data/app/views/klastera/clusters/edit.html.erb +10 -0
- data/app/views/klastera/clusters/index.html.erb +21 -0
- data/app/views/klastera/clusters/new.html.erb +10 -0
- data/app/views/klastera/clusters/transfer.html.erb +10 -0
- data/app/views/klastera/clusters/update.js.erb +1 -0
- data/app/views/layouts/klastera/_cluster_role.html.erb +26 -0
- data/app/views/layouts/klastera/_cluster_user_fields.html.erb +10 -0
- data/app/views/layouts/klastera/_nested_cluster_user.html.erb +20 -0
- data/app/views/layouts/klastera/_options.html.erb +38 -0
- data/config/locales/es.yml +78 -0
- data/config/routes.rb +7 -0
- data/db/migrate/20200324203929_create_klastera_clusters.rb +12 -0
- data/db/migrate/20200326111219_add_cluster_options_to_organizations.rb +20 -0
- data/db/migrate/20200330010551_create_klastera_cluster_users.rb +9 -0
- data/lib/klastera/engine.rb +5 -0
- data/lib/klastera/version.rb +3 -0
- data/lib/klastera.rb +5 -0
- data/lib/tasks/klastera_tasks.rake +4 -0
- data/test/controllers/klastera/clusters_controller_test.rb +52 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +29 -0
- data/test/dummy/config/application.rb +26 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +1 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +41 -0
- data/test/dummy/config/environments/production.rb +79 -0
- data/test/dummy/config/environments/test.rb +42 -0
- data/test/dummy/config/initializers/assets.rb +11 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/to_time_preserves_timezone.rb +10 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +4 -0
- data/test/dummy/config/secrets.yml +22 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/log/development.log +108 -0
- data/test/dummy/log/test.log +0 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/tmp/pids/server.pid +1 -0
- data/test/fixtures/klastera/cluster_users.yml +9 -0
- data/test/fixtures/klastera/clusters.yml +11 -0
- data/test/integration/navigation_test.rb +8 -0
- data/test/klastera_test.rb +7 -0
- data/test/models/klastera/cluster_test.rb +9 -0
- data/test/models/klastera/cluster_user_test.rb +9 -0
- data/test/test_helper.rb +21 -0
- metadata +207 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6cd1ffdd78e48cf6ed10e49ca9c25aec28b85687edfab45128c19e0fe355bbe6
|
4
|
+
data.tar.gz: b973e72ea86f49cdafdb2651470a6cf9ce4285ba255db1a83ecc0efffb3bd980
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 19db0f35f3a830aaea9e0805a4235ee67ef25713f0cdde55675098d18d2d79ed2eefd9bbfc206b8feba2c4cc0df2d87901a5b2eeae97006aade6142fbcd1f79c
|
7
|
+
data.tar.gz: 8a1092f64ada8807fc6f8db2f2903a59a42a30cccb7b399dd151a2e9e274972c57d0d428a8926d2c2c155df49625e81a4b2165c0c9af15e005a802ebd3826495
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2020 Gino Barahona
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Klastera'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
|
21
|
+
load 'rails/tasks/statistics.rake'
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
Bundler::GemHelper.install_tasks
|
26
|
+
|
27
|
+
require 'rake/testtask'
|
28
|
+
|
29
|
+
Rake::TestTask.new(:test) do |t|
|
30
|
+
t.libs << 'lib'
|
31
|
+
t.libs << 'test'
|
32
|
+
t.pattern = 'test/**/*_test.rb'
|
33
|
+
t.verbose = false
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
task default: :test
|
@@ -0,0 +1,13 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require_tree .
|
@@ -0,0 +1,15 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any styles
|
10
|
+
* defined in the other CSS/SCSS files in this directory. It is generally better to create a new
|
11
|
+
* file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require_self
|
15
|
+
*/
|
@@ -0,0 +1,56 @@
|
|
1
|
+
body { background-color: #fff; color: #333; }
|
2
|
+
|
3
|
+
body, p, ol, ul, td {
|
4
|
+
font-family: verdana, arial, helvetica, sans-serif;
|
5
|
+
font-size: 13px;
|
6
|
+
line-height: 18px;
|
7
|
+
}
|
8
|
+
|
9
|
+
pre {
|
10
|
+
background-color: #eee;
|
11
|
+
padding: 10px;
|
12
|
+
font-size: 11px;
|
13
|
+
}
|
14
|
+
|
15
|
+
a { color: #000; }
|
16
|
+
a:visited { color: #666; }
|
17
|
+
a:hover { color: #fff; background-color:#000; }
|
18
|
+
|
19
|
+
div.field, div.actions {
|
20
|
+
margin-bottom: 10px;
|
21
|
+
}
|
22
|
+
|
23
|
+
#notice {
|
24
|
+
color: green;
|
25
|
+
}
|
26
|
+
|
27
|
+
.field_with_errors {
|
28
|
+
padding: 2px;
|
29
|
+
background-color: red;
|
30
|
+
display: table;
|
31
|
+
}
|
32
|
+
|
33
|
+
#error_explanation {
|
34
|
+
width: 450px;
|
35
|
+
border: 2px solid red;
|
36
|
+
padding: 7px;
|
37
|
+
padding-bottom: 0;
|
38
|
+
margin-bottom: 20px;
|
39
|
+
background-color: #f0f0f0;
|
40
|
+
}
|
41
|
+
|
42
|
+
#error_explanation h2 {
|
43
|
+
text-align: left;
|
44
|
+
font-weight: bold;
|
45
|
+
padding: 5px 5px 5px 15px;
|
46
|
+
font-size: 12px;
|
47
|
+
margin: -7px;
|
48
|
+
margin-bottom: 0px;
|
49
|
+
background-color: #c00;
|
50
|
+
color: #fff;
|
51
|
+
}
|
52
|
+
|
53
|
+
#error_explanation ul li {
|
54
|
+
font-size: 12px;
|
55
|
+
list-style: square;
|
56
|
+
}
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Klastera
|
2
|
+
class ApplicationController < ::ApplicationController
|
3
|
+
protect_from_forgery with: :exception
|
4
|
+
layout :disable_in_xhr_requests
|
5
|
+
before_action :set_xhr_render_session
|
6
|
+
before_action :check_access_to_cluster_admin
|
7
|
+
|
8
|
+
def current_organization
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def check_access_to_cluster_admin
|
13
|
+
unless current_user.can_admin_clusters?
|
14
|
+
redirect_to main_app.root_path, flash: { alert: t('klastera.messages.access_cluster_admin') }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
if respond_to?(:skip_after_action)
|
19
|
+
skip_after_action :verify_authorized
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def disable_in_xhr_requests
|
24
|
+
request.xhr? ? false : nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def set_xhr_render_session
|
28
|
+
return unless request.get?
|
29
|
+
# request.xhr? return nil when this is not a XHR request, but we need a boolean
|
30
|
+
session[:xhr_render] = request.xhr?.present?
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require_dependency "klastera/application_controller"
|
2
|
+
|
3
|
+
module Klastera
|
4
|
+
class ClustersController < ApplicationController
|
5
|
+
before_action :set_cluster, only: %i[ edit update destroy transfer ]
|
6
|
+
before_action :set_clusters, only: %i[ index ]
|
7
|
+
|
8
|
+
def index; end
|
9
|
+
|
10
|
+
def new
|
11
|
+
@cluster = ::Cluster.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def edit; end
|
15
|
+
|
16
|
+
def transfer
|
17
|
+
@transfer = Transfer.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def create
|
21
|
+
@cluster = ::Cluster.new(cluster_params)
|
22
|
+
if @cluster.save
|
23
|
+
@cluster = ::Cluster.new
|
24
|
+
set_clusters
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def update
|
29
|
+
if @cluster.update(cluster_params)
|
30
|
+
set_clusters
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def destroy
|
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
|
+
)
|
40
|
+
if @transfer.valid?
|
41
|
+
@transfer.to!(::Cluster.related_entities.map{|re|re.constantize})
|
42
|
+
@cluster.destroy
|
43
|
+
set_clusters
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def set_cluster
|
49
|
+
@cluster = ::Cluster.find(params[:id])
|
50
|
+
end
|
51
|
+
|
52
|
+
###
|
53
|
+
# We are assuming that the main application
|
54
|
+
# has have implemented current_organization
|
55
|
+
###
|
56
|
+
def set_clusters
|
57
|
+
@clusters = current_organization.clusters
|
58
|
+
end
|
59
|
+
|
60
|
+
def cluster_params
|
61
|
+
parameters = params.require(:cluster).permit(:nid, :name, :color)
|
62
|
+
parameters[:organization_id]=current_organization.id
|
63
|
+
parameters
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Klastera
|
2
|
+
module ApplicationHelper
|
3
|
+
def ac_errors_as_html_list(errors)
|
4
|
+
array_as_html_list(
|
5
|
+
errors.messages.inject([]) do |array,(attr,errors_array)|
|
6
|
+
array.concat(errors_array)
|
7
|
+
array
|
8
|
+
end
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
def array_as_html_list(array)
|
13
|
+
"<ul style='padding:0;'><li>#{array.join("</li><li>")}</li></ul>".html_safe
|
14
|
+
end
|
15
|
+
|
16
|
+
def method_missing(method, *args, &block)
|
17
|
+
if method.to_s.end_with?('_path') or method.to_s.end_with?('_url')
|
18
|
+
if main_app.respond_to?(method)
|
19
|
+
main_app.send(method, *args)
|
20
|
+
else
|
21
|
+
super
|
22
|
+
end
|
23
|
+
else
|
24
|
+
super
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def respond_to?(method, include_priv=false)
|
29
|
+
if method.to_s.end_with?('_path') or method.to_s.end_with?('_url')
|
30
|
+
if main_app.respond_to?(method,include_priv)
|
31
|
+
true
|
32
|
+
else
|
33
|
+
super
|
34
|
+
end
|
35
|
+
else
|
36
|
+
super
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Klastera::Concerns::Cluster
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
# 'included do' causes the included code to be evaluated in the
|
4
|
+
# context where it is included (cluster.rb), rather than being
|
5
|
+
# executed in the module's context (klastera/concerns/models/cluster).
|
6
|
+
included do
|
7
|
+
self.table_name = 'clusters'
|
8
|
+
|
9
|
+
attr_reader :last_record
|
10
|
+
|
11
|
+
MODES = [ :suborganization, :required_filter, :optional_filter ].freeze
|
12
|
+
|
13
|
+
default_scope { includes(:organization).order(id: :desc) }
|
14
|
+
belongs_to :organization, class_name: Klastera.organization_class
|
15
|
+
|
16
|
+
scope :all_clusters_of, lambda { |organization,except_ids=[]|
|
17
|
+
g=where(organization: organization)
|
18
|
+
g.where.not(id: except_ids) unless except_ids.blank?
|
19
|
+
}
|
20
|
+
|
21
|
+
validates :name, presence: true
|
22
|
+
validates :nid, presence: true
|
23
|
+
validates :organization_id, presence: true
|
24
|
+
|
25
|
+
validates_uniqueness_of :nid, scope: [:nid, :organization_id]
|
26
|
+
|
27
|
+
before_destroy do |record|
|
28
|
+
self.can_transfer_and_destroy?
|
29
|
+
end
|
30
|
+
|
31
|
+
def siblings
|
32
|
+
self.class.all_clusters_of(self.organization,[self.id])
|
33
|
+
end
|
34
|
+
|
35
|
+
def is_the_last_record_in_suborganization_mode?
|
36
|
+
self.organization.suborganization_mode? && self.siblings.blank?
|
37
|
+
end
|
38
|
+
|
39
|
+
def required_transfer?
|
40
|
+
self.organization.suborganization_mode?
|
41
|
+
end
|
42
|
+
|
43
|
+
def has_related_entities_using_it?
|
44
|
+
::Cluster.total_records_assign_to(self) > 0
|
45
|
+
end
|
46
|
+
|
47
|
+
def can_transfer_and_destroy?
|
48
|
+
can_destroy = true
|
49
|
+
if is_the_last_record_in_suborganization_mode?
|
50
|
+
errors.add(:last_record, I18n.t('klastera.messages.cant_delete_the_last_record_in_suborganization_mode'))
|
51
|
+
can_destroy = false
|
52
|
+
end
|
53
|
+
can_destroy
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
module ClassMethods
|
58
|
+
def related_entities(attr_needed: :class_name, macro: :has_many)
|
59
|
+
::Cluster.reflections.map do |association_name, reflection|
|
60
|
+
reflection.send(attr_needed) if reflection.macro == macro
|
61
|
+
end.compact
|
62
|
+
end
|
63
|
+
|
64
|
+
def total_records_assign_to(cluster_instance)
|
65
|
+
related_entities(attr_needed: :name).inject(0) do |total, association_name|
|
66
|
+
entity = cluster_instance.send(association_name)
|
67
|
+
if entity.respond_to?(:cluster_id)
|
68
|
+
total+=entity.where(cluster_id: cluster_instance.id).count
|
69
|
+
end
|
70
|
+
total
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def modes_as_strings
|
75
|
+
::Cluster::MODES.map{|m|m.to_s}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Klastera::Concerns::ClusterUser
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
self.table_name = 'cluster_users'
|
6
|
+
|
7
|
+
belongs_to :user
|
8
|
+
belongs_to :cluster
|
9
|
+
|
10
|
+
validates :user_id, presence: true
|
11
|
+
validates :cluster_id, presence: true
|
12
|
+
|
13
|
+
before_destroy do |record|
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Klastera::Concerns::Organization
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
included do
|
4
|
+
|
5
|
+
has_many :clusters
|
6
|
+
|
7
|
+
validates :use_cluster_as, inclusion: { in: ::Cluster.modes_as_strings , message: I18n.t('klastera.clusters.wrong_option') }, if: proc{ use_cluster_as.present? }
|
8
|
+
|
9
|
+
# Return a symbol version of use_cluster_as value
|
10
|
+
def cluster_mode
|
11
|
+
self.use_cluster_as.to_s.to_sym
|
12
|
+
end
|
13
|
+
|
14
|
+
##
|
15
|
+
# Return a boolean if one of three of options was set in organization
|
16
|
+
# As useless option you can retrieve the value passing false as argument.
|
17
|
+
#
|
18
|
+
##
|
19
|
+
def is_in_cluster_mode?(return_the_mode=false)
|
20
|
+
is_active = cluster_mode.present? && ::Cluster::MODES.include?(cluster_mode)
|
21
|
+
( return_the_mode ? ( is_active ? cluster_mode : false ) : is_active )
|
22
|
+
end
|
23
|
+
|
24
|
+
def suborganization_mode?
|
25
|
+
cluster_mode == ::Cluster::MODES.first
|
26
|
+
end
|
27
|
+
|
28
|
+
def required_filter_mode?
|
29
|
+
cluster_mode == ::Cluster::MODES.second
|
30
|
+
end
|
31
|
+
|
32
|
+
def optional_filter_mode?
|
33
|
+
cluster_mode == ::Cluster::MODES.third
|
34
|
+
end
|
35
|
+
|
36
|
+
def cluster_is_required?
|
37
|
+
self.suborganization_mode? || self.required_filter_mode?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
module ClassMethods
|
42
|
+
def cluster_params
|
43
|
+
[ :use_cluster_as ]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Klastera::Concerns::Transfer
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
include ActiveModel::Model
|
8
|
+
include ActiveModel::Validations::Callbacks
|
9
|
+
|
10
|
+
attr_accessor :current_cluster, :new_cluster_id, :entities_transfered
|
11
|
+
|
12
|
+
validates :current_cluster, presence: true
|
13
|
+
validates :new_cluster_id, presence: true, if: proc { self.required_transfer? }
|
14
|
+
|
15
|
+
validate do
|
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
|
+
#
|
24
|
+
if current_cluster.class.name != 'Cluster' || current_cluster.try(:is_the_last_record_in_suborganization_mode?)
|
25
|
+
errors.add(:current_cluster, I18n.t('klastera.messages.current_cluster.cant_transfer'))
|
26
|
+
elsif self.required_transfer? && new_cluster_id.present? && new_cluster.nil?
|
27
|
+
errors.add(:new_cluster_id, I18n.t('klastera.messages.new_cluster_id.nil'))
|
28
|
+
elsif current_cluster.id == new_cluster.try(:id)
|
29
|
+
errors.add(:new_cluster_id, I18n.t('klastera.messages.new_cluster_id.same'))
|
30
|
+
elsif new_cluster.present? && current_cluster.organization_id != new_cluster.organization_id
|
31
|
+
# Clusters from another organization do not exist
|
32
|
+
errors.add(:new_cluster_id, I18n.t('klastera.messages.new_cluster_id.nil'))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
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
|
+
def required_transfer?
|
51
|
+
self.current_cluster.required_transfer? && self.current_cluster.has_related_entities_using_it?
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Klastera::Concerns::User
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
|
6
|
+
## @@current_organization = nil
|
7
|
+
|
8
|
+
CLUSTER_ROOT = 'root'.freeze
|
9
|
+
CLUSTER_ADMIN = 'admin'.freeze
|
10
|
+
CLUSTER_USER = 'user'.freeze
|
11
|
+
|
12
|
+
CLUSTER_ROLES = [ CLUSTER_ROOT, CLUSTER_ADMIN, CLUSTER_USER ].freeze
|
13
|
+
|
14
|
+
has_many :cluster_users, inverse_of: :user, class_name: ::ClusterUser.to_s
|
15
|
+
accepts_nested_attributes_for :cluster_users, reject_if: :all_blank, allow_destroy: true
|
16
|
+
validates :cluster_role, presence: true, if: proc{ self.organization.is_in_cluster_mode? }
|
17
|
+
validates :cluster_role, inclusion: { in: CLUSTER_ROLES , message: I18n.t('klastera.clusters.wrong_option') }, if: proc{ cluster_role.present? }
|
18
|
+
validate :at_least_one_role, if: proc{ self.use_cluster? && self.need_cluster_assignation? }
|
19
|
+
before_destroy do |record|
|
20
|
+
end
|
21
|
+
|
22
|
+
def use_cluster?; self.cluster_role.present?; end
|
23
|
+
|
24
|
+
def is_a_cluster_root?; self.cluster_role == CLUSTER_ROOT; end
|
25
|
+
def is_a_cluster_admin?; self.cluster_role == CLUSTER_ADMIN; end
|
26
|
+
def is_a_cluster_user?; self.cluster_role == CLUSTER_USER; end
|
27
|
+
|
28
|
+
def is_not_a_cluster_root?; ! self.is_a_cluster_root?; end
|
29
|
+
def is_not_a_cluster_admin?; ! self.is_a_cluster_admin?; end
|
30
|
+
def is_not_a_cluster_user?; ! self.is_a_cluster_user?; end
|
31
|
+
|
32
|
+
def need_cluster_assignation?
|
33
|
+
self.organization.is_in_cluster_mode? && self.organization.suborganization_mode? && self.is_a_cluster_user?
|
34
|
+
end
|
35
|
+
|
36
|
+
def can_admin_clusters?
|
37
|
+
self.organization.is_in_cluster_mode? && ( self.is_a_cluster_admin? || self.is_a_cluster_root? )
|
38
|
+
end
|
39
|
+
|
40
|
+
# def __current_organization
|
41
|
+
# @@current_organization
|
42
|
+
# end
|
43
|
+
|
44
|
+
# def __current_organization=(organization)
|
45
|
+
# @@current_organization = organization
|
46
|
+
# end
|
47
|
+
|
48
|
+
private
|
49
|
+
def at_least_one_role
|
50
|
+
if cluster_users.none?
|
51
|
+
errors.add(:base, I18n.t('klastera.user.you_need_add_at_least_one_cluster'))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
module ClassMethods
|
58
|
+
def cluster_params
|
59
|
+
[ :cluster_role, { cluster_users_attributes: [:id, :cluster_id, :_destroy] } ]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
File without changes
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<%= simple_form_for( @cluster, remote: true, html: { autocomplete: :off, class: 'slim-form-field' } ) do |f| %>
|
2
|
+
<%=render 'layouts/remote_form/header', o: @cluster, title: title, fa: :qrcode %>
|
3
|
+
|
4
|
+
<div class="<%=classes_for_remote_modal_body()%>">
|
5
|
+
<div class="row">
|
6
|
+
<div class="col-xs-12">
|
7
|
+
<%= f.input :name, as: :string, label: t('klastera.cluster_name') %>
|
8
|
+
</div>
|
9
|
+
<div class="col-xs-12">
|
10
|
+
<%= f.input :nid, as: :string, label: t('klastera.cluster_nid') %>
|
11
|
+
</div>
|
12
|
+
<%# <div class="col-xs-12"> %>
|
13
|
+
<%# <%= f.input :organization_id, as: :string, label: t('klastera.organization_name') %>
|
14
|
+
<%# </div> %>
|
15
|
+
<div class="col-xs-12">
|
16
|
+
<%= f.input :color, as: :string, label: t('klastera.cluster_color'), input_html: { class: 'color-picker'} %>
|
17
|
+
</div>
|
18
|
+
</div>
|
19
|
+
</div>
|
20
|
+
|
21
|
+
<%=render 'layouts/remote_form/footer', o: @cluster %>
|
22
|
+
<% end %>
|
@@ -0,0 +1,34 @@
|
|
1
|
+
<% can_transfer_and_destroy = @cluster.can_transfer_and_destroy? %>
|
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
|
+
<%=render 'layouts/remote_form/header', o: @cluster, title: title, fa: :qrcode %>
|
4
|
+
<div class="<%=classes_for_remote_modal_body()%>">
|
5
|
+
<h4 class="text-center">Esta acción es irreversible.<br/>¿Está seguro?</h4>
|
6
|
+
<% if ::Cluster.total_records_assign_to(@cluster) > 0 %>
|
7
|
+
<br />
|
8
|
+
<div class="row">
|
9
|
+
<% if can_transfer_and_destroy %>
|
10
|
+
<div class="col-xs-12">
|
11
|
+
<div class="alert alert-<%=current_organization.suborganization_mode? ? :warning : :info %> text-center">
|
12
|
+
<i class="fa fa-2x fa-<%=current_organization.suborganization_mode? ? :warning : 'info-circle' %> float-none"></i>
|
13
|
+
<p class="margin-top-5 fa-1-2x"><%=t("klastera.clusters.transfer.#{current_organization.suborganization_mode? ? :required : :optional}")%></p>
|
14
|
+
</div>
|
15
|
+
</div>
|
16
|
+
<div class="col-xs-12">
|
17
|
+
<%= f.input :new_cluster_id, collection: @cluster.siblings.map{|s|[s.name,s.id]}, label: t('klastera.clusters.siblings') %>
|
18
|
+
</div>
|
19
|
+
<% else %>
|
20
|
+
<div class="col-xs-12">
|
21
|
+
<div class="alert alert-danger text-center no-margin">
|
22
|
+
<i class="fa fa-2x fa-times-circle float-none"></i>
|
23
|
+
<br />
|
24
|
+
<div class="fa-1-2x">
|
25
|
+
<%=ac_errors_as_html_list(@cluster.errors)%>
|
26
|
+
</div>
|
27
|
+
</div>
|
28
|
+
</div>
|
29
|
+
<% end %>
|
30
|
+
</div>
|
31
|
+
<% end %>
|
32
|
+
</div>
|
33
|
+
<%=render 'layouts/remote_form/footer', o: @cluster, btn_type: :danger, hide_submit_button: (! can_transfer_and_destroy ) %>
|
34
|
+
<% end %>
|