klastera 1.0.0

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.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +3 -0
  4. data/Rakefile +37 -0
  5. data/app/assets/javascripts/klastera/application.js +13 -0
  6. data/app/assets/javascripts/klastera/clusters.js +2 -0
  7. data/app/assets/stylesheets/klastera/application.css +15 -0
  8. data/app/assets/stylesheets/klastera/clusters.css +4 -0
  9. data/app/assets/stylesheets/scaffold.css +56 -0
  10. data/app/controllers/klastera/application_controller.rb +33 -0
  11. data/app/controllers/klastera/clusters_controller.rb +66 -0
  12. data/app/helpers/klastera/application_helper.rb +40 -0
  13. data/app/helpers/klastera/clusters_helper.rb +4 -0
  14. data/app/models/klastera/cluster.rb +5 -0
  15. data/app/models/klastera/cluster_user.rb +5 -0
  16. data/app/models/klastera/concerns/cluster.rb +78 -0
  17. data/app/models/klastera/concerns/cluster_user.rb +20 -0
  18. data/app/models/klastera/concerns/organization.rb +46 -0
  19. data/app/models/klastera/concerns/transfer.rb +54 -0
  20. data/app/models/klastera/concerns/user.rb +62 -0
  21. data/app/models/klastera/transfer.rb +5 -0
  22. data/app/views/klastera/clusters/_filter.html.erb +0 -0
  23. data/app/views/klastera/clusters/_form.html.erb +22 -0
  24. data/app/views/klastera/clusters/_form_transfer.html.erb +34 -0
  25. data/app/views/klastera/clusters/_table.html.erb +35 -0
  26. data/app/views/klastera/clusters/create.js.erb +1 -0
  27. data/app/views/klastera/clusters/destroy.js.erb +6 -0
  28. data/app/views/klastera/clusters/edit.html.erb +10 -0
  29. data/app/views/klastera/clusters/index.html.erb +21 -0
  30. data/app/views/klastera/clusters/new.html.erb +10 -0
  31. data/app/views/klastera/clusters/transfer.html.erb +10 -0
  32. data/app/views/klastera/clusters/update.js.erb +1 -0
  33. data/app/views/layouts/klastera/_cluster_role.html.erb +26 -0
  34. data/app/views/layouts/klastera/_cluster_user_fields.html.erb +10 -0
  35. data/app/views/layouts/klastera/_nested_cluster_user.html.erb +20 -0
  36. data/app/views/layouts/klastera/_options.html.erb +38 -0
  37. data/config/locales/es.yml +78 -0
  38. data/config/routes.rb +7 -0
  39. data/db/migrate/20200324203929_create_klastera_clusters.rb +12 -0
  40. data/db/migrate/20200326111219_add_cluster_options_to_organizations.rb +20 -0
  41. data/db/migrate/20200330010551_create_klastera_cluster_users.rb +9 -0
  42. data/lib/klastera/engine.rb +5 -0
  43. data/lib/klastera/version.rb +3 -0
  44. data/lib/klastera.rb +5 -0
  45. data/lib/tasks/klastera_tasks.rake +4 -0
  46. data/test/controllers/klastera/clusters_controller_test.rb +52 -0
  47. data/test/dummy/README.rdoc +28 -0
  48. data/test/dummy/Rakefile +6 -0
  49. data/test/dummy/app/assets/javascripts/application.js +13 -0
  50. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  51. data/test/dummy/app/controllers/application_controller.rb +5 -0
  52. data/test/dummy/app/helpers/application_helper.rb +2 -0
  53. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  54. data/test/dummy/bin/bundle +3 -0
  55. data/test/dummy/bin/rails +4 -0
  56. data/test/dummy/bin/rake +4 -0
  57. data/test/dummy/bin/setup +29 -0
  58. data/test/dummy/config/application.rb +26 -0
  59. data/test/dummy/config/boot.rb +5 -0
  60. data/test/dummy/config/database.yml +1 -0
  61. data/test/dummy/config/environment.rb +5 -0
  62. data/test/dummy/config/environments/development.rb +41 -0
  63. data/test/dummy/config/environments/production.rb +79 -0
  64. data/test/dummy/config/environments/test.rb +42 -0
  65. data/test/dummy/config/initializers/assets.rb +11 -0
  66. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  67. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  68. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  69. data/test/dummy/config/initializers/inflections.rb +16 -0
  70. data/test/dummy/config/initializers/mime_types.rb +4 -0
  71. data/test/dummy/config/initializers/session_store.rb +3 -0
  72. data/test/dummy/config/initializers/to_time_preserves_timezone.rb +10 -0
  73. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  74. data/test/dummy/config/locales/en.yml +23 -0
  75. data/test/dummy/config/routes.rb +4 -0
  76. data/test/dummy/config/secrets.yml +22 -0
  77. data/test/dummy/config.ru +4 -0
  78. data/test/dummy/log/development.log +108 -0
  79. data/test/dummy/log/test.log +0 -0
  80. data/test/dummy/public/404.html +67 -0
  81. data/test/dummy/public/422.html +67 -0
  82. data/test/dummy/public/500.html +66 -0
  83. data/test/dummy/public/favicon.ico +0 -0
  84. data/test/dummy/tmp/pids/server.pid +1 -0
  85. data/test/fixtures/klastera/cluster_users.yml +9 -0
  86. data/test/fixtures/klastera/clusters.yml +11 -0
  87. data/test/integration/navigation_test.rb +8 -0
  88. data/test/klastera_test.rb +7 -0
  89. data/test/models/klastera/cluster_test.rb +9 -0
  90. data/test/models/klastera/cluster_user_test.rb +9 -0
  91. data/test/test_helper.rb +21 -0
  92. 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
@@ -0,0 +1,3 @@
1
+ = Klastera
2
+
3
+ This project rocks and uses MIT-LICENSE.
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,2 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.
@@ -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,4 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
@@ -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,4 @@
1
+ module Klastera
2
+ module ClustersHelper
3
+ end
4
+ end
@@ -0,0 +1,5 @@
1
+ module Klastera
2
+ class Cluster < ActiveRecord::Base
3
+ include Klastera::Concerns::Cluster
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Klastera
2
+ class ClusterUser < ActiveRecord::Base
3
+ include Klastera::Concerns::ClusterUser
4
+ end
5
+ 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
@@ -0,0 +1,5 @@
1
+ module Klastera
2
+ class Transfer
3
+ include Klastera::Concerns::Transfer
4
+ end
5
+ 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 %>