summaryrefslogtreecommitdiff
path: root/app/models
diff options
context:
space:
mode:
Diffstat (limited to 'app/models')
-rw-r--r--app/models/ability.rb11
-rw-r--r--app/models/access_authorization.rb4
-rw-r--r--app/models/api/row.rb2
-rw-r--r--app/models/automatic_call_distributor.rb4
-rw-r--r--app/models/call_forward.rb2
-rw-r--r--app/models/call_route.rb241
-rw-r--r--app/models/callthrough.rb2
-rw-r--r--app/models/conference.rb6
-rw-r--r--app/models/conference_invitee.rb2
-rw-r--r--app/models/fax_document.rb4
-rw-r--r--app/models/gateway.rb35
-rw-r--r--app/models/gateway_parameter.rb19
-rw-r--r--app/models/gateway_setting.rb31
-rw-r--r--app/models/gemeinschaft_setup.rb21
-rw-r--r--app/models/gs_cluster_sync_log_entry.rb4
-rw-r--r--app/models/gs_parameter.rb35
-rw-r--r--app/models/hunt_group.rb10
-rw-r--r--app/models/phone.rb7
-rw-r--r--app/models/phone_book.rb2
-rw-r--r--app/models/phone_book_entry.rb2
-rw-r--r--app/models/phone_model.rb2
-rw-r--r--app/models/phone_number.rb16
-rw-r--r--app/models/phone_number_range.rb2
-rw-r--r--app/models/route_element.rb26
-rw-r--r--app/models/sip_account.rb6
-rw-r--r--app/models/tenant.rb29
-rw-r--r--app/models/user.rb4
27 files changed, 473 insertions, 56 deletions
diff --git a/app/models/ability.rb b/app/models/ability.rb
index d9ec74a..b846af0 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -27,6 +27,10 @@ class Ability
#
cannot [:create, :destroy, :edit, :update], Tenant, :id => 1
+ # Can't destroy any tenant
+ #
+ cannot :destroy, Tenant
+
cannot :manage, PhoneBook
# Phonebooks and PhoneBookEntries
@@ -70,9 +74,14 @@ class Ability
# Dirty hack to disable PhoneNumberRange in the GUI
#
- if STRICT_INTERNAL_EXTENSION_HANDLING == false
+ if GsParameter.get('STRICT_INTERNAL_EXTENSION_HANDLING') == false
cannot :manage, PhoneNumberRange
end
+
+ # GsParameter and GuiFunction can't be created or deleted via the GUI
+ #
+ cannot [:create, :destroy], GsParameter
+ cannot [:create, :destroy], GuiFunction
else
# Any user can do the following stuff.
#
diff --git a/app/models/access_authorization.rb b/app/models/access_authorization.rb
index ef33115..878799a 100644
--- a/app/models/access_authorization.rb
+++ b/app/models/access_authorization.rb
@@ -18,7 +18,9 @@ class AccessAuthorization < ActiveRecord::Base
:allow_nil => true, :allow_blank => true,
:message => "must be numeric."
- validates_length_of :pin, :minimum => MINIMUM_PIN_LENGTH, :maximum => MAXIMUM_PIN_LENGTH,
+ validates_length_of :pin,
+ :minimum => (GsParameter.get('MINIMUM_PIN_LENGTH').nil? ? 4 : GsParameter.get('MINIMUM_PIN_LENGTH')),
+ :maximum => (GsParameter.get('MAXIMUM_PIN_LENGTH').nil? ? 10 : GsParameter.get('MAXIMUM_PIN_LENGTH')),
:allow_nil => true, :allow_blank => true
has_many :phone_numbers, :as => :phone_numberable, :dependent => :destroy
diff --git a/app/models/api/row.rb b/app/models/api/row.rb
index ac35516..e82a3e2 100644
--- a/app/models/api/row.rb
+++ b/app/models/api/row.rb
@@ -28,7 +28,7 @@ class Api::Row < ActiveRecord::Base
end
def create_a_new_gemeinschaft_user
- tenant = Tenant.find(DEFAULT_API_TENANT_ID)
+ tenant = Tenant.find(GsParameter.get('DEFAULT_API_TENANT_ID'))
# Find or create the user
#
diff --git a/app/models/automatic_call_distributor.rb b/app/models/automatic_call_distributor.rb
index cd887d5..5807757 100644
--- a/app/models/automatic_call_distributor.rb
+++ b/app/models/automatic_call_distributor.rb
@@ -1,7 +1,7 @@
class AutomaticCallDistributor < ActiveRecord::Base
attr_accessible :uuid, :name, :strategy, :automatic_call_distributorable_type, :automatic_call_distributorable_id, :max_callers, :agent_timeout, :retry_timeout, :join, :leave, :gs_node_id, :announce_position, :announce_call_agents, :greeting, :goodbye, :music
- belongs_to :automatic_call_distributorable, :polymorphic => true
+ belongs_to :automatic_call_distributorable, :polymorphic => true, :touch => true
has_many :acd_agents, :dependent => :destroy
has_many :phone_numbers, :as => :phone_numberable, :dependent => :destroy
@@ -26,6 +26,6 @@ class AutomaticCallDistributor < ActiveRecord::Base
self.announce_call_agents ||= 'ivr/ivr-stay_on_line_call_answered_momentarily.wav'
self.greeting ||= 'ivr/ivr-thank_you_for_calling.wav'
self.goodbye ||= 'ivr/ivr-thank_you_for_calling.wav'
- self.music ||= 'local_stream://mohl'
+ self.music ||= 'local_stream://moh'
end
end
diff --git a/app/models/call_forward.rb b/app/models/call_forward.rb
index 0018cfb..8a8d1df 100644
--- a/app/models/call_forward.rb
+++ b/app/models/call_forward.rb
@@ -28,7 +28,7 @@ class CallForward < ActiveRecord::Base
validates_numericality_of :depth,
:only_integer => true,
:greater_than_or_equal_to => 1,
- :less_than_or_equal_to => MAX_CALL_FORWARD_DEPTH
+ :less_than_or_equal_to => (GsParameter.get('MAX_CALL_FORWARD_DEPTH').nil? ? 0 : GsParameter.get('MAX_CALL_FORWARD_DEPTH'))
before_validation {
self.timeout = nil if self.call_forward_case_id != 3
diff --git a/app/models/call_route.rb b/app/models/call_route.rb
new file mode 100644
index 0000000..28120c1
--- /dev/null
+++ b/app/models/call_route.rb
@@ -0,0 +1,241 @@
+class CallRoute < ActiveRecord::Base
+ # https://github.com/rails/strong_parameters
+ include ActiveModel::ForbiddenAttributesProtection
+
+ ROUTING_TABLES = ['prerouting', 'outbound', 'inbound']
+
+ has_many :route_elements, :dependent => :destroy
+
+ validates :name,
+ :presence => true
+
+ validates :routing_table,
+ :presence => true,
+ :inclusion => { :in => ROUTING_TABLES }
+
+ acts_as_list :scope => '`routing_table` = \'#{routing_table}\''
+
+ def to_s
+ name.to_s
+ end
+
+ def move_up?
+ return self.position.to_i > CallRoute.where(:routing_table => self.routing_table ).order(:position).first.position.to_i
+ end
+
+ def move_down?
+ return self.position.to_i < CallRoute.where(:routing_table => self.routing_table ).order(:position).last.position.to_i
+ end
+
+ def self.factory_defaults_prerouting(country_code, national_prefix = '', international_prefix = '', trunk_access_code = '', area_code = '')
+ CallRoute.where(:routing_table => "prerouting").destroy_all
+
+ CallRoute.create_prerouting_entry('international call', [
+ { :pattern => '^'+trunk_access_code+international_prefix+'([1-9]%d+)$', :replacement => '+%1', },
+ ], 'phonenumber')
+
+ CallRoute.create_prerouting_entry('national call', [
+ { :pattern => '^'+trunk_access_code+national_prefix+'([1-9]%d+)$', :replacement => '+'+country_code+'%1', },
+ ], 'phonenumber')
+
+ if !trunk_access_code.blank? && !area_code.blank?
+ CallRoute.create_prerouting_entry('local call', [
+ { :pattern => '^'+trunk_access_code+'([1-9]%d+)$', :replacement => '+'+country_code+area_code+'%1', },
+ ], 'phonenumber')
+ end
+
+ CallRoute.create_prerouting_entry('log in', [
+ { :pattern => '^%*0%*$', :replacement => 'f-li', },
+ { :pattern => '^%*0%*(%+?%d+)#*$', :replacement => 'f-li-%1', },
+ { :pattern => '^%*0%*(%+?%d+)%*(%d+)#*$', :replacement => 'f-li-%1-%2', },
+ ])
+
+ CallRoute.create_prerouting_entry('log out', [
+ { :pattern => '^#0#$', :replacement => 'f-lo', },
+ ])
+
+ CallRoute.create_prerouting_entry('toggle ACD membership', [
+ { :pattern => '^%*5%*(%+?%d+)#$', :replacement => 'f-acdmtg-0-%1', },
+ ])
+
+ CallRoute.create_prerouting_entry('activate CLIP', [
+ { :pattern => '^%*30#$', :replacement => 'f-clipon', },
+ ])
+
+ CallRoute.create_prerouting_entry('deactivate CLIP', [
+ { :pattern => '^#30#$', :replacement => 'f-clipoff', },
+ ])
+
+ CallRoute.create_prerouting_entry('activate CLIR', [
+ { :pattern => '^#31#$', :replacement => 'f-cliron', },
+ ])
+
+ CallRoute.create_prerouting_entry('deactivate CLIR', [
+ { :pattern => '^%*31#$', :replacement => 'f-cliroff', },
+ ])
+
+ elements = [
+ { :pattern => '^#31#(%+?[1-9]%d+)$', :replacement => 'f-dcliron-%1', },
+ { :pattern => '^#31#'+trunk_access_code+international_prefix+'([1-9]%d+)$', :replacement => 'f-dcliron-+%1' },
+ { :pattern => '^#31#'+trunk_access_code+national_prefix+'([1-9]%d+)$', :replacement => 'f-dcliron-+'+country_code+'%1' },
+ ]
+
+ if !trunk_access_code.blank? && !area_code.blank?
+ elements << { :pattern => '^#31#'+trunk_access_code+'([1-9]%d+)$', :replacement => 'f-dcliron-+'+country_code+area_code+'%1' }
+ end
+
+ CallRoute.create_prerouting_entry('activate CLIR for call', elements)
+
+ elements = [
+ { :pattern => '^%*31#(%+?[1-9]%d+)$', :replacement => 'f-dcliroff-%1', },
+ { :pattern => '^%*31#'+trunk_access_code+international_prefix+'([1-9]%d+)$', :replacement => 'f-dcliroff-+%1' },
+ { :pattern => '^%*31#'+trunk_access_code+national_prefix+'([1-9]%d+)$', :replacement => 'f-dcliroff-+'+country_code+'%1' },
+ ]
+
+ if !trunk_access_code.blank? && !area_code.blank?
+ elements << { :pattern => '^%*31#'+trunk_access_code+'([1-9]%d+)$', :replacement => 'f-dcliroff-+'+country_code+area_code+'%1' }
+ end
+
+ CallRoute.create_prerouting_entry('deactivate CLIR for call', elements)
+
+ CallRoute.create_prerouting_entry('activate call waiting', [
+ { :pattern => '^%*43#$', :replacement => 'f-cwaon', },
+ ])
+
+ CallRoute.create_prerouting_entry('deactivate call waiting', [
+ { :pattern => '^#43#$', :replacement => 'f-cwaoff', },
+ ])
+
+ CallRoute.create_prerouting_entry('deactivate all call forwards', [
+ { :pattern => '^#002#$', :replacement => 'f-cfoff', },
+ ])
+
+ CallRoute.create_prerouting_entry('delete all call forwards', [
+ { :pattern => '^##002#$', :replacement => 'f-cfdel', },
+ ])
+
+ elements = [
+ { :pattern => '^%*21#$', :replacement => 'f-cfu', },
+ { :pattern => '^%*%*?21%*(%+?[1-9]%d+)#$', :replacement => 'f-cfu-%1', },
+ { :pattern => '^%*%*?21%*'+trunk_access_code+international_prefix+'([1-9]%d+)#$', :replacement => 'f-cfu-+%1', },
+ { :pattern => '^%*%*?21%*'+trunk_access_code+national_prefix+'([1-9]%d+)#$', :replacement => 'f-cfu-+'+country_code+'%1', },
+ ]
+
+ if !trunk_access_code.blank? && !area_code.blank?
+ elements << { :pattern => '^%*%*?21%*'+trunk_access_code+'([1-9]%d+)#$', :replacement => 'f-cfu-+'+country_code+area_code+'%1' }
+ end
+
+ CallRoute.create_prerouting_entry('set unconditional call forwarding', elements)
+
+ CallRoute.create_prerouting_entry('deactivate unconditional call forwarding', [
+ { :pattern => '^#21#$', :replacement => 'f-cfuoff', },
+ ])
+
+ CallRoute.create_prerouting_entry('delete unconditional call forwarding', [
+ { :pattern => '^##21#$', :replacement => 'f-cfudel', },
+ ])
+
+ elements = [
+ { :pattern => '^%*61#$', :replacement => 'f-cfn', },
+ { :pattern => '^%*%*?61%*'+trunk_access_code+international_prefix+'([1-9]%d+)#$', :replacement => 'f-cfn-+%1', },
+ { :pattern => '^%*%*?61%*'+trunk_access_code+national_prefix+'([1-9]%d+)#$', :replacement => 'f-cfn-+'+country_code+'%1', },
+ { :pattern => '^%*%*?61%*(%+?[1-9]%d+)#$', :replacement => 'f-cfn-%1', },
+ { :pattern => '^%*%*?61%*'+trunk_access_code+international_prefix+'([1-9]%d+)%*(%d+)#$', :replacement => 'f-cfn-+%1-%2', },
+ { :pattern => '^%*%*?61%*'+trunk_access_code+national_prefix+'([1-9]%d+)%*(%d+)#$', :replacement => 'f-cfn-+'+country_code+'%1-%2', },
+ { :pattern => '^%*%*?61%*(%+?[1-9]%d+)%*(%d+)#$', :replacement => 'f-cfn-%1-%2', },
+ ]
+
+ if !trunk_access_code.blank? && !area_code.blank?
+ elements << { :pattern => '^%*%*?61%*'+trunk_access_code+'([1-9]%d+)#$', :replacement => 'f-cfn-+'+country_code+area_code+'%1' }
+ elements << { :pattern => '^%*%*?61%*'+trunk_access_code+'([1-9]%d+)%*(%d+)#$', :replacement => 'f-cfn-+'+country_code+area_code+'%1-%2' }
+ end
+
+ CallRoute.create_prerouting_entry('call forward if not answered', elements)
+
+ CallRoute.create_prerouting_entry('deactivate call forward if not answered', [
+ { :pattern => '^#61#$', :replacement => 'f-cfnoff', },
+ ])
+
+ CallRoute.create_prerouting_entry('delete call forward if not answered', [
+ { :pattern => '^##61#$', :replacement => 'f-cfndel', },
+ ])
+
+ elements = [
+ { :pattern => '^%*62#$', :replacement => 'f-cfo', },
+ { :pattern => '^%*%*?62%*(%+?[1-9]%d+)#$', :replacement => 'f-cfo-%1', },
+ { :pattern => '^%*%*?62%*'+trunk_access_code+international_prefix+'([1-9]%d+)#$', :replacement => 'f-cfo-+%1', },
+ { :pattern => '^%*%*?62%*'+trunk_access_code+national_prefix+'([1-9]%d+)#$', :replacement => 'f-cfo-+'+country_code+'%1', },
+ ]
+
+ if !trunk_access_code.blank? && !area_code.blank?
+ elements << { :pattern => '^%*%*?62%*'+trunk_access_code+'([1-9]%d+)#$', :replacement => 'f-cfo-+'+country_code+area_code+'%1' }
+ end
+
+ CallRoute.create_prerouting_entry('call forward if offline', elements)
+
+ CallRoute.create_prerouting_entry('deactivate call forward if offline', [
+ { :pattern => '^#62#$', :replacement => 'f-cfooff', },
+ ])
+
+ CallRoute.create_prerouting_entry('delete call forward if offline', [
+ { :pattern => '^##62#$', :replacement => 'f-cfodel', },
+ ])
+
+ elements = [
+ { :pattern => '^%*67#$', :replacement => 'f-cfb', },
+ { :pattern => '^%*%*?67%*(%+?[1-9]%d+)#$', :replacement => 'f-cfb-%1', },
+ { :pattern => '^%*%*?67%*'+trunk_access_code+international_prefix+'([1-9]%d+)#$', :replacement => 'f-cfb-+%1', },
+ { :pattern => '^%*%*?67%*'+trunk_access_code+national_prefix+'([1-9]%d+)#$', :replacement => 'f-cfb-+'+country_code+'%1', },
+ ]
+
+ if !trunk_access_code.blank? && !area_code.blank?
+ elements << { :pattern => '^%*%*?67%*'+trunk_access_code+'([1-9]%d+)#$', :replacement => 'f-cfb-+'+country_code+area_code+'%1' }
+ end
+
+ CallRoute.create_prerouting_entry('call forward if busy', elements)
+
+ CallRoute.create_prerouting_entry('deactivate call forward if busy', [
+ { :pattern => '^#67#$', :replacement => 'f-cfboff', },
+ ])
+
+ CallRoute.create_prerouting_entry('delete call forward if busy', [
+ { :pattern => '^##67#$', :replacement => 'f-cfbdel', },
+ ])
+
+
+ CallRoute.create_prerouting_entry('redial', [
+ { :pattern => '^%*66#$', :replacement => 'f-redial', },
+ ])
+
+ CallRoute.create_prerouting_entry('check voicemail', [
+ { :pattern => '^%*98$', :replacement => 'f-vmcheck', },
+ { :pattern => '^%*98#$', :replacement => 'f-vmcheck', },
+ { :pattern => '^%*98%*(%+?%d+)#$', :replacement => 'f-vmcheck-%1', },
+ ])
+
+ CallRoute.create_prerouting_entry('acivate auto logout', [
+ { :pattern => '^%*1337%*1%*1#$', :replacement => 'f-loaon', },
+ ])
+
+ CallRoute.create_prerouting_entry('deacivate auto logout', [
+ { :pattern => '^%*1337%*1%*0#$', :replacement => 'f-loaoff', },
+ ])
+ end
+
+ def self.create_prerouting_entry(name, elements, endpoint_type = 'dialplanfunction')
+ call_route = CallRoute.create(:routing_table => 'prerouting', :name => name, :endpoint_type => endpoint_type)
+
+ if !call_route.errors.any? then
+ elements.each do |element|
+ call_route.route_elements.create(
+ :var_in => 'destination_number',
+ :var_out => 'destination_number',
+ :pattern => element[:pattern],
+ :replacement => element[:replacement],
+ :action => 'match',
+ :mandatory => false
+ )
+ end
+ end
+ end
+end
diff --git a/app/models/callthrough.rb b/app/models/callthrough.rb
index c057fa6..ae461cf 100644
--- a/app/models/callthrough.rb
+++ b/app/models/callthrough.rb
@@ -5,7 +5,7 @@ class Callthrough < ActiveRecord::Base
# Validations and Associations
#
- belongs_to :tenant
+ belongs_to :tenant, :touch => true
validates_presence_of :tenant_id
validates_presence_of :tenant
diff --git a/app/models/conference.rb b/app/models/conference.rb
index 8be9f21..8c5a752 100644
--- a/app/models/conference.rb
+++ b/app/models/conference.rb
@@ -3,7 +3,7 @@ class Conference < ActiveRecord::Base
:open_for_anybody, :max_members, :announce_new_member_by_name,
:announce_left_member_by_name
- belongs_to :conferenceable, :polymorphic => true
+ belongs_to :conferenceable, :polymorphic => true, :touch => true
has_many :conference_invitees, :dependent => :destroy
has_many :phone_numbers, :as => :phone_numberable, :dependent => :destroy
@@ -15,7 +15,7 @@ class Conference < ActiveRecord::Base
validates_presence_of :max_members
validates_numericality_of :max_members, :only_integer => true,
:greater_than => 0,
- :less_than => (MAXIMUM_NUMBER_OF_PEOPLE_IN_A_CONFERENCE + 1),
+ :less_than => ((GsParameter.get('MAXIMUM_NUMBER_OF_PEOPLE_IN_A_CONFERENCE').nil? ? 10 : GsParameter.get('MAXIMUM_NUMBER_OF_PEOPLE_IN_A_CONFERENCE')) + 1),
:allow_nil => false,
:allow_blank => false
@@ -25,7 +25,7 @@ class Conference < ActiveRecord::Base
:greater_than => 0,
:allow_nil => true,
:allow_blank => true
- validates_length_of :pin, :minimum => MINIMUM_PIN_LENGTH,
+ validates_length_of :pin, :minimum => (GsParameter.get('MINIMUM_PIN_LENGTH').nil? ? 4 : GsParameter.get('MINIMUM_PIN_LENGTH')),
:allow_nil => true,
:allow_blank => true
diff --git a/app/models/conference_invitee.rb b/app/models/conference_invitee.rb
index 7de20de..d6e3bac 100644
--- a/app/models/conference_invitee.rb
+++ b/app/models/conference_invitee.rb
@@ -13,7 +13,7 @@ class ConferenceInvitee < ActiveRecord::Base
:greater_than => 0,
:allow_nil => true,
:allow_blank => true
- validates_length_of :pin, :minimum => MINIMUM_PIN_LENGTH,
+ validates_length_of :pin, :minimum => (GsParameter.get('MINIMUM_PIN_LENGTH').nil? ? 4 : GsParameter.get('MINIMUM_PIN_LENGTH')),
:allow_nil => true,
:allow_blank => true
diff --git a/app/models/fax_document.rb b/app/models/fax_document.rb
index 67bdea9..080bdaa 100644
--- a/app/models/fax_document.rb
+++ b/app/models/fax_document.rb
@@ -54,7 +54,7 @@ class FaxDocument < ActiveRecord::Base
private
def render_thumbnails
- directory = "/tmp/GS-#{GEMEINSCHAFT_VERSION}/fax_thumbnails/#{self.id}"
+ directory = "/tmp/GS-#{GsParameter.get('GEMEINSCHAFT_VERSION')}/fax_thumbnails/#{self.id}"
system('mkdir -p ' + directory)
system("cd #{directory} && convert #{Rails.root.to_s}/public#{self.document.to_s}[0-100] -colorspace Gray PNG:'fax_page.png'")
number_of_thumbnails = Dir["#{directory}/fax_page-*.png"].count
@@ -70,7 +70,7 @@ class FaxDocument < ActiveRecord::Base
def convert_pdf_to_tiff
page_size_a4 = '595 842'
page_size_command = "<< /Policies << /PageSize 3 >> /InputAttributes currentpagedevice /InputAttributes get dup { pop 1 index exch undef } forall dup 0 << /PageSize [ #{page_size_a4} ] >> put >> setpagedevice"
- directory = "/tmp/GS-#{GEMEINSCHAFT_VERSION}/faxes/#{self.id}"
+ directory = "/tmp/GS-#{GsParameter.get('GEMEINSCHAFT_VERSION')}/faxes/#{self.id}"
system('mkdir -p ' + directory)
tiff_file_name = File.basename(self.document.to_s.downcase, ".pdf") + '.tiff'
system "cd #{directory} && gs -q -r#{self.fax_resolution.resolution_value} -dNOPAUSE -dBATCH -dSAFER -sDEVICE=tiffg3 -sOutputFile=\"#{tiff_file_name}\" -c \"#{page_size_command}\" -- \"#{Rails.root.to_s}/public#{self.document.to_s}\""
diff --git a/app/models/gateway.rb b/app/models/gateway.rb
new file mode 100644
index 0000000..3e791a6
--- /dev/null
+++ b/app/models/gateway.rb
@@ -0,0 +1,35 @@
+class Gateway < ActiveRecord::Base
+ TECHNOLOGIES = ['sip']
+
+ attr_accessible :name, :technology, :inbound, :outbound, :description
+
+ has_many :gateway_settings, :dependent => :destroy
+ has_many :gateway_parameters, :dependent => :destroy
+
+ validates :name,
+ :presence => true,
+ :uniqueness => true
+
+ validates :technology,
+ :presence => true,
+ :inclusion => { :in => TECHNOLOGIES }
+
+ after_initialize :set_defaults
+ before_validation :downcase_technology
+
+ def to_s
+ name
+ end
+
+ private
+ def downcase_technology
+ self.technology = self.technology.downcase if !self.technology.blank?
+ end
+
+ def set_defaults
+ if TECHNOLOGIES.count == 1
+ self.technology = TECHNOLOGIES.first
+ end
+ end
+
+end
diff --git a/app/models/gateway_parameter.rb b/app/models/gateway_parameter.rb
new file mode 100644
index 0000000..a66af75
--- /dev/null
+++ b/app/models/gateway_parameter.rb
@@ -0,0 +1,19 @@
+class GatewayParameter < ActiveRecord::Base
+ CLASS_TYPES = ['String', 'Integer', 'Boolean']
+
+ attr_accessible :gateway_id, :name, :value, :class_type, :description
+
+ belongs_to :gateway, :touch => true
+
+ validates :name,
+ :presence => true,
+ :uniqueness => {:scope => :gateway_id}
+
+ validates :class_type,
+ :presence => true,
+ :inclusion => { :in => CLASS_TYPES }
+
+ def to_s
+ name
+ end
+end
diff --git a/app/models/gateway_setting.rb b/app/models/gateway_setting.rb
new file mode 100644
index 0000000..c01f0a8
--- /dev/null
+++ b/app/models/gateway_setting.rb
@@ -0,0 +1,31 @@
+class GatewaySetting < ActiveRecord::Base
+ CLASS_TYPES = ['String', 'Integer', 'Boolean']
+ GATEWAY_SETTINGS = {
+ 'sip' => {
+ 'domain' => 'String',
+ 'username' => 'String',
+ 'password' => 'String',
+ 'contact' => 'String',
+ 'register' => 'Boolean',
+ 'auth_source' => 'String',
+ 'auth_pattern' => 'String',
+ 'number_source' => 'String',
+ },
+ }
+
+ attr_accessible :gateway_id, :name, :value, :class_type, :description
+
+ belongs_to :gateway
+
+ validates :name,
+ :presence => true,
+ :uniqueness => {:scope => :gateway_id}
+
+ validates :class_type,
+ :presence => true,
+ :inclusion => { :in => CLASS_TYPES }
+
+ def to_s
+ name
+ end
+end
diff --git a/app/models/gemeinschaft_setup.rb b/app/models/gemeinschaft_setup.rb
index b445b21..6056236 100644
--- a/app/models/gemeinschaft_setup.rb
+++ b/app/models/gemeinschaft_setup.rb
@@ -5,4 +5,25 @@ class GemeinschaftSetup < ActiveRecord::Base
accepts_nested_attributes_for :sip_domain
belongs_to :country
belongs_to :language
+
+ # Remove the cache which was created by the heater rake task.
+ #
+ after_create :expire_cache
+
+ before_validation :format_default_area_code
+
+ private
+ def expire_cache
+ ActionController::Base.expire_page(Rails.application.routes.url_helpers.new_gemeinschaft_setup_path)
+ end
+
+ def format_default_area_code
+ if self.default_area_code.blank?
+ self.default_area_code = nil
+ else
+ if self.country != nil && !self.country.trunk_prefix.blank?
+ self.default_area_code.gsub(/^#{self.country.trunk_prefix}/,'')
+ end
+ end
+ end
end
diff --git a/app/models/gs_cluster_sync_log_entry.rb b/app/models/gs_cluster_sync_log_entry.rb
index 063ff23..51e3b05 100644
--- a/app/models/gs_cluster_sync_log_entry.rb
+++ b/app/models/gs_cluster_sync_log_entry.rb
@@ -15,7 +15,7 @@ class GsClusterSyncLogEntry < ActiveRecord::Base
after_create :apply_to_local_database
def apply_to_local_database
- if self.homebase_ip_address != HOMEBASE_IP_ADDRESS
+ if self.homebase_ip_address != GsParameter.get('HOMEBASE_IP_ADDRESS')
if self.class_name.constantize.new.attribute_names.include?('is_native')
case self.action
when 'create'
@@ -84,7 +84,7 @@ class GsClusterSyncLogEntry < ActiveRecord::Base
end
def populate_other_cluster_nodes
- if self.homebase_ip_address == HOMEBASE_IP_ADDRESS && self.waiting_to_be_synced == true
+ if self.homebase_ip_address == GsParameter.get('HOMEBASE_IP_ADDRESS') && self.waiting_to_be_synced == true
if GsNode.where(:push_updates_to => true).count > 0
GsNode.where(:push_updates_to => true).each do |gs_node|
RemoteGsNode::GsClusterSyncLogEntry.site = gs_node.site
diff --git a/app/models/gs_parameter.rb b/app/models/gs_parameter.rb
new file mode 100644
index 0000000..fe2a845
--- /dev/null
+++ b/app/models/gs_parameter.rb
@@ -0,0 +1,35 @@
+class GsParameter < ActiveRecord::Base
+ # https://github.com/rails/strong_parameters
+ include ActiveModel::ForbiddenAttributesProtection
+
+ validates :name,
+ :presence => true,
+ :uniqueness => { :scope => [ :entity, :section ] }
+
+ validates :class_type,
+ :presence => true,
+ :inclusion => { :in => ['String', 'Integer', 'Boolean', 'YAML', 'Nil'] }
+
+ def self.get(wanted_variable)
+ if GsParameter.table_exists?
+ item = GsParameter.where(:name => wanted_variable).first
+ if item.nil? || item.class_type == 'Nil'
+ return nil
+ else
+ return item.value.to_i if item.class_type == 'Integer'
+ return item.value.to_s if item.class_type == 'String'
+ if item.class_type == 'Boolean'
+ return true if item.value == 'true'
+ return false if item.value == 'false'
+ end
+ return YAML.load(item.value) if item.class_type == 'YAML'
+ end
+ else
+ nil
+ end
+ end
+
+ def to_s
+ name
+ end
+end
diff --git a/app/models/hunt_group.rb b/app/models/hunt_group.rb
index 276ae53..5011bf0 100644
--- a/app/models/hunt_group.rb
+++ b/app/models/hunt_group.rb
@@ -1,24 +1,24 @@
class HuntGroup < ActiveRecord::Base
attr_accessible :name, :strategy, :seconds_between_jumps, :phone_numbers_attributes
- belongs_to :tenant
+ belongs_to :tenant, :touch => true
has_many :call_forwards, :as => :call_forwardable, :dependent => :destroy
validates_uniqueness_of :name, :scope => :tenant_id,
:allow_nil => true, :allow_blank => true
validates_presence_of :strategy
- validates_inclusion_of :strategy, :in => HUNT_GROUP_STRATEGIES
+ validates_inclusion_of :strategy, :in => (GsParameter.get('HUNT_GROUP_STRATEGIES').nil? ? [] : GsParameter.get('HUNT_GROUP_STRATEGIES'))
validates_presence_of :seconds_between_jumps,
:if => Proc.new{ |hunt_group| hunt_group.strategy != 'ring_all' }
validates_numericality_of :seconds_between_jumps,
:only_integer => true,
- :greater_than_or_equal_to => VALID_SECONDS_BETWEEN_JUMPS_VALUES.min,
- :less_than_or_equal_to => VALID_SECONDS_BETWEEN_JUMPS_VALUES.max,
+ :greater_than_or_equal_to => (GsParameter.get('VALID_SECONDS_BETWEEN_JUMPS_VALUES').nil? ? 2 : GsParameter.get('VALID_SECONDS_BETWEEN_JUMPS_VALUES').min),
+ :less_than_or_equal_to => (GsParameter.get('VALID_SECONDS_BETWEEN_JUMPS_VALUES').nil? ? 120 : GsParameter.get('VALID_SECONDS_BETWEEN_JUMPS_VALUES').max),
:if => Proc.new{ |hunt_group| hunt_group.strategy != 'ring_all' }
validates_inclusion_of :seconds_between_jumps,
- :in => VALID_SECONDS_BETWEEN_JUMPS_VALUES,
+ :in => (GsParameter.get('VALID_SECONDS_BETWEEN_JUMPS_VALUES').nil? ? [] : GsParameter.get('VALID_SECONDS_BETWEEN_JUMPS_VALUES')),
:if => Proc.new{ |hunt_group| hunt_group.strategy != 'ring_all' }
validates_inclusion_of :seconds_between_jumps,
:in => [nil],
diff --git a/app/models/phone.rb b/app/models/phone.rb
index a606834..8b41b59 100644
--- a/app/models/phone.rb
+++ b/app/models/phone.rb
@@ -9,7 +9,7 @@ class Phone < ActiveRecord::Base
# Associations
#
belongs_to :phone_model
- belongs_to :phoneable, :polymorphic => true
+ belongs_to :phoneable, :polymorphic => true, :touch => true
has_many :phone_sip_accounts, :dependent => :destroy, :uniq => true, :order => :position
has_many :sip_accounts, :through => :phone_sip_accounts
@@ -162,7 +162,6 @@ class Phone < ActiveRecord::Base
if ! self.resync(true, sip_account_resync)
errors.add(:resync, "Resync failed")
- return false
end
return true
@@ -201,6 +200,10 @@ class Phone < ActiveRecord::Base
# Sanitize MAC address.
#
def sanitize_mac_address
+ if self.mac_address.split(/:/).count == 6 && self.mac_address.length < 17
+ splitted_mac_address = self.mac_address.split(/:/)
+ self.mac_address = splitted_mac_address.map{|part| (part.size == 1 ? "0#{part}" : part)}.join('')
+ end
self.mac_address = self.mac_address.to_s.upcase.gsub( /[^A-F0-9]/, '' )
end
diff --git a/app/models/phone_book.rb b/app/models/phone_book.rb
index 3603eae..21d30c0 100644
--- a/app/models/phone_book.rb
+++ b/app/models/phone_book.rb
@@ -1,7 +1,7 @@
class PhoneBook < ActiveRecord::Base
attr_accessible :name, :description, :uuid
- belongs_to :phone_bookable, :polymorphic => true
+ belongs_to :phone_bookable, :polymorphic => true, :touch => true
has_many :phone_book_entries, :dependent => :destroy
validates_presence_of :name
diff --git a/app/models/phone_book_entry.rb b/app/models/phone_book_entry.rb
index db2b44b..275c7b6 100644
--- a/app/models/phone_book_entry.rb
+++ b/app/models/phone_book_entry.rb
@@ -6,7 +6,7 @@ class PhoneBookEntry < ActiveRecord::Base
attr_accessible :first_name, :middle_name, :last_name, :title, :nickname, :organization, :is_organization, :department, :job_title, :is_male, :birthday, :birth_name, :description, :homepage_personal, :homepage_organization, :twitter_account, :facebook_account, :google_plus_account, :xing_account, :linkedin_account, :mobileme_account, :image
- belongs_to :phone_book
+ belongs_to :phone_book, :touch => true
has_many :conference_invitees, :dependent => :destroy
acts_as_list :scope => :phone_book
diff --git a/app/models/phone_model.rb b/app/models/phone_model.rb
index e00e0e3..ac4d4a3 100644
--- a/app/models/phone_model.rb
+++ b/app/models/phone_model.rb
@@ -3,7 +3,7 @@ class PhoneModel < ActiveRecord::Base
# Associations
#
- belongs_to :manufacturer
+ belongs_to :manufacturer, :touch => true
has_many :phones, :dependent => :destroy
diff --git a/app/models/phone_number.rb b/app/models/phone_number.rb
index 4c0cf46..d1e950f 100644
--- a/app/models/phone_number.rb
+++ b/app/models/phone_number.rb
@@ -18,8 +18,8 @@ class PhoneNumber < ActiveRecord::Base
before_save :save_value_of_to_s
after_create :copy_existing_call_forwards_if_necessary
before_validation :'parse_and_split_number!'
- validate :validate_number, :if => Proc.new { |phone_number| STRICT_INTERNAL_EXTENSION_HANDLING && STRICT_DID_HANDLING }
- validate :check_if_number_is_available, :if => Proc.new { |phone_number| STRICT_INTERNAL_EXTENSION_HANDLING && STRICT_DID_HANDLING }
+ validate :validate_number, :if => Proc.new { |phone_number| GsParameter.get('STRICT_INTERNAL_EXTENSION_HANDLING') && GsParameter.get('STRICT_DID_HANDLING') }
+ validate :check_if_number_is_available, :if => Proc.new { |phone_number| GsParameter.get('STRICT_INTERNAL_EXTENSION_HANDLING') && GsParameter.get('STRICT_DID_HANDLING') }
acts_as_list :scope => [:phone_numberable_id, :phone_numberable_type]
@@ -95,7 +95,7 @@ class PhoneNumber < ActiveRecord::Base
else
# Check if the number is an internal extension.
if tenant
- internal_extension_range = tenant.phone_number_ranges.where(:name => INTERNAL_EXTENSIONS).first
+ internal_extension_range = tenant.phone_number_ranges.where(:name => GsParameter.get('INTERNAL_EXTENSIONS')).first
if internal_extension_range
if internal_extension_range.phone_numbers.where(:number => number).length > 0
parts[:extension] = number
@@ -192,8 +192,8 @@ class PhoneNumber < ActiveRecord::Base
end
def parse_and_split_number!
- if self.phone_numberable_type == 'PhoneNumberRange' && self.phone_numberable.name == INTERNAL_EXTENSIONS
- # The parent is the PhoneNumberRange INTERNAL_EXTENSIONS. Therefor it must be an extensions.
+ if self.phone_numberable_type == 'PhoneNumberRange' && self.phone_numberable.name == GsParameter.get('INTERNAL_EXTENSIONS')
+ # The parent is the PhoneNumberRange GsParameter.get('INTERNAL_EXTENSIONS'). Therefor it must be an extensions.
#
self.country_code = nil
self.area_code = nil
@@ -202,8 +202,8 @@ class PhoneNumber < ActiveRecord::Base
self.extension = self.number.to_s.strip
else
if self.tenant &&
- self.tenant.phone_number_ranges.exists?(:name => INTERNAL_EXTENSIONS) &&
- self.tenant.phone_number_ranges.where(:name => INTERNAL_EXTENSIONS).first.phone_numbers.exists?(:number => self.number)
+ self.tenant.phone_number_ranges.exists?(:name => GsParameter.get('INTERNAL_EXTENSIONS')) &&
+ self.tenant.phone_number_ranges.where(:name => GsParameter.get('INTERNAL_EXTENSIONS')).first.phone_numbers.exists?(:number => self.number)
self.country_code = nil
self.area_code = nil
self.subscriber_number = nil
@@ -263,7 +263,7 @@ class PhoneNumber < ActiveRecord::Base
if self.phone_numberable_type != 'PhoneBookEntry' && self.tenant
phone_number_ranges = self.tenant.phone_number_ranges.where(
- :name => [INTERNAL_EXTENSIONS, DIRECT_INWARD_DIALING_NUMBERS]
+ :name => [GsParameter.get('INTERNAL_EXTENSIONS'), GsParameter.get('DIRECT_INWARD_DIALING_NUMBERS')]
)
if !phone_number_ranges.empty?
if !PhoneNumber.where(:phone_numberable_type => 'PhoneNumberRange').
diff --git a/app/models/phone_number_range.rb b/app/models/phone_number_range.rb
index 2fdd9b6..b666487 100644
--- a/app/models/phone_number_range.rb
+++ b/app/models/phone_number_range.rb
@@ -6,7 +6,7 @@ class PhoneNumberRange < ActiveRecord::Base
validates_presence_of :name
validates_uniqueness_of :name, :scope => [:phone_number_rangeable_id, :phone_number_rangeable_type]
- validates_inclusion_of :name, :in => [INTERNAL_EXTENSIONS, DIRECT_INWARD_DIALING_NUMBERS, SERVICE_NUMBERS]
+ validates_inclusion_of :name, :in => [GsParameter.get('INTERNAL_EXTENSIONS'), GsParameter.get('DIRECT_INWARD_DIALING_NUMBERS'), GsParameter.get('SERVICE_NUMBERS')]
validates_presence_of :phone_number_rangeable_id
validates_presence_of :phone_number_rangeable
diff --git a/app/models/route_element.rb b/app/models/route_element.rb
new file mode 100644
index 0000000..94f0f84
--- /dev/null
+++ b/app/models/route_element.rb
@@ -0,0 +1,26 @@
+class RouteElement < ActiveRecord::Base
+ ELEMENT_ACTIONS = ['none', 'match', 'not_match', 'set']
+
+ attr_accessible :call_route_id, :var_in, :var_out, :pattern, :replacement, :action, :mandatory, :position
+
+ belongs_to :call_route
+
+ acts_as_list :scope => :call_route
+
+ validates :action,
+ :presence => true,
+ :inclusion => { :in => ELEMENT_ACTIONS }
+
+
+ def to_s
+ "#{pattern} => #{var_in} #{var_out}"
+ end
+
+ def move_up?
+ #return self.position.to_i > RouteElement.where(:call_route_id => self.call_route_id ).order(:position).first.position.to_i
+ end
+
+ def move_down?
+ #return self.position.to_i < RouteElement.where(:call_route_id => self.call_route_id ).order(:position).last.position.to_i
+ end
+end
diff --git a/app/models/sip_account.rb b/app/models/sip_account.rb
index 8459265..d35f9b4 100644
--- a/app/models/sip_account.rb
+++ b/app/models/sip_account.rb
@@ -10,7 +10,7 @@ class SipAccount < ActiveRecord::Base
# Associations:
#
- belongs_to :sip_accountable, :polymorphic => true
+ belongs_to :sip_accountable, :polymorphic => true, :touch => true
has_many :phone_sip_accounts, :uniq => true
has_many :phones, :through => :phone_sip_accounts
@@ -71,7 +71,7 @@ class SipAccount < ActiveRecord::Base
after_update :log_out_phone_if_not_local
def to_s
- truncate((self.caller_name || "SipAccount ID #{self.id}"), :length => TO_S_MAX_CALLER_NAME_LENGTH) + " (#{truncate(self.auth_name, :length => TO_S_MAX_LENGTH_OF_AUTH_NAME)}@...#{self.host.split(/\./)[2,3].to_a.join('.') if self.host })"
+ truncate((self.caller_name || "SipAccount ID #{self.id}"), :length => GsParameter.get('TO_S_MAX_CALLER_NAME_LENGTH')) + " (#{truncate(self.auth_name, :length => GsParameter.get('TO_S_MAX_LENGTH_OF_AUTH_NAME'))}@...#{self.host.split(/\./)[2,3].to_a.join('.') if self.host })"
end
def call_forwarding_toggle( call_forwarding_service, to_voicemail = nil )
@@ -200,7 +200,7 @@ class SipAccount < ActiveRecord::Base
# log out phone if sip_account is not on this node
def log_out_phone_if_not_local
- if self.gs_node_id && ! GsNode.where(:ip_address => HOMEBASE_IP_ADDRESS, :id => self.gs_node_id).first
+ if self.gs_node_id && ! GsNode.where(:ip_address => GsParameter.get('HOMEBASE_IP_ADDRESS'), :id => self.gs_node_id).first
self.phones.each do |phone|
phone.user_logout;
end
diff --git a/app/models/tenant.rb b/app/models/tenant.rb
index d9351b7..419ac3a 100644
--- a/app/models/tenant.rb
+++ b/app/models/tenant.rb
@@ -1,15 +1,8 @@
# encoding: UTF-8
class Tenant < ActiveRecord::Base
- attr_accessible :name, :description, :sip_domain_id, :country_id, :language_id, :from_field_pin_change_email, :from_field_voicemail_email
-
- if STRICT_INTERNAL_EXTENSION_HANDLING == true
- attr_accessible :internal_extension_ranges
- end
-
- if STRICT_DID_HANDLING == true
- attr_accessible :did_list
- end
+ # https://github.com/rails/strong_parameters
+ include ActiveModel::ForbiddenAttributesProtection
# Associations:
#
@@ -93,7 +86,7 @@ class Tenant < ActiveRecord::Base
name
end
- if STRICT_INTERNAL_EXTENSION_HANDLING == true
+ if GsParameter.get('STRICT_INTERNAL_EXTENSION_HANDLING') == true
def array_of_internal_extension_numbers
ranges = self.internal_extension_ranges.gsub(/[^0-9\-,]/,'').gsub(/[\-]+/,'-').gsub(/[,]+/,',').split(/,/)
output = []
@@ -112,7 +105,7 @@ class Tenant < ActiveRecord::Base
# Generate the internal_extensions
#
def generate_internal_extensions
- internal_extensions = self.phone_number_ranges.find_or_create_by_name(INTERNAL_EXTENSIONS, :description => 'A list of all available internal extensions.')
+ internal_extensions = self.phone_number_ranges.find_or_create_by_name(GsParameter.get('INTERNAL_EXTENSIONS'), :description => 'A list of all available internal extensions.')
phone_number_list = Array.new
@@ -122,7 +115,7 @@ class Tenant < ActiveRecord::Base
elsif
# Don't create extensions like 911, 110 or 112 (at least by default)
#
- phone_number_list = (self.array_of_internal_extension_numbers - self.country.phone_number_ranges.where(:name => SERVICE_NUMBERS).first.phone_numbers.map{|entry| entry.number})
+ phone_number_list = (self.array_of_internal_extension_numbers - self.country.phone_number_ranges.where(:name => GsParameter.get('SERVICE_NUMBERS')).first.phone_numbers.map{|entry| entry.number})
end
end
@@ -133,7 +126,7 @@ class Tenant < ActiveRecord::Base
end
- if STRICT_DID_HANDLING == true
+ if GsParameter.get('STRICT_DID_HANDLING') == true
def array_of_dids_generated_from_did_list
numbers = self.did_list.downcase.gsub(/[^0-9,x\+]/,'').gsub(/[,]+/,',').split(/,/)
array_of_all_external_numbers = []
@@ -152,7 +145,7 @@ class Tenant < ActiveRecord::Base
# Generate the external numbers (DIDs)
#
def generate_dids
- dids = self.phone_number_ranges.find_or_create_by_name(DIRECT_INWARD_DIALING_NUMBERS, :description => 'A list of all available DIDs.')
+ dids = self.phone_number_ranges.find_or_create_by_name(GsParameter.get('DIRECT_INWARD_DIALING_NUMBERS'), :description => 'A list of all available DIDs.')
self.array_of_dids_generated_from_did_list.each do |number|
dids.phone_numbers.find_or_create_by_name_and_number('DID', number)
end
@@ -167,7 +160,7 @@ class Tenant < ActiveRecord::Base
@internal_extensions_and_dids ||= self.phone_number_ranges_phone_numbers.
where(:phone_numberable_type => 'PhoneNumberRange').
where(:phone_numberable_id => self.phone_number_ranges.
- where(:name => [INTERNAL_EXTENSIONS, DIRECT_INWARD_DIALING_NUMBERS]).
+ where(:name => [GsParameter.get('INTERNAL_EXTENSIONS'), GsParameter.get('DIRECT_INWARD_DIALING_NUMBERS')]).
map{|pnr| pnr.id })
end
@@ -175,7 +168,7 @@ class Tenant < ActiveRecord::Base
@array_of_internal_extensions ||= self.phone_number_ranges_phone_numbers.
where(:phone_numberable_type => 'PhoneNumberRange').
where(:phone_numberable_id => self.phone_number_ranges.
- where(:name => INTERNAL_EXTENSIONS).
+ where(:name => GsParameter.get('INTERNAL_EXTENSIONS')).
map{|pnr| pnr.id }).
map{|phone_number| phone_number.number }.
sort.uniq
@@ -184,7 +177,7 @@ class Tenant < ActiveRecord::Base
def array_of_dids
@array_of_dids ||= self.phone_number_ranges_phone_numbers.
where(:phone_numberable_type => 'PhoneNumberRange').
- where(:phone_numberable_id => self.phone_number_ranges.where(:name => DIRECT_INWARD_DIALING_NUMBERS).map{|pnr| pnr.id }).
+ where(:phone_numberable_id => self.phone_number_ranges.where(:name => GsParameter.get('DIRECT_INWARD_DIALING_NUMBERS')).map{|pnr| pnr.id }).
map{|phone_number| phone_number.to_s }.
sort.uniq
end
@@ -215,7 +208,7 @@ class Tenant < ActiveRecord::Base
# Create a public phone book for this tenant
def create_a_default_phone_book
- if self.name != SUPER_TENANT_NAME
+ if self.name != GsParameter.get('SUPER_TENANT_NAME')
general_phone_book = self.phone_books.find_or_create_by_name_and_description(
I18n.t('phone_books.general_phone_book.name'),
I18n.t('phone_books.general_phone_book.description', :resource => self.to_s)
diff --git a/app/models/user.rb b/app/models/user.rb
index 2d0256f..b902b99 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -27,7 +27,8 @@ class User < ActiveRecord::Base
}
validates_length_of [:new_pin, :new_pin_confirmation],
- :minimum => MINIMUM_PIN_LENGTH, :maximum => MAXIMUM_PIN_LENGTH,
+ :minimum => (GsParameter.get('MINIMUM_PIN_LENGTH').nil? ? 4 : GsParameter.get('MINIMUM_PIN_LENGTH')),
+ :maximum => (GsParameter.get('MAXIMUM_PIN_LENGTH').nil? ? 10 : GsParameter.get('MAXIMUM_PIN_LENGTH')),
:allow_blank => true, :allow_nil => true
validates_format_of [:new_pin, :new_pin_confirmation],
:with => /^[0-9]+$/,
@@ -202,6 +203,7 @@ class User < ActiveRecord::Base
else
phone.destroy
end
+ phone.resync
end
end