diff --git a/Gemfile b/Gemfile index 03d0975..d1d60da 100644 --- a/Gemfile +++ b/Gemfile @@ -7,9 +7,9 @@ gem 'rails', '3.2.6' # gem 'pg' gem 'mysql2' -gem 'devise', '~> 2.0.4' +gem 'devise', '~> 2.1.0' gem 'cancan', '~> 1.6.7' -gem 'squeel', '~> 0.9.3' +gem 'squeel', '~> 1.0.0' gem 'sentient_user', '~> 0.3.2' gem 'userstamp_basic', '~> 0.1.0' gem 'validates_hostname', '~> 1.0.0', git: 'https://github.com/KimNorgaard/validates_hostname.git' @@ -23,7 +23,7 @@ gem 'simple_form', '~> 2.0.0' gem 'concerned_with', '~> 0.1.0' gem 'navigasmic', '~> 0.5.6', git: 'https://github.com/jejacks0n/navigasmic.git' gem 'rails-backbone', '~> 0.7.0' -gem 'acts_as_nested_interval', '~> 0.0.5' +gem 'acts_as_nested_interval', '~> 0.0.7' # path: '/home/clyfe/dev/acts_as_nested_interval' # git: 'https://github.com/clyfe/acts_as_nested_interval.git' @@ -41,8 +41,8 @@ end gem 'jquery-rails' gem 'dalli', '~> 1.1.3' # gem 'foreigner' ? -gem 'active_scaffold', '~> 3.2.12' -# git: 'https://github.com/activescaffold/active_scaffold.git' +gem 'active_scaffold', '~> 3.2.12', + git: 'https://github.com/activescaffold/active_scaffold.git' # path: '/home/clyfe/dev/active_scaffold' gem 'pjax_rails', '~> 0.2.0' diff --git a/Gemfile.lock b/Gemfile.lock index 478b4c0..0ba1144 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,6 +4,13 @@ GIT specs: validates_hostname (1.0.0) +GIT + remote: https://github.com/activescaffold/active_scaffold.git + revision: 0c01d763cfce3f910368491e672e2a7f493d916e + specs: + active_scaffold (3.2.12) + rails (>= 3.1.3) + GIT remote: https://github.com/jejacks0n/navigasmic.git revision: 1ffe437f279657c6fb87bb4b0215eb723df4ea7a @@ -29,8 +36,6 @@ GEM active-model-email-validator (1.0.2) activemodel mail - active_scaffold (3.2.12) - rails (>= 3.1.3) activemodel (3.2.6) activesupport (= 3.2.6) builder (~> 3.0.0) @@ -45,7 +50,7 @@ GEM activesupport (3.2.6) i18n (~> 0.6) multi_json (~> 1.0) - acts_as_nested_interval (0.0.5) + acts_as_nested_interval (0.0.7) rails (~> 3.2.1) addressable (2.2.8) arel (3.0.2) @@ -88,11 +93,11 @@ GEM daemons (1.0.10) dalli (1.1.5) database_cleaner (0.7.2) - devise (2.0.4) + devise (2.1.2) bcrypt-ruby (~> 3.0) - orm_adapter (~> 0.0.3) + orm_adapter (~> 0.1) railties (~> 3.1) - warden (~> 1.1.1) + warden (~> 1.2.1) diff-lcs (1.1.3) ejs (1.0.0) erubis (2.7.0) @@ -126,11 +131,12 @@ GEM railties (>= 3.2.0, < 5.0) thor (~> 0.14) json (1.7.3) - libnotify (0.7.3) + libnotify (0.7.4) + ffi (~> 1.0.11) libv8 (3.3.10.4) libwebsocket (0.1.3) addressable - listen (0.4.5) + listen (0.4.6) rb-fchange (~> 0.0.5) rb-fsevent (~> 0.9.1) rb-inotify (~> 0.8.8) @@ -156,12 +162,12 @@ GEM activerecord (>= 3.0.0) activesupport (>= 3.0.0) nokogiri (1.5.4) - orm_adapter (0.0.7) + orm_adapter (0.1.0) pjax_rails (0.2.1) jquery-rails polyamorous (0.5.0) activerecord (~> 3.0) - polyglot (0.3.2) + polyglot (0.3.3) rack (1.4.1) rack-cache (1.2) rack (>= 0.4) @@ -216,15 +222,14 @@ GEM sexp_processor (~> 3.0) ruby_parser (2.3.1) sexp_processor (~> 3.0) - rubyzip (0.9.8) + rubyzip (0.9.9) sass (3.1.19) sass-rails (3.2.5) railties (~> 3.2.0) sass (>= 3.1.10) tilt (~> 1.3) - selenium-webdriver (2.22.2) + selenium-webdriver (2.24.0) childprocess (>= 0.2.5) - ffi (~> 1.0) libwebsocket (~> 0.1.3) multi_json (~> 1.0) rubyzip @@ -247,26 +252,25 @@ GEM hike (~> 1.2) rack (~> 1.0) tilt (~> 1.1, != 1.3.0) - squeel (0.9.5) + squeel (1.0.6) activerecord (~> 3.0) activesupport (~> 3.0) polyamorous (~> 0.5.0) switch_user (0.6.0) therubyracer (0.10.1) libv8 (~> 3.3.10) - thor (0.15.2) + thor (0.15.3) tilt (1.3.3) tins (0.4.3) - treetop (1.4.10) - polyglot + treetop (1.4.9) polyglot (>= 0.3.1) tzinfo (0.3.33) - uglifier (1.2.4) + uglifier (1.2.5) execjs (>= 0.3.0) - multi_json (>= 1.0.2) + multi_json (~> 1.3) userstamp_basic (0.1.0) sentient_user (>= 0.1.0) - warden (1.1.1) + warden (1.2.1) rack (>= 1.0) xpath (0.1.4) nokogiri (~> 1.3) @@ -276,8 +280,8 @@ PLATFORMS DEPENDENCIES active-model-email-validator (~> 1.0.2) - active_scaffold (~> 3.2.12) - acts_as_nested_interval (~> 0.0.5) + active_scaffold (~> 3.2.12)! + acts_as_nested_interval (~> 0.0.7) bootstrap-sass (~> 2.0.3) cancan (~> 1.6.7) capistrano (~> 2.9.0) @@ -288,7 +292,7 @@ DEPENDENCIES concerned_with (~> 0.1.0) dalli (~> 1.1.3) database_cleaner (~> 0.7.1) - devise (~> 2.0.4) + devise (~> 2.1.0) factory_girl_rails (~> 1.6.0) faker (~> 1.0.1) guard-rspec (~> 0.6.0) @@ -312,7 +316,7 @@ DEPENDENCIES simplecov sourcify (~> 0.6.0.rc1) spork (~> 1.0.0.rc0) - squeel (~> 0.9.3) + squeel (~> 1.0.0) switch_user (~> 0.6.0) therubyracer uglifier (>= 1.0.3) diff --git a/app/controllers/domains_controller.rb b/app/controllers/domains_controller.rb index ced9fd7..a377a02 100644 --- a/app/controllers/domains_controller.rb +++ b/app/controllers/domains_controller.rb @@ -1,13 +1,17 @@ class DomainsController < ApplicationController active_scaffold :domain do |conf| - conf.columns = [:name, :ip, :records, :soa_record, :ns_records] + conf.columns = [:name, :ip, :records, :soa_record, :ns_records, :apply_subdomains] conf.list.columns = [:name, :records, :permissions] conf.create.columns = [:name, :ip, :soa_record, :ns_records] - conf.update.columns = [:name] + conf.update.columns = [:name, :apply_subdomains] conf.columns[:name].description = 'Ex. "domain.com"' conf.columns[:ip].description = 'Ex. "10.10.5.12", optional IP to associate your domain with' conf.columns[:ns_records].show_blank_record = false conf.columns[:permissions].label = 'Sharing' + conf.columns[:apply_subdomains].label = 'Also rename subdomains' + conf.columns[:apply_subdomains].description = 'If checked, will also rename all subdomains accordingly' + conf.columns[:apply_subdomains].form_ui = :checkbox + conf.columns[:apply_subdomains].options = { class: 'checkbox', checked: true } conf.actions.exclude :show conf.create.refresh_list = true # because tree structure might change conf.update.refresh_list = true # because tree structure might change diff --git a/app/models/domain.rb b/app/models/domain.rb index 2440e86..d0257b4 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -51,7 +51,7 @@ class Domain < ActiveRecord::Base a_records.build(:content => ip) if ip.present? end - concerned_with :name_change, :tree_structure + concerned_with :name_change_records, :name_change_subdomains, :tree_structure scope :host_domains, where(:name => Settings.host_domains) diff --git a/app/models/domain/name_change.rb b/app/models/domain/name_change_records.rb similarity index 100% rename from app/models/domain/name_change.rb rename to app/models/domain/name_change_records.rb diff --git a/app/models/domain/name_change_subdomains.rb b/app/models/domain/name_change_subdomains.rb new file mode 100644 index 0000000..cb2e513 --- /dev/null +++ b/app/models/domain/name_change_subdomains.rb @@ -0,0 +1,51 @@ +class Domain < ActiveRecord::Base + attr_accessor :apply_subdomains + + def apply_subdomains=(v) + @apply_subdomains = ActiveRecord::ConnectionAdapters::Column.value_to_boolean(v) + end + + after_update do + if name_changed? + apply_subdomains ? chain_rename : sync_orphan_children + end + end + + protected + + # previous subdomains in the system for the previous domain name + # in case of domain name change + def previous_subdomains + Domain.where(:name_reversed.matches => "#{name_reversed_was}.%") + end + + # only immediate children + def previous_children_subdomains + descendants = previous_subdomains.preorder.all + first = descendants.first + return [] unless first.present? + + # only immediate children + depth = first.depth + descendants.select { |d| d.depth == depth } + end + + # Syncs with nested interval when the parent is added later than the children + def sync_orphan_children + previous_children_subdomains.each do |subdomain| + subdomain.parent = nil + subdomain.save! + end + end + + # applies the tail rename to children + def chain_rename + name_was_pattern = /#{Regexp.escape(name_was)}$/ + previous_children_subdomains.each do |subdomain| + subdomain.name = subdomain.name.sub(name_was_pattern, name) + subdomain.apply_subdomains = true + subdomain.save! # rest will chain recursively + end + end + +end diff --git a/app/models/domain/tree_structure.rb b/app/models/domain/tree_structure.rb index 3bea0a5..a9f6d4b 100644 --- a/app/models/domain/tree_structure.rb +++ b/app/models/domain/tree_structure.rb @@ -7,6 +7,8 @@ class Domain < ActiveRecord::Base sync_parent end + after_save :sync_children + # this goes after sync_parent, to order callbacks correctly acts_as_nested_interval virtual_root: true @@ -30,15 +32,14 @@ class Domain < ActiveRecord::Base Ability::CRUD.all?{|operation| User.current.can?(operation, self)} end - after_save :sync_children - # Returns the first immediate parent, if exists (and caches the search) def parent_domain return nil if name.nil? @parent_domain ||= {} @parent_domain[name] ||= _parent_domain end - + + # subdomains in the system for the current domain name def subdomains Domain.where(:name_reversed.matches => "#{name_reversed}.%") end @@ -47,6 +48,7 @@ class Domain < ActiveRecord::Base name.end_with?('.' + domain.name) end + # override def self.rebuild_nested_interval_tree! skip_callback :update, :before, :sync_children super @@ -67,6 +69,17 @@ class Domain < ActiveRecord::Base return nil end + # only immediate children + def children_subdomains + descendants = subdomains.preorder.all + first = descendants.first + return [] unless first.present? + + # only immediate children + depth = first.depth + descendants.select { |d| d.depth == depth } + end + # Syncs with nested interval when a child is added and parent exists def sync_parent if !@parent_synced && parent_domain.present? && self.parent_id != parent_domain.id @@ -76,18 +89,10 @@ class Domain < ActiveRecord::Base # Syncs with nested interval when the parent is added later than the children def sync_children - descendants = subdomains.preorder.all - first = descendants.first - return true unless first.present? - - # only immediate children, rest will chain recursively - depth = first.depth - descendants = descendants.select { |d| d.depth == depth } - - subdomains.each do |subdomain| + children_subdomains.each do |subdomain| subdomain.parent = self subdomain.parent_synced = true # no n+1 - subdomain.save! + subdomain.save! # rest will chain recursively end end diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index a5f261a..6b3a90d 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -90,10 +90,6 @@ Devise.setup do |config| # If true, extends the user's remember period when remembered via cookie. # config.extend_remember_period = false - # If true, uses the password salt as remember token. This should be turned - # to false if you are not using database authenticatable. - config.use_salt_as_remember_token = true - # Options to be passed to the created cookie. For instance, you can set # :secure => true in order to force SSL only cookies. # config.cookie_options = {} diff --git a/spec/models/domain_spec.rb b/spec/models/domain_spec.rb index dcf044c..5aea825 100644 --- a/spec/models/domain_spec.rb +++ b/spec/models/domain_spec.rb @@ -113,4 +113,30 @@ describe Domain do ] end + it "chains rename to children" do + domain + subdomain + subsubdomain + domain.apply_subdomains = true + domain.update_attributes(:name => "changed#{domain.name}") + + subdomain.reload.name.should =~ /#{domain.name}$/ + subsubdomain.reload.name.should =~ /#{domain.name}$/ + end + + it "orphans children" do + domain + subdomain + subsubdomain + domain.apply_subdomains = false + domain.update_attributes(:name => "changed#{domain.name}") + s = subdomain.reload + ss = subsubdomain.reload + + s.name.should_not =~ /#{domain.name}$/ + s.parent.should be_nil + ss.name.should_not =~ /#{domain.name}$/ + ss.parent.should_not be_nil + end + end